使用实时补丁内核的ubuntu中安装nvidia显卡驱动

根据上上篇的博客,安装了打了PREEMPT_RT实时补丁的内核。在我的应用场景中,是想在一台机器上同时能够使用两种内核,并且在nvidia-docker中,跑一个需要用到显卡的程序。

结果发现在实时内核中,非实时内核中装好的显卡驱动不能用,直接使用apt install nvidia-410又说已经update了。如果系统是刚装的,非实时内核中还没有装过显卡驱动,直接在实时内核中apt install nvidia-410会看到报错说,不支持rt内核。

经过一番折腾,终于摸索到了在两个内核中都用上显卡驱动的方法。如果你只需要在实时内核上安装显卡驱动,或许也可以使用类似的思路,但我没有验证过。

 

我的环境:

Ubuntu16.04

自带内核4.15.0-45-generic

实时内核4.16.18-rt12

显卡:NVIDIA GEFORCE GTX 1080Ti

 

1、在非实时内核中安装显卡驱动

sudo执行以下命令:

rm -f /etc/apt/sources.list.d/graphics-drivers-ubuntu-ppa-*
add-apt-repository -y ppa:graphics-drivers
sed -i 's/http:\/\/ppa.launchpad.net/https:\/\/launchpad.proxy.ustclug.org/g' /etc/apt/sources.list.d/graphics-drivers-ubuntu-ppa-*.list
apt update
apt install -y nvidia-410;     

在高于Ubuntu16.04的环境下,可能还要装些别的东西,我没有试过,如果这样装完有问题可以再查查高版本ubuntu中的安装指导。上面装完后,重启,nvidia-smi应该可以正确显示显卡的信息。

此时,我也可以成功启动nvidia-docker,并在其中正确运行nvidia-smi和使用显卡的程序。

 

2、在实时内核中安装显卡驱动

这一步是在第1步完成的前提下进行的。如果没有apt install nvidia-410,仅仅执行下面的脚本,是无法安装成功的。

这个脚本来自于apollo的开源,我是在https://github.com/ApolloAuto/apollo/issues/1872#issuecomment-352624933这个回答里看到这个脚本的。一开始,我直接注释掉了启动对于uname -r的检查,因为我知道我的确是在一个实时内核上安装,装完之后确实能用nvidia-smi了,但是切换回普通内核,显卡驱动却不能使用了。

我们先来看下这个脚本,我把每个函数的函数体在这里先省略掉,可以直接去上述链接中下载到完整的脚本文件。

#!/bin/bash

# ....

# check environment
check_env

# prepare for nvidia
prepare_nv

# build nvidia.ko
build_nv

# install user lib
install_lib

# clean environment
clean_env

echo "Done to install nvidia kernel driver and user libraries."

从这个文件最后几行,可以看出它大概干了些什么。首先是检查环境,然后是准备工作,然后编译、安装,最后清理环境。

(1)check_env

如果你用的不是apollo提供的实时内核,在check_env的时候,脚本就会报错退出,所以我们可以根据自己的情况注释掉这个检查,或者把目标内核版本改成我们自己的实时内核版本,用#####标出的是修改过的行:

function check_env() {
    # check if in rt kernel     #####
    uname -r | grep rt 1>/dev/null 2>&1   #####
    if [ $? -ne 0 ]
    then
        echo "Not in rt kernel, Please install rt kernel and reboot machine first."  #####
        exit 2
    fi

    # check if nv ko already in kernel
    if [ ! -f /lib/modules/`uname -r`/kernel/drivers/video/nvidia.ko ]
    then
        export NEED_TO_COMPILE_NV_KO=1
    fi
}

(2)prepare_nv

