复习
_start()
→ ...本次课回答的问题
本次课主要内容
回顾 thread-os.c 的加载过程
_start()
操作系统会加载 “第一个程序”
init=
,按照 “默认列表” 尝试一遍程序:状态机
没有存储设备,只有包含两个文件的 “initramfs”
$ tree .
.
├── bin
│ └── busybox (可以在我们的Linux里直接执行)
└── init
加上 vmlinuz (内核镜像) 就可以在 QEMU 里启动了
可以直接在文件系统中添加静态链接的二进制文件
BusyBox: The Swiss Army Knife of embedded Linux
c1="arch ash base64 cat chattr chgrp chmod chown conspy cp cpio cttyhack date dd df dmesg dnsdomainname dumpkmap echo ed egrep false fatattr fdflush fgrep fsync getopt grep gunzip gzip hostname hush ionice iostat ipcalc kbd_mode kill link linux32 linux64 ln login ls lsattr lzop makemime mkdir mknod mktemp more mount mountpoint mpstat mt mv netstat nice nuke pidof ping ping6 pipe_progress printenv ps pwd reformime resume rev rm rmdir rpm run-parts scriptreplay sed setarch setpriv setserial sh sleep stat stty su sync tar touch true umount uname usleep vi watch zcat"
c2="[ [[ awk basename bc beep blkdiscard bunzip2 bzcat bzip2 cal chpst chrt chvt cksum clear cmp comm crontab cryptpw cut dc deallocvt diff dirname dos2unix dpkg dpkg-deb du dumpleases eject env envdir envuidgid expand expr factor fallocate fgconsole find flock fold free ftpget ftpput fuser groups hd head hexdump hexedit hostid id install ipcrm ipcs killall last less logger logname lpq lpr lsof lspci lsscsi lsusb lzcat lzma man md5sum mesg microcom mkfifo mkpasswd nc nl nmeter nohup nproc nsenter nslookup od openvt passwd paste patch pgrep pkill pmap printf pscan"
c3="pstree pwdx readlink realpath renice reset resize rpm2cpio runsv runsvdir rx script seq setfattr setkeycodes setsid setuidgid sha1sum sha256sum sha3sum sha512sum showkey shred shuf smemcap softlimit sort split ssl_client strings sum sv svc svok tac tail taskset tcpsvd tee telnet test tftp time timeout top tr traceroute traceroute6 truncate ts tty ttysize udhcpc6 udpsvd unexpand uniq unix2dos unlink unlzma unshare unxz unzip uptime users uudecode uuencode vlock volname w wall wc wget which who whoami whois xargs xxd xz xzcat yes"
for cmd in $c1 $c2 $c3; do
/bin/busybox ln -s /bin/busybox /bin/$cmd
done
mkdir -p /proc && mount -t proc none /proc
mkdir -p /sys && mount -t sysfs none /sys
export PS1='(linux) '
2021 年,CCF 以迅雷不及掩耳盗铃之势发布了 NOILinux 2.0
和刚才的 “最小” 系统但本质一样
switch_root
(pivot_root
系统调用) 完成 “启动”Linux 操作系统启动流程
_start()
→ 第一个程序 /bin/init
→ 程序 (状态机) 执行 + 系统调用操作系统为 (所有) 程序提供 API
C 程序 = 状态机
main(argc, argv)
如果要创建状态机,我们应该提供什么样的 API?
UNIX 的答案: fork
int fork();
模拟状态机需要资源
:(){:|:&};: # 刚才的一行版本
:() { # 格式化一下
: | : &
}; :
fork() { # bash: 允许冒号作为标识符……
fork | fork &
}; fork
因为状态机是复制的,因此总能找到 “父子关系”
systemd-+-accounts-daemon---2*[{accounts-daemon}]
|-agetty
|-atd
|-automount---2*[{automount}]
|-avahi-daemon---avahi-daemon
|-cron
|-dbus-daemon
|-irqbalance---{irqbalance}
|-lxcfs---7*[{lxcfs}]
...
试着拿出一张纸,写出以下程序的输出结果
pid_t pid1 = fork();
pid_t pid2 = fork();
pid_t pid3 = fork();
printf("Hello World from (%d, %d, %d)\n", pid1, pid2, pid3);
问以下程序的输出结果
./a.out
v.s. ./a.out | cat
for (int i = 0; i < 2; i++) {
fork();
printf("Hello\n");
}
计算机系统里没有魔法。机器永远是对的。
多线程程序的某个线程执行 fork()
,应该发生什么?
我们可能作出以下设计:
光有 fork 还不够,怎么 “执行别的程序”?
UNIX 的答案: execve
int execve(const char *filename, char * const argv, char * const envp);
filename
的程序argv
(v) 和环境变量 envp
(e)main()
的参数!“应用程序执行的环境”
env
命令查看PATH
: 可执行文件搜索路径PWD
: 当前路径HOME
: home 目录DISPLAY
: 图形输出PS1
: shell 的提示符export
: 告诉 shell 在创建子进程时设置环境变量export ARCH=x86_64-qemu
或 export ARCH=native
AM_HOME
终于破案了PATH
可执行文件搜索路径
[pid 28369] execve("/usr/local/sbin/as", ["as", "--64", ...
[pid 28369] execve("/usr/local/bin/as", ["as", "--64", ...
[pid 28369] execve("/usr/sbin/as", ["as", "--64", ...
[pid 28369] execve("/usr/bin/as", ["as", "--64", ...
PATH
里指定的顺序$ PATH="" /usr/bin/gcc a.c
gcc: error trying to exec 'as': execvp: No such file or directory
$ PATH="/usr/bin/" gcc a.c
计算机系统里没有魔法。机器永远是对的。
有了 fork, execve 我们就能自由执行任何程序了,最后只缺一个销毁状态机的函数!
UNIX 的答案: _exit
void _exit(int status)
这个简单……
exit 的几种写法 (它们是
exit(0)
- stdlib.h
中声明的 libc 函数atexit
_exit(0)
- glibc 的 syscall wrapperexit_group
” 系统调用终止整个进程 (所有线程)atexit
syscall(SYS_exit, 0)
exit
” 系统调用终止当前线程atexit
本次课回答的问题
Take-away messages
_start()
→ 执行第一个程序 /bin/init
→ 中断/异常处理程序