根據上上篇的博客,安裝了打了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都拷過去會更好一些。