Ubuntu双系统与NVIDIA的奇妙冒险
背景
选修的并行计算课程需要GPU编程,想着熟悉CUDA或许是以后找工作的亮点,在CUDA和GPGPU中还是偏向CUDA一点,并且我记得之前在弄大创的时候就装过CUDA,想来也会简单一些。谁曾想这个选择会折磨我一个通宵…
虚拟机安装CUDA的尝试
一开始,我是希望在虚拟机上实现CUDA安装的,因为在课堂上老师说必须在Linux上或者虚拟机上实现。在断断续续的几天里(摸鱼),我甚至连虚拟机连通GPU都无法做到,上网搜了一下才发现是虚拟机无法接触很底层的东西,所以VMware是无法实现的,得使用VMware ESXI.
VMware ESXI
于是我就去找VMware ESXI,先是在官网乱找一通,这个VMware ESXI在官网上的名字又不一样。等找到以后才发现,这是给人家服务器用的,相当于一个操作系统。虚拟机我感觉本来就是图方便,如果只是为了某几个应用的环境完全可以使用docker,所以就想,既然如此,还不如直接装个ubuntu双系统。
忆往昔
ubuntu我装过没有呢?我是装过的,当时用的游戏本装的(后面才知道是没装对驱动),于是一怒之下又换回windows。
Ubuntu
这一次装Ubuntu成了装系统路上无法抹去的噩梦👿
开门红
“诶~装个系统,多大点事儿”,第一次非常之顺利,唯一让我略感不安的就是在启动盘安装的过程中,由于屏幕分辨率的问题,无法显示所有的选项,
dotnetcli sudo reboot
后,我傻眼了,系统进程每次都会卡在PPM init failed
上,我的好copilot GPT告诉我,可能是磁盘问题或者驱动问题。既然我CUDA都装好了,
想起在设置分区时分区格式的logical和primary设置还有Use as,我才想起来我设置出现了问题。
/目录和/home目录所在分区应该为Primary格式,Primary格式和Logical格式的区别在于能否被引导程序所发现和能否无限分区,其中GPT格式的启动可以实现Primary格式的无限分区,MBR格式则不行,而GPT格式与MBR格式与是BIOS启动还是UEFI启动关联
再次痛击
总之在问题递归后,我总算排除了磁盘问题,心想着这次一定不会出问题了。小心翼翼的设置了一番,sudo reboot
,然而,问题并没有如我所希望那样彻底解决,而是又复现了。在反复的重启后,我将重点放在了PPM init failed前面的错误信息上:
1 | ucpi_acip: fail to get stauts |
在Kurisu(我的GPT engine)建议下,我在GRUB中直接禁掉了acip,结果好家伙,直接连GRUB都无法进入,停留在initramfs内核上。想要重启acip就要进入命令行,但是initramfs让我连GRUB都无法进入,无奈只能重装。
确定问题
重装后问题还是在复现🤮
上网后得知
这个模块是控制type-c相关的
内心os: mad,怎么还扯到type-c了
但我还是确定,应该出在驱动问题上。
因为在Kurisu指导下,
1 | lshw -C display |
显示的两个display都是UNCLAIMED,也就是说系统无法识别这两张显卡,一张NVIDIA和INTEL的核显
驱动签名与Secure boot
在之前失败的经历中,我注意到在NVIDIA的驱动安装过程中,有Sign the driver
和Install without sign
的选项
在Bios中设置Secure boot后,系统只会加载那些受信的驱动。
麻雀虽小,五脏俱全。虽然我应该不需要考虑什么安全问题,但我还是没有禁用Secure boot
,于是在弄明白Linux下的公钥私钥后,成功地对驱动进行了签名,并且在命令行nvidia-smi
后也是能正确加载显卡信息的。但是lshw -C display
却无法识别INTEL的核显,在官网上也找不到对应的Linux环境下的驱动,在nvidia-smi中,我发现显卡是off不工作的,我猜想是
峰回路转,难道是内核?
由于重装过一次,因此在我GRUB界面选择进入Advanced Ubuntu时,有两个长得很像的选项,一个是6.5.0.18...
一个是6.5.0.28...
,其中一个是第一次安装时的残留
我在6.5.0.18的内核装了NVIDIA驱动后,在6.5.0.28的内核上nvidia-smi
失败,因此我想
库吃库吃把.28内核删掉后,我满怀希望,望向屏幕,像怀春的少女,怕他不来,又怕他乱来。等待的圈圈不停转动,最终停住。一如当初满怀希望的我,却最终心如死灰。
不死心的我与奇迹
难道,一切都结束了吗?
我还是那个不死心的我,即便窗外已从黑夜到初晨,我还是不甘心就这样放弃。
在各种文章、博客的解决方法中,总是会有各种各样的命令,但却都不出所料的无法解决问题
想想,我总是心高气傲:就算是测试程序,也要努力让它健壮,即便会涉及更高级的语法;开发环境,开发软件总是一次到位,希望能让自己更加“专业”,这样的我对于’自动’这种东西总是持怀疑态度,我怀疑它们是否能正确识别到我的问题所在。一条不起眼的命令自然就被我忽略了
ubuntu-drivers autoinstall
彼时的我认为
我都无法正确安装驱动,你又如何实现自动安装,况且连INTEL官网都没有针对的驱动识别核显,你怎么能靠内置的i915实现对核显的驱动
但就在我抱着试一试的态度后
奇迹,发生了
——重启毫无问题,lshw -C display
正确识别显卡,包括核显,双显示器正常识别,nvidia-smi
正常识别,一切都来得那么轻悄悄。
卷土重来?
在美滋滋的装完CUDA后,重启后,黑屏如梦魇般缠上了我,这次是新问题
NVME Driver/Library dismatch
冷静分析,问题应该出现在CUDA Toolkit上,但为什么会不匹配呢,我是安装nvidia-smi
给出的版本安装的,不应该会出错。dpkg | grep nvidia-*
后发现确实有不匹配的依赖,回想有关CUDA Toolkit的操作,那么真相只有一个
sudo apt-get install nvidia-cuda-toolkit
这是由于安装了CUDA Toolkit却nvcc --version
失败后的系统建议,在卸载后果然恢复了正常。而在往bashrc中添加了变量后nvcc --version
也能正确显示了。
SDK测试代码
#include <iostream>
#include <boost/program_options.hpp>
#include <string>
#include <ctype.h>
#include <vector>
#include <type_traits>
#include <cmath>
namespace po = boost::program_options;
class TEST {
public:
TEST(std::vector<std::string> _nums, size_t _size);
~TEST() {
cudaFree(d_A);
cudaFree(d_B);
cudaFree(d_C);
delete[] h_A;
delete[] h_B;
delete[] h_C;
};
template <typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type computeProcess(T a);
template <typename T1, typename T2>
typename std::enable_if<std::is_integral<T1>::value && std::is_integral<T2>::value>::type computeProcess(T1 a, T2 b);
void cpuVectorAdd(const float* A, const float* B, float* C);
void cpuMatrixMul(const float* A, const float* B, float* C, const int row, const int col);
bool correctCheck(const float* A, const float* B);
private:
float *h_A, *h_B, *h_C, *d_A, *d_B, *d_C, *_d_C;
size_t size;
int sum;
std::vector<std::string> nums;
};
__global__ void vectorAdd(const float* A, const float* B, float* C, int e_sum) {
int i = blockDim.x * blockIdx.x + threadIdx.x;
if (i < e_sum)
C[i] = A[i] + B[i];
}
__global__ void matrixMul(const float* A, const float* B, float* C, int row, int col) {
int _col = blockDim.x * blockIdx.x + threadIdx.x;
int _row = blockDim.y * blockIdx.y + threadIdx.y;
if (_col < col && _row < row) {
float value = 0.0f;
for (int k = 0;k < _col;k++) {
value += A[_row * col + k] * B[k * row + _col];
}
C[_row * col + _col] = value;
}
}
TEST::TEST(std::vector<std::string> _nums, size_t _size) : nums(_nums), size(_size), d_A(NULL), d_B(NULL), d_C(NULL) {
h_A = new float[size];
h_B = new float[size];
h_C = new float[size];
cudaMalloc((void**)&d_A, size);
cudaMalloc((void**)&d_B, size);
d_C = new float[size];
cudaMalloc((void**)&_d_C, size);
for (int i = 0;i < size / sizeof(float);i++) {
h_A[i] = rand() / (float)RAND_MAX;
h_B[i] = rand() / (float)RAND_MAX;
}
cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);
if (nums.size() == 1) {
int v_length = std::stoi(nums[0]);
computeProcess(v_length);
} else if (nums.size() == 2) {
int m_line = std::stoi(nums[0]);
int m_column = std::stoi(nums[1]);
computeProcess(m_line, m_column);
}
}
void TEST::cpuVectorAdd(const float* A, const float* B, float* C) {
for (int i = 0;i < size / sizeof(float);i++) {
C[i] = A[i] + B[i];
}
}
void TEST::cpuMatrixMul(const float* A, const float* B, float* C, const int row, const int col) {
for (int _i = 0;_i < row;_i++) {
for (int _j = 0;_j < row;_j++) {
float value = 0.0f;
for (int k = 0; k < col; k++) {
value += A[_i * col + k] * B[k * row + _j];
}
C[_i * row + _j] = value;
}
}
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type TEST::computeProcess(T a) {
cpuVectorAdd(h_A, h_B, h_C);
int threads_per_block = 256;
int blocks_per_grim = (a + threads_per_block - 1) / threads_per_block;
vectorAdd<<<blocks_per_grim, threads_per_block>>>(d_A, d_B, _d_C, a);
cudaMemcpy(d_C, _d_C, size, cudaMemcpyDeviceToHost);
if (correctCheck(h_C, d_C))
std::cout << "Test passed.\n";
else
std::cout << "Test failed.\n";
};
template <typename T1, typename T2>
typename std::enable_if<std::is_integral<T1>::value && std::is_integral<T2>::value>::type TEST::computeProcess(T1 a, T2 b) {
cpuMatrixMul(h_A, h_B, h_C, a, b);
dim3 threadsPerBlock(16, 16);
dim3 blocksPerGrid((a + threadsPerBlock.x - 1) / threadsPerBlock.x,
(b + threadsPerBlock.y - 1) / threadsPerBlock.y);
matrixMul<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, _d_C, a, b);
cudaMemcpy(d_C, _d_C, size, cudaMemcpyDeviceToHost);
if (correctCheck(h_C, d_C))
std::cout << "Test passed.\n";
else
std::cout << "Test failed.\n";
};
bool TEST::correctCheck(const float* A, const float* B) {
for (int i = 0;i < size / sizeof(float);i++) {
if(fabs(A[i] - B[i]) > 1e-5)
return false;
}
return true;
}
int main(int argc, char *argv[]) {
try {
size_t size;
po::options_description desc("Options allowed");
desc.add_options()
("help, h", "help message")
("nums, n", po::value<std::vector<std::string>>()->multitoken(), "set scale of numbers");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << "\n";
return 0;
} else if (vm.count("nums")) {
std::vector<std::string> nums = vm["nums"].as<std::vector<std::string>>();
if (nums.size() == 1) {
size = std::stoi(nums[0]) * sizeof(float);
} else if (nums.size() == 2) {
size = std::stoi(nums[0]) * std::stoi(nums[1]) * sizeof(float);
}
TEST test(nums, size);
}
} catch(std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return 1;
} catch(...) {
std::cerr << "Unknown error!\n";
return 1;
}
}
当--nums
获取到的参数为一个数时,使用向量加法验证;两个数时使用矩阵乘法验证。
运行截图
一些过程截图
安装过程