1. 引言:操作系统构建的范式转变

传统上,构建一个可运行的操作系统(即使是极简版本)是一项极其复杂的工程任务。开发者需要精通引导程序(如GRUB)、内核编译、文件系统制作、init进程编写以及各种硬件驱动集成。这个过程涉及数十个甚至上百个配置文件、脚本和源代码文件,学习曲线陡峭,调试困难。

本文介绍的自动化脚本彻底改变了这一范式。它的核心思想是:“应用即系统”。开发者只需专注于编写核心的C语言应用程序,并将其编译为静态链接的ELF(Executable and Linkable Format)可执行文件。剩下的所有工作——从创建可引导环境到打包成ISO镜像——全部由脚本自动完成。这极大地降低了操作系统开发的门槛,让开发者能够快速原型验证、创建专用嵌入式系统或教学演示。详细视频教程:操作系统构建器详细视频

我是一名初中生,传统开发操作系统对于我来说不现实,所以自己设计了原理和测试代码

2. 脚本核心功能与特性

2.1 核心工作流程

  1. 输入:用户提供一个静态链接的ELF文件(例如 myapp.elf)。
  2. 环境检测与依赖安装:脚本自动检测当前系统(Kali/Debian/Ubuntu),并安装必要的工具链(如 gccmakexorrisogrub-pc-bin)。
  3. 引导环境构建:自动创建GRUB引导配置、生成ISO 9660文件系统结构。
  4. 系统集成:将用户提供的ELF文件作为系统的“内核”或唯一的init进程,集成到文件系统中。
  5. 输出:生成一个完整的、可启动的ISO镜像文件(例如 myos.iso),可直接在虚拟机(如QEMU、VirtualBox)或实体机中引导运行。

2.2 技术栈与兼容性

  • 宿主系统:Kali Linux, Debian, Ubuntu 及其衍生版。依赖标准的APT包管理器。
  • 目标文件静态链接的ELF可执行文件。这意味着所有库函数(如libc)都已编译进二进制文件内部,无需目标系统提供任何动态库。
  • 暂不兼容
    • Windows系统(脚本基于Bash和Linux工具链)。
    • 动态链接的ELF文件(开发中)。动态链接需要目标系统存在对应的共享库,增加了复杂性。
    • 非ELF格式的可执行文件(如PE格式的Windows可执行文件)。
  • 未来规划:下一步将兼容使用PyInstaller等工具打包的Python应用程序生成的动态ELF,通过自动打包依赖库来实现。

3. 与传统操作系统开发流程的对比

下表清晰地展示了本脚本带来的革命性简化:

对比维度 传统操作系统开发 本脚本方案 简化程度
核心工作 编写内核、驱动、引导程序、init系统、用户态程序等。 只需编写一个静态ELF应用程序 从“建造整栋大楼”简化为“设计一个房间”。
文件数量 数十至数百个文件(.c, .h, .S, Makefile, 各种.conf, .cfg)。 1个文件(用户提供的.elf)。 数量级降低。
构建复杂度 需要手动配置工具链、交叉编译、链接脚本、处理硬件抽象层。 一键运行脚本,自动安装依赖并完成所有底层配置。 从专家级手工活变为自动化流水线。
调试难度 极高。问题可能出现在引导、内核、驱动、应用等任何层面。 集中。只需调试你自己的ELF应用程序逻辑。 问题域大幅收窄。
学习成本 需要掌握操作系统理论、汇编、硬件接口等大量知识。 只需掌握C语言和Linux基础编程。 门槛极低,适合快速原型和教学。

核心优势总结:传统开发如同从零开始造汽车,需要发动机、底盘、变速箱等所有部件。而本脚本方案如同提供一个“标准底盘”,开发者只需造一个“定制车厢”(ELF应用)放上去,就能立刻得到一辆能跑的车。

4. 使用教程:从C代码到可运行ISO

4.1 准备静态ELF文件

首先,编写一个简单的C程序并静态编译。这个程序将是你操作系统的“全部”。

