1. 目标与设计思路
场景需求:
在一台"部署计算机"上运行自行开发的 C++ 多线程计算程序(可使用 CPU 或 GPU),要求:
- 操作系统极度精简,去除网络、键盘、鼠标、声卡、多媒体等与计算和存储无关的内核功能;
- 只保留 CPU/GPU 计算、线程调度、文件系统、终端显示及必要硬件驱动;
- 开机即自动启动程序,自动探测全部 CPU/GPU 及内存资源并最大化利用;
- 计算结束或程序崩溃后自动关机;
- 系统部署时写入内核、程序及数据源文件,并预留足够磁盘空间存放输出与日志。
核心设计:
- 裁剪内核:只编译必需的内核子系统,彻底移除网络栈、输入设备、声音等。
- 最小根文件系统 :采用
initramfs内含静态编译的 BusyBox,仅负责挂载真正的数据分区并启动计算程序。 - 数据分区:存放计算程序、依赖库、输入数据,并提供可写空间给输出和日志。
- 自动流程 :由
init脚本自动探测资源、启动程序,并在退出后调用poweroff。
整个系统无任何网络能力,无交互设备,最大程度降低攻击面,满足保密与性能需求。
2. 准备工作与软件清单
在普通开发机(完整 Linux,如 Ubuntu 22.04)上准备好:
| 资源 | 用途 |
|---|---|
Linux 内核源码(如 linux-5.15.100) |
裁剪编译 |
BusyBox 源码(如 busybox-1.36.0) |
生成最小用户空间工具集 |
| GCC、make、flex、bison、libelf-dev、openssl-dev 等 | 编译内核与 BusyBox |
| GRUB2、dosfstools、mtools | 制作引导与磁盘镜像 |
(可选)NVIDIA 驱动 .run 包 + CUDA Toolkit |
GPU 计算支持 |
| QEMU / 实机 | 测试裁剪系统 |
3. 裁剪 Linux 内核
3.1 内核配置策略
必须保留:
- 64-bit kernel、SMP、多核调度(
CONFIG_SMP、CONFIG_NR_CPUS适量) - 进程/线程管理、futex、epoll、signals
- 内存管理、交换(若需要)
- ELF 可执行文件格式
- 文件系统:
ext4、/proc、/sys、devtmpfs、tmpfs - 块设备:SATA(
CONFIG_ATA)、NVMe(CONFIG_BLK_DEV_NVME)、virtio-blk(测试用) - PCI/PCIe 总线及 MSI(GPU 必需)
- TTY、
/dev/console、VGA 文本控制台(CONFIG_VGA_CONSOLE、CONFIG_FRAMEBUFFER_CONSOLE)用于显示 - 内核模块支持(
CONFIG_MODULES),以便加载 GPU 驱动模块 - 标准 C 库所需系统调用(如
clock_gettime、sched_setaffinity等)
彻底移除:
- 网络协议栈:
CONFIG_NET=n,连带所有网络设备驱动、socket、TCP/IP 等 - 输入子系统:
CONFIG_INPUT=n,去掉键盘、鼠标、触摸屏等 - 声音:
CONFIG_SOUND=n - USB 支持:
CONFIG_USB_SUPPORT=n - 多媒体设备与摄像头:
CONFIG_MEDIA_SUPPORT=n - 无线、蓝牙、红外
- 非必需文件系统(如 NFS、FAT/VFAT 除非 EFI 分区需要;若用 UEFI 引导,ESP 分区可用 FAT 但内核本身可裁剪掉 VFAT 支持,因为 initramfs 不依赖它挂载 ESP;GRUB 已读取内核,之后由 initramfs 挂载 ext4 数据分区即可)
- 调试与追踪(
CONFIG_DEBUG_KERNEL等,可以去掉以缩减体积)
3.2 实际操作步骤
bash
tar xf linux-5.15.100.tar.xz
cd linux-5.15.100
make defconfig # 先产生默认配置,再裁剪
make menuconfig
在 menuconfig 界面中按以下路径修改(关键项):
General setup
- 关闭
Networking support - 关闭
POSIX Message Queues如不需要可留,但建议保留
Processor type and features
- 选中
Symmetric multi-processing support - 设置
Maximum number of CPUs为大于部署机核心数 - 选择
Preemption Model (Preemptible Kernel (Low-Latency Desktop))利于计算响应
Bus options (PCI etc.)
- 确保
PCI support、PCI Express、Message Signaled Interrupts (MSI and MSI-X)选中
Device Drivers
- 完全关闭
Input device support、Sound card support、USB support、Network device support - 保留
Graphics support中的VGA text console和Framebuffer Console,但可以关闭 DRM/KMS 若只需要文本界面 - 保留
Serial ATA and Parallel ATA drivers下的 AHCI/ATA 驱动 - 保留
NVM Express block device - 保留
Virtio drivers(若用 VM 测试)
File systems
- 选中
The Extended 4 (ext4) filesystem - 保留
Pseudo filesystems(/proc、/sys) - 可关闭其他所有文件系统
Kernel hacking
- 关闭全部以减少体积
保存为 .config,随后编译:
bash
make -j$(nproc) bzImage modules
生成的 arch/x86/boot/bzImage 即为裁剪内核。如有模块(例如后续第三方驱动),会放在各子目录,我们稍后处理。
4. 构建 initramfs 与最小用户空间
4.1 使用 BusyBox 生成静态工具集
bash
tar xf busybox-1.36.0.tar.bz2
cd busybox-1.36.0
make defconfig
make menuconfig
# 在 Settings ->Build Options 中勾选 "Build static binary (no shared libs)"
# 可进一步精简掉不需要的 applet(如网络工具全部关闭),保留 sh, mount, umount, poweroff, reboot, cat, echo, sleep, test 等。
make -j$(nproc)&&make install
安装后的目录默认在 _install/,包含 bin/, sbin/ 和 linuxrc 等。
4.2 编写 init 脚本
创建目录结构作为 initramfs 根:
bash
mkdir -p initramfs/{bin,sbin,dev,proc,sys,mnt,root,etc}
cp -a busybox-1.36.0/_install/* initramfs/
在 initramfs/init 中写入:
bash
#!/bin/sh
# 挂载基本文件系统
mount -t proc proc /proc
mount -t sysfs sys /sys
mount -t devtmpfs dev /dev
# 等待设备就绪(可简单延时)
sleep 2
# 探测数据分区,假设根磁盘为 /dev/sda,数据分区为 /dev/sda2
# 可尝试 mount 多种可能设备,这里固定为 /dev/sda2
mount -t ext4 /dev/sda2 /mnt || {
echo "Failed to mount data partition"
poweroff -f
}
# 可选:加载 NVIDIA 内核模块
if [ -d /mnt/lib/modules ]; then
for mod in /mnt/lib/modules/*.ko; do
insmod "$mod"
done
fi
# 输出系统资源信息
echo "CPU cores: $(nproc)">/mnt/logs/hardware.txt
echo "Memory: $(awk '/MemTotal/ {print $2}' /proc/meminfo) kB">>/mnt/logs/hardware.txt
# nvidia-smi 可从 CUDA 工具包获取,若存放在 /mnt/opt/cuda/bin,可执行
/mnt/opt/cuda/bin/nvidia-smi -L>>/mnt/logs/hardware.txt 2>/dev/null || true
# 执行自动启动脚本,所有输出重定向到日志
if [ -x /mnt/start.sh ]; then
cd /mnt
/mnt/start.sh >> /mnt/logs/compute.log 2>&1
EXITCODE=$?
echo "Process exited with code $EXITCODE at $(date)" >> /mnt/logs/compute.log
else
echo "No start script found" > /mnt/logs/compute.log
fi
# 卸载分区(可选)
umount /mnt
sync
# 关机
poweroff -f
赋予执行权限:chmod +x initramfs/init。
4.3 打包 initramfs
bash
cd initramfs
find . | cpio -o -H newc | gzip>../initramfs.cpio.gz
5. 准备计算程序及依赖(示例)
下面分别给出纯 CPU 多线程和 GPU 加速的两个示例,说明文件布局。
示例 A:CPU 多线程圆周率计算
pi.cpp(使用 OpenMP,计算 π 的积分近似):
cpp
#include<iostream>
#include<cmath>
#include<omp.h>
int main() {
long num_steps = 1000000000;
double step = 1.0 / num_steps;
double sum = 0.0;
#pragma omp parallel for reduction(+:sum)
for (long i = 0; i < num_steps; ++i) {
double x = (i + 0.5) * step;
sum += 4.0 / (1.0 + x * x);
}
double pi = step * sum;
std::cout << "PI = " << pi << " error = " << fabs(pi - M_PI) << std::endl;
return 0;
}
在开发机上编译为静态二进制(避免目标系统缺少库):
bash
g++ -O3 -fopenmp pi.cpp -o pi -static
示例 B:CUDA 矩阵乘法
matmul.cu:
cpp
#include<iostream>
#include<cuda_runtime.h>
__global__ void matMul(float* A, float* B, float* C, int N) {
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
float sum = 0;
if (row < N && col < N) {
for (int k = 0; k < N; ++k)
sum += A[row*N+k] * B[k*N+col];
C[row*N+col] = sum;
}
}
int main() {
int N = 4096;
// 省略内存申请及初始化...
// 查询所有 GPU 设备
int devCount;
cudaGetDeviceCount(&devCount);
std::cout << "Found " << devCount << " CUDA devices." << std::endl;
// 启动核函数...
return 0;
}
编译:在装有 CUDA 的开发机上用 nvcc -O3 matmul.cu -o matmul,生成动态链接可执行文件,它依赖 libcudart.so 等。
6. 部署磁盘布局与引导
6.1 磁盘分区方案
假设部署计算机使用一块空白硬盘 /dev/sda,采用传统 BIOS/MBR 引导(UEFI 同理,稍作调整):
/dev/sda1:boot 分区,ext4,大小 500 MB,存放内核、initramfs、GRUB/dev/sda2:data 分区,ext4,占用剩余所有空间,用于计算程序和输出
创建分区:
bash
parted /dev/sda mklabel msdos
parted /dev/sda mkpart primary ext4 1MiB 501MiB
parted /dev/sda mkpart primary ext4 501MiB 100%
mkfs.ext4 /dev/sda1
mkfs.ext4 /dev/sda2
6.2 安装引导程序
bash
mount /dev/sda1 /mnt/boot
grub-install --target=i386-pc --boot-directory=/mnt/boot /dev/sda
创建 /mnt/boot/grub/grub.cfg:
set timeout=0
set default=0
menuentry "Compute Node" {
linux /boot/bzImage console=tty1 quiet
initrd /boot/initramfs.cpio.gz
}
复制文件:
bash
cp arch/x86/boot/bzImage /mnt/boot/boot/
cp initramfs.cpio.gz /mnt/boot/boot/
umount /mnt/boot
6.3 数据分区内容
挂载 /dev/sda2 到 /mnt/data,创建目录结构:
bash
mount /dev/sda2 /mnt/data
mkdir -p /mnt/data/{programs,data_input,output,logs,lib/modules,opt/cuda}
programs/:放入编译好的可执行文件(pi或matmul)data_input/:放置输入数据文件output/、logs/:留空,运行后写入
对于 CPU 示例,直接将 pi 复制到 /mnt/data/programs/。
对于 GPU 示例,需将 matmul 及其依赖的 CUDA 库(如 libcudart.so.11.0)复制到 /mnt/data/programs/,并将整个 CUDA Toolkit 安装目录复制到 /mnt/data/opt/cuda;同时安装匹配该裁剪内核的 NVIDIA 驱动模块:
- 在开发机,使用同一裁剪内核的源码目录和
.config,执行NVIDIA-Linux-x86_64-550.xx.run --kernel-source-path=/path/to/linux-5.15.100 --no-kernel-module-source --ui=none -K只编译内核模块,然后将生成的nvidia*.ko放到/mnt/data/lib/modules/。 - 或者直接运行
.run文件指定--kernel-module-install-path到数据分区。
6.4 自动启动脚本 start.sh
在数据分区根目录创建 start.sh,负责探测资源并启动对应的计算程序。
CPU 示例:
bash
#!/bin/sh
cd /mnt/programs
THREADS=$(nproc)
./pi --threads $THREADS
GPU 示例:
bash
#!/bin/sh
export PATH=/mnt/opt/cuda/bin:$PATH
export LD_LIBRARY_PATH=/mnt/opt/cuda/lib64:/mnt/programs
cd /mnt/programs
./matmul
确保脚本可执行。
7. 资源最大化利用机制
- CPU :在程序内部调用
std::thread::hardware_concurrency()或omp_get_max_threads(),自动获取核心数;或通过启动脚本的nproc传出参数。线程数可以略少于物理核心数,或直接设置为全部核心以保证满载。 - 内存 :程序可按需分配,若需最大化可使用
/proc/meminfo中的MemAvailable,由程序内部决定工作集大小,避免 OOM。 - GPU :CUDA 程序通过
cudaGetDeviceCount()获取所有可用的 GPU 设备,并可使用多流或多进程并行。示例脚本通过nvidia-smi列出设备,程序可自动遍历设备并分配计算任务。
一切依赖程序自身适配,无需内核层参与资源划分,保证简洁高效。
8. 计算完成或崩溃后自动关机
init 脚本中,启动程序后通过 $? 捕获退出码,无论正常结束还是因异常崩溃(例如段错误导致返回非零值),都会继续执行后续的 poweroff -f。为防止程序卡死可加入超时机制:
bash
/mnt/start.sh&
PID=$!
sleep 3600&# 1小时超时
WATCHDOG=$!
wait -n $PID $WATCHDOG # 任一先退出
kill -9 $PID $WATCHDOG 2>/dev/null
poweroff -f
这样即使程序陷入死循环,一小时后系统也会强制关机,避免无谓耗电。
9. 测试与部署到物理机
使用 QEMU 快速验证 CPU 示例:
bash
qemu-system-x86_64 -m 2G -smp 4 -enable-kvm \
-drive file=/dev/sda,format=raw,if=virtio # 或使用虚拟盘镜像
启动后看到日志输出,最终系统自动关闭。确认 /mnt/data/logs/compute.log 中有正确结果。
对于 GPU 计算,需要直通物理 GPU 或在实际硬件上测试,基本流程一致。
最后,将整个磁盘镜像用 dd 或克隆工具灌录到多台部署计算机的硬盘即可,实现了批量部署。
10. 软件与文件资源需求总表
| 类别 | 具体项 | 说明 |
|---|---|---|
| 开发工具 | Ubuntu 22.04、gcc-11、make、flex、bison、libelf-dev、openssl、grub2、cpio、gzip | 编译内核及制作 initramfs |
| 内核 | linux-5.15.100.tar.xz | LTS 稳定版 |
| 用户空间 | busybox-1.36.0 | 静态编译 sh、mount、poweroff 等 |
| GPU 支持 | NVIDIA-Linux-x86_64-550.90.07.run、cuda_12.2_linux.run | 提供驱动模块与 CUDA 运行时 |
| C++ 程序 | 自研计算代码(.cpp/.cu)及其依赖库 | 静态或动态编译 |
| 输出存储 | 数据分区 ext4,目录 output/、logs/ | 存放最终结果 |
| 配置文件 | grub.cfg、init 脚本、start.sh | 实现全自动流程 |