在prepare_nv这步中,其实就是下载了nvidia驱动的.run文件。可能在我们的网络环境下,直接从这个url下载会很慢,也可以在网络环境好的地方先下载好,然后注释掉下载的命令。然后有一点非常重要:这里一定要用和非实时内核中相同版本的驱动,否则会出现各种问题,我之前应该就是因为在不同内核中装了不同版本的驱动,导致后来驱动不可用,无奈之下只能重装系统。

NV_FILE="NVIDIA-Linux-x86_64-410.78.run"
NV_URL="https://us.download.nvidia.cn/XFree86/Linux-x86_64/410.78/${NV_FILE}"

在非实时内核中,nvidia-smi显示驱动版本为410.78,所以这里也改成410.78,这两行在脚本的开头处。

(3)build_nv

function build_nv() {

    if [ ${NEED_TO_COMPILE_NV_KO} == 0 ]
    then
        return
    fi

    NVIDIA_MOD_REL_PATH='kernel/drivers/video'
    NVIDIA_OUTPUT_PATH="/lib/modules/`uname -r`/${NVIDIA_MOD_REL_PATH}"
    CPUNUM=`cat /proc/cpuinfo | grep processor | wc | awk -F " " '{print $1}'`

    export IGNORE_PREEMPT_RT_PRESENCE=true    #####
    cd ${NVIDIA_SOURCE} && make -j ${CPUNUM} module
    cd ${BUILD_BASE}

    unset IGNORE_PREEMPT_RT_PRESENCE  #####

    mkdir -p ${NVIDIA_OUTPUT_PATH}

    [ -f ${NVIDIA_SOURCE}/nvidia.ko ] && cp ${NVIDIA_SOURCE}/nvidia.ko ${NVIDIA_OUTPUT_PATH}
    [ -f ${NVIDIA_SOURCE}/nvidia-modeset.ko ] && cp ${NVIDIA_SOURCE}/nvidia-modeset.ko ${NVIDIA_OUTPUT_PATH}
    [ -f ${NVIDIA_SOURCE}/nvidia-drm.ko ] && cp ${NVIDIA_SOURCE}/nvidia-drm.ko ${NVIDIA_OUTPUT_PATH}
    [ -f ${NVIDIA_SOURCE}/nvidia-uvm.ko ] && cp ${NVIDIA_SOURCE}/nvidia-uvm.ko ${NVIDIA_OUTPUT_PATH}

    depmod -a
}

上面用#####标出的两行需要特别注意,这是编译驱动源码时涉及的一个环境变量,默认如果没有设置过的话,是会检查当前是否是实时内核,如果是的话,会报错说不支持。这样设置一下,就可以编译了。这也是整个脚本的灵魂所在。

build_env中生成的.ko文件,其实是整个安装过程中,对于实时和非实时内核来说,生成的唯一不同的东西。.ko文件是内核的模块文件,depmod -a是在生成.ko文件后,重新生成内核模块的依赖关系的一个命令,加载内核时应该会用到这个文件来查看模块文件的位置和依赖关系。

(4)install_lib