/*
 * my_os.c - 你的操作系统本体
 * 编译: gcc -static -Os -s -o my_os my_os.c
 * 然后上传 my_os 给 566.py 的 ELF 路径
 */

#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/wait.h>

/* ========== 基础工具 ========== */

static void write_all(int fd, const char *buf, size_t len) {
    while (len > 0) {
        ssize_t n = write(fd, buf, len);
        if (n <= 0) { if (errno == EINTR) continue; return; }
        buf += n; len -= n;
    }
}

static void puts_s(const char *s) {
    write_all(1, s, strlen(s));
}

/* ========== 终端 raw 模式 ========== */

static struct termios g_orig_termios;

static void restore_term(void) {
    tcsetattr(0, TCSANOW, &g_orig_termios);
}

static void set_raw(void) {
    tcgetattr(0, &g_orig_termios);
    struct termios raw = g_orig_termios;
    raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
    raw.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON);
    raw.c_cflag |= CS8;
    raw.c_oflag &= ~OPOST;
    raw.c_cc[VMIN]  = 1;
    raw.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &raw);
    atexit(restore_term);
}

/* ========== 挂载文件系统 ========== */

static void mount_all(void) {
    mkdir("/proc", 0755); mount("proc", "/proc", "proc", 0, NULL);
    mkdir("/sys", 0755);  mount("sysfs", "/sys", "sysfs", 0, NULL);
    mkdir("/dev", 0755);  mount("devtmpfs", "/dev", "devtmpfs", 0, NULL);
    mkdir("/tmp", 0755);  mount("tmpfs", "/tmp", "tmpfs", 0, NULL);
}

/* ========== 命令执行 ========== */

static void handle_cmd(const char *cmd) {
    if (cmd[0] == '\0') return;

    if (strcmp(cmd, "help") == 0) {
        puts_s("Commands:\n"
               "  help      show this\n"
               "  clear     clear screen\n"
               "  mem       show memory info\n"
               "  cpu       show CPU info\n"
               "  uptime    show uptime\n"
               "  echo <t>  print text\n"
               "  fork      fork a child process\n"
               "  panic     trigger kernel panic (test only)\n");
    }
    else if (strcmp(cmd, "clear") == 0) {
        write_all(1, "\x1b[2J\x1b[H", 7);
    }
    else if (strncmp(cmd, "echo ", 5) == 0) {
        puts_s(cmd + 5);
        puts_s("\n");
    }
    else if (strcmp(cmd, "mem") == 0) {
        int fd = open("/proc/meminfo", O_RDONLY);
        if (fd >= 0) {
            char buf[512];
            ssize_t n = read(fd, buf, sizeof(buf) - 1);
            if (n > 0) { buf[n] = '\0'; write_all(1, buf, n); }
            close(fd);
        }
    }
    else if (strcmp(cmd, "cpu") == 0) {
        int fd = open("/proc/cpuinfo", O_RDONLY);
        if (fd >= 0) {
            char buf[512];
            ssize_t n = read(fd, buf, sizeof(buf) - 1);
            if (n > 0) {
                buf[n] = '\0';
                char *p = strstr(buf, "model name");
                if (p) {
                    char *nl = strchr(p, '\n');
                    if (nl) *nl = '\0';
                    puts_s(p); puts_s("\n");
                }
            }
            close(fd);
        }
    }
    else if (strcmp(cmd, "uptime") == 0) {
        int fd = open("/proc/uptime", O_RDONLY);
        if (fd >= 0) {
            char buf[64];
            ssize_t n = read(fd, buf, sizeof(buf) - 1);
            if (n > 0) { buf[n] = '\0'; puts_s("uptime: "); write_all(1, buf, n); }
            close(fd);
        }
    }
    else if (strcmp(cmd, "fork") == 0) {
        pid_t pid = fork();
        if (pid == 0) {
            puts_s("  [child] Hello from forked process!\n");
            _exit(0);
        } else if (pid > 0) {
            char buf[32];
            snprintf(buf, sizeof(buf), "  [parent] child PID=%d\n", pid);
            puts_s(buf);
            waitpid(pid, NULL, 0);
            puts_s("  [parent] child exited\n");
        } else {
            puts_s("  fork failed\n");
        }
    }
    else if (strcmp(cmd, "panic") == 0) {
        puts_s("Triggering panic in 1s...\n");
        sleep(1);
        int fd = open("/proc/sys/kernel/sysrq", O_WRONLY);
        if (fd >= 0) { write(fd, "1", 1); close(fd); }
        fd = open("/proc/sysrq-trigger", O_WRONLY);
        if (fd >= 0) { write(fd, "c", 1); close(fd); }
    }
    else {
        write_all(1, "Unknown: ", 9);
        puts_s(cmd);
        puts_s("\n");
    }
}

