Linux的诞生
Hello, everybody out there using minix – I’m doing a (free) operating system (just a hobby, won’t be big and professional like gnu) for 386(486) AT clones. This has been brewing since April, and is starting to get ready.
—— Linus Torvalds (时年 21 岁)
上面这段话大致意思是:我写了一个加强版的操作系统实验,现在与大家分享,发布在 comp.os.minix,跑的都是 GNU 的程序:gcc, bash, ...,依赖 Minix 的工具链,从零开始做东西是不现实的。
Minix?
Minix: 完全用于教学的真实操作系统 by Andrew S. Tanenbaum
年轻人的第一个 “全功能” 操作系统
Minix1 (1987): UNIXv7 兼容:Linus 实现 Linux 的起点
Minix2 (1997): POSIX 兼容:更加完备的系统,书后附全部内核代码
Minix3 (2006): POSIX/NetBSD 兼容:一度是世界上应用最广的操作系统,Intel ME 人手一个
Linux 起飞
- Linux 2.0 引入多处理器 (Big Kernel Lock, 内核不能并行)
- Linux 2.4 内核并行
- 2002 年才引入 Read-Copy-Update (RCU) 无锁同步
- 2003 年 Linux 2.6 发布,随云计算开始起飞
Linux 的 “两面”
专注的内核
- 加载第一个进程
- 相当于在操作系统中 “放置一个位于初始状态的状态机”
- Single user model (高权限)
- 包含一些进程可操纵的操作系统对象
- 除此之外 “什么也没有”
- Linux 变为一个中断 (系统调用) 处理程序
百家争鸣的生态
构建最小Linux
你完全可以构建一个 “只有一个文件” 的 Linux 系统——Linux 系统会首先加载一个 “init RAM Disk” 或 “init RAM FS”,在作系统最小初始化完成后,将控制权移交给 “第一个进程”。借助互联网或人工智能,你能够找到正确的文档,例如 The kernel’s command-line parameters 描述了所有能传递给 Linux Kernel 的命令行选项。
恰恰是 UNIX “干净” 的设计 (完成初始化后将控制权移交给第一个进程) 使得 Linus 可以在可控的工程代价下实现 (相当完善的) POSIX 兼容,从而掀起一场操作系统的革命。时至今日,实现接口级的兼容已经是一件极为困难的工程问题,典型的例子是微软的工程师最终抛弃了 API 行为兼容的 Windows Subsystem for Linux 1.0,进而转向了虚拟机上运行的 Linux 内核。
我们来看一个最小 Linux 的例子:用 QEMU 在给定的 Linux 内核完成初始化后,直接执行我自己编写的、静态链接的 init 二进制文件。
#!/bin/busybox sh
# initrd, only busybox and /init
BB=/bin/busybox
# (1) Print something and exit
$BB echo -e "\033[31mHello, OS World\033[0m"
$BB poweroff -f
# (2) Run a shell on the init console
$BB sh
# (3) Rock'n Roll!
for cmd in $($BB --list); do
$BB ln -s $BB /bin/$cmd
done
mkdir -p /tmp
mkdir -p /proc && mount -t proc none /proc
mkdir -p /sys && mount -t sysfs none /sys
mknod /dev/tty c 4 1
setsid /bin/sh /dev/tty 2>&1
#include
.globl _start
_start:
movq $SYS_write, %rax // write(
movq $1, %rdi // fd=1,
movq $st, %rsi // buf=st,
movq $(ed - st), %rdx // count=ed-st
syscall // );
movq $SYS_exit, %rax // exit(
movq $1, %rdi // status=1
syscall // );
st:
.ascii "\033[01;31mHello, OS World\033[0m\n"
ed:
# Requires statically linked busybox
INIT := /init
initramfs:
# Copy kernel and busybox from the host system
@mkdir -p build/initramfs/bin
sudo bash -c "cp /boot/vmlinuz build/ && chmod 666 build/vmlinuz"
cp init build/initramfs/
cp $(shell which busybox) build/initramfs/bin/
# Pack build/initramfs as gzipped cpio archive
cd build/initramfs && \
find . -print0 \
| cpio --null -ov --format=newc \
| gzip -9 > ../initramfs.cpio.gz
run:
# Run QEMU with the installed kernel and generated initramfs
qemu-system-x86_64 \
-serial mon:stdio \
-kernel build/vmlinuz \
-initrd build/initramfs.cpio.gz \
-machine accel=kvm:tcg \
-append "console=ttyS0 quiet rdinit=$(INIT)"
clean:
rm -rf build
.PHONY: initramfs run clean
通过 initrd 执行我自己编写的、静态链接的 init 二进制文件。
initrd之后
initrd: 并不是我们实际看到的 Linux,只是一个内存里的小文件系统
- 我们 “看到” 的都是被 init 创造出来的
- 加载剩余必要的驱动程序,例如网卡
- 根据 fstab 中的信息挂载文件系统,例如网络驱动器
- 将根文件系统和控制权移交给另一个程序,例如 systemd
构建一个 “真正” 的应用世界
int pivot_root(const char *new_root, const char *put_old);
pivot_root()
changes the root mount in the mount namespace of the calling process. More precisely, it moves the root mount to the directoryput_old
and makesnew_root
the new root mount. The calling process must have theCAP_SYS_ADMIN
capability in the user namespace that owns the caller's mount namespace.- 执行
/usr/sbin
(Kernel 的 init 选项)- 看一看系统里的文件是什么吧
- 计算机系统没有魔法 (一切都有合适的解释)
例子:NOILinux Lite
在 init 时多做一些事
export PATH=/bin
busybox mknod /dev/sda b 8 0
busybox mkdir -p /newroot
busybox mount -t ext2 /dev/sda /newroot
exec busybox switch_root /newroot/ /etc/init
- pivot_root 之后才加载网卡驱动、配置 IP
- 这些都是 systemd 的工作
- (你会留意到 tty 字体变了)
- 之后 initramfs 就功成身退,资源释放