面向高性能保密计算的定制 Linux 系统构建与自动部署方案

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_SMPCONFIG_NR_CPUS 适量)
  • 进程/线程管理、futex、epoll、signals
  • 内存管理、交换(若需要)
  • ELF 可执行文件格式
  • 文件系统:ext4/proc/sysdevtmpfstmpfs
  • 块设备:SATA(CONFIG_ATA)、NVMe(CONFIG_BLK_DEV_NVME)、virtio-blk(测试用)
  • PCI/PCIe 总线及 MSI(GPU 必需)
  • TTY、/dev/console、VGA 文本控制台(CONFIG_VGA_CONSOLECONFIG_FRAMEBUFFER_CONSOLE)用于显示
  • 内核模块支持(CONFIG_MODULES),以便加载 GPU 驱动模块
  • 标准 C 库所需系统调用(如 clock_gettimesched_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 supportPCI ExpressMessage Signaled Interrupts (MSI and MSI-X) 选中

Device Drivers

  • 完全关闭 Input device supportSound card supportUSB supportNetwork device support
  • 保留 Graphics support 中的 VGA text consoleFramebuffer 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/:放入编译好的可执行文件(pimatmul
  • 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 实现全自动流程
相关推荐
着迷不白1 小时前
五、文本处理工具+正则表达式
linux·运维·服务器
载数而行5201 小时前
Linux 4常用指令(文件/时间/搜索查找/压缩解压指令)
linux
不做无法实现的梦~2 小时前
MAVLink 协议教程
linux·stm32·嵌入式硬件·算法
实心儿儿2 小时前
Linux —— 线程控制(2)
linux·运维·服务器
烛衔溟2 小时前
TypeScript 模块与声明文件全解
linux·ubuntu·typescript
代码飞一会儿2 小时前
CTF之跟我一起逆向得到flag吧
安全
量子炒饭大师2 小时前
【Linux系统编程:进程概念】——【从 冯诺依曼系统体系结构 到 操作系统】
linux·运维·服务器·操作系统·冯诺依曼
2023自学中3 小时前
imx6ull 开发板,手机,MQTT 物联网通信实验。
linux·服务器·物联网·嵌入式·开发板·应用编程
f8979070703 小时前
把文件进行锁死,不要有写的权限。不被恶意攻击
linux