/* ========== main ========== */

int main(void) {
    /* 1. 挂载 */
    mount_all();

    /* 2. raw 模式 */
    set_raw();

    /* 3. 横幅 */
    write_all(1, "\x1b[2J\x1b[H", 7);
    puts_s("╔══════════════════════════════════╗\n");
    puts_s("║     HelloOS - Your OS is Alive   ║\n");
    puts_s("║     Kernel: Linux 6.6.32         ║\n");
    puts_s("║     Built with 566.py            ║\n");
    puts_s("╚══════════════════════════════════╝\n\n");
    puts_s("Type 'help' for commands.\n\n");

    /* 4. 主循环 - 读键盘 */
    char line[256];
    int len = 0;
    puts_s("os> ");

    for (;;) {
        char c;
        ssize_t n = read(0, &c, 1);
        if (n != 1) continue;

        if (c == '\r' || c == '\n') {
            puts_s("\n");
            line[len] = '\0';
            handle_cmd(line);
            len = 0;
            puts_s("os> ");
        }
        else if (c == '\x7f' || c == '\b') {
            if (len > 0) { len--; write_all(1, "\b \b", 3); }
        }
        else if (c == '\x03') { /* Ctrl+C */
            puts_s("^C\nos> ");
            len = 0;
        }
        else if (c >= 0x20 && c < 0x7f) {
            if (len < 255) { line[len++] = c; write_all(1, &c, 1); }
        }
    }

    return 0;
}


 

使用gcc进行静态编译

gcc -static -Os -s -o myos.elf hello_os.c

使用 file 命令验证是否为静态链接:

file myos.elf
# 输出应包含 “statically linked”

4.2 运行构建脚本

假设脚本名为 build_os_iso.sh。将其置于与 myos.elf 相同的目录,并赋予执行权限。

点击elf或者命令行运行./elfos.elf运行程序
上传你编写好的elf文件
即可构建

4.3 测试运行

使用QEMU虚拟机快速测试生成的ISO:

qemu-system-x86_64 -cdrom myos.iso

5. 脚本实现原理浅析

5.1 引导的奥秘:GRUB与Multiboot

脚本利用GRUB 2支持Multiboot或Multiboot2协议。它配置GRUB将一个特殊的ELF文件(即你的应用)作为“内核”加载到内存并跳转执行。你的ELF文件的入口点(通常是 main)就成为了操作系统的起点。

5.2 静态链接的重要性

静态链接确保了你的程序不依赖目标系统上的任何动态库(如 libc.so.6)。在ISO引导的早期环境里,这些库根本不存在。静态编译将所需的所有代码都打包进一个文件,实现了真正的“开箱即跑”。

6. 总结与展望

这款自动化脚本代表了操作系统构建工具的一种新思路:极度简化、以应用为中心

未来工作

  1. 兼容动态ELF:通过分析Python打包产物(如PyInstaller生成的ELF)的依赖,自动将所需的 .so 库打包进ISO,实现Python应用的“一键OS化”。
  2. 增强配置:允许用户通过配置文件自定义ISO元数据(如系统名称、版本)、引导参数等。

通过降低操作系统构建的复杂度,我们希望激发更多开发者探索系统底层、创造专用系统的热情。从编写一个简单的C程序开始,你的个人操作系统之旅,现在只需一条命令。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