function install_lib() {

    NV_LIB_OUTPUT_PATH="/usr/lib/x86_64-linux-gnu/"
    NV_BIN_OUTPUT_PATH="/usr/bin/"

    [ -f ./${NV_DIR}/libnvidia-ml.so.${NV_VERSION} ] && /bin/cp -f ./${NV_DIR}/libnvidia-ml.so.${NV_VERSION} ${NV_LIB_OUTPUT_PATH}
    [ -f ./${NV_DIR}/libnvidia-fatbinaryloader.so.${NV_VERSION} ] && /bin/cp -f ./${NV_DIR}/libnvidia-fatbinaryloader.so.${NV_VERSION} ${NV_LIB_OUTPUT_PATH}
    [ -f ./${NV_DIR}/libnvidia-ptxjitcompiler.so.${NV_VERSION} ] && /bin/cp -f ./${NV_DIR}/libnvidia-ptxjitcompiler.so.${NV_VERSION} ${NV_LIB_OUTPUT_PATH}
    [ -f ./${NV_DIR}/libcuda.so.${NV_VERSION} ] && /bin/cp -f ./${NV_DIR}/libcuda.so.${NV_VERSION} ${NV_LIB_OUTPUT_PATH}
    [ -f ./${NV_DIR}/nvidia-modprobe ] && /bin/cp -f ./${NV_DIR}/nvidia-modprobe ${NV_BIN_OUTPUT_PATH}
    [ -f ./${NV_DIR}/nvidia-smi ] && /bin/cp -f ./${NV_DIR}/nvidia-smi ${NV_BIN_OUTPUT_PATH}

    chmod +x /usr/bin/nvidia*
    chmod +s /usr/bin/nvidia-modprobe

    # link for nvidia
    /bin/rm -rf /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1  /usr/lib/x86_64-linux-gnu/libnvidia-ml.so
    /bin/ln -s /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.${NV_VERSION} /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1
    /bin/ln -s /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1 /usr/lib/x86_64-linux-gnu/libnvidia-ml.so

    /bin/rm -rf /usr/lib/x86_64-linux-gnu/libcuda.so  /usr/lib/x86_64-linux-gnu/libcuda.so.1
    /bin/ln -s /usr/lib/x86_64-linux-gnu/libcuda.so.${NV_VERSION} /usr/lib/x86_64-linux-gnu/libcuda.so.1
    /bin/ln -s /usr/lib/x86_64-linux-gnu/libcuda.so.1 /usr/lib/x86_64-linux-gnu/libcuda.so

    # take effect
    /sbin/ldconfig 1>/dev/null 2>&1
}

这一步是将.run文件解压后,目录中的一些.so、可执行文件拷贝到一些系统路径下。在执行脚本后,可以看到这些文件的时间戳并未改变,所以不管内核有没有实时补丁,这些东西是不受影响的。

 

 

根据上述,对这个脚本需要做两处改动,一是check_env中的内核版本检查,而是显卡驱动的版本。sudo执行该脚本后,在实时内核中nvidia-smi就可以使用了。如果非实时内核中没有安装过apt install nvidia-410,只跑了这个脚本,nvidia-smi还是不能用的。

 

3、实时内核的nvidia-docker中使用显卡驱动

启动nvidia-docker的时候报错:

nvidia-container-cli: initialization error: cuda error: unknown error

直接执行nvidia-container-cli -k -d /dev/tty info也可以看到这个报错。

 

查了一圈,看到有人提到说是因为nvidia-uvm这个内核模块没有被加载成功。用 lsmod | grep -i nvidia查看了一下,果然没有这个模块。nvidia-uvm.ko正是上面这个脚本build_nv中生成的一个文件。在脚本里可以看到这个文件最终被拷贝到了/lib/modules/4.16.18-rt12/kernel/drivers/video/下

尝试加载这个模块:sudo modprobe nvidia-uvm,报错说找不到nvidia_410_uvm这个模块。

之前在折腾的时候,偶尔发现过,在非实时内核中,/lib/modules/4.15.0-45-generic/updates/这个路径下,有四个.ko文件,和上面生成的四个.ko文件名字上一一对应,但是是类似nvidia_410_uvm这样包含了版本号的名字。就试着把nvidia_uvm.ko拷贝过去:

sudo cp /lib/modules/4.16.18-rt12/kernel/drivers/video/nvidia-uvm.ko /lib/modules/4.16.18-rt12/updates/nvidia_410_uvm.ko
sudo depmod -a
sudo modprobe nvidia-uvm   #不再报错,没有输出
nvidia-container-cli -k -d /dev/tty info  #不再报错,输出一堆东西

然后再打开nvidia-docker,就不再报错了,可以运行nvidia-smi以及使用显卡的程序。

不过最后一步感觉路子有点野,虽然暂时看起来可以用了,不知道会不会有什么坑,或许把4个.ko都拷过去会更好一些。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章