第三章:运行程序
这篇文章是关于如何在Linux操作系统上执行程序的详细解析。
1. 背景知识:
- 文章首先回顾了CPU执行机器码、基于环的安全模型以及系统调用(syscalls)的基础知识。
2. Linux内核与x86-64架构:
- 专注于Linux操作系统在x86-64架构上的程序加载和运行机制。
- Linux是开源的,易于通过阅读源代码进行研究。
- x86-64架构是现代桌面计算机常用的架构,也是许多代码的目标架构。
3. exec系统调用(Syscalls):
execve
是用于加载程序并替换当前进程的系统调用。- 其他如
execlp
、execvpe
等系统调用都是建立在execve
之上的。
4. execve的工作原理:
execve
调用签名:cint execve(const char *filename, char *const argv[], char *const envp[]);
filename
参数指定要运行的程序路径。argv
是程序参数的空终止列表。envp
包含环境变量的空终止列表。
5. execve的内部实现:
SYSCALL_DEFINE3
宏定义了一个三参数系统调用的代码。getname()
函数用于将用户空间的字符串复制到内核空间。
6. linux_binprm结构体:
- 用于准备新程序的虚拟内存管理。
- 存储
argc
、envc
、filename
、interp
和缓冲区buf
。
7. Binfmt处理程序:
- 内核通过迭代一系列binfmt(二进制格式)处理程序来确定程序的格式。
- 每个处理程序都有一个
load_binary()
函数,用于检查程序格式并准备执行。
8. 脚本格式处理:
- 脚本的shebang(#!)行是由内核处理的,而不是由shell处理。
- 如果文件以shebang开头,binfmt处理程序将读取解释器路径和参数。
9. 脚本参数修改:
binfmt_script
假定argv[0]
是程序名称,并会修改argv
,添加解释器路径和参数。
10. binfmt_misc:
- 允许通过用户空间配置添加有限格式的处理程序。
- 通过挂载特殊文件系统
/proc/sys/fs/binfmt_misc/
来配置。
11. exec系统调用的结果:
- 如果找到可执行的二进制格式,内核将运行该代码,替换旧代码。
- 如果没有找到合适的格式处理程序,将返回错误码。
12. 没有shebang的脚本:
- 如果exec调用失败,大多数shell会将文件作为shell脚本重新尝试执行。
13. POSIX标准:
- 如果exec调用因
ENOEXEC
错误失败,shell将执行一个命令,相当于调用shell并传递命令名称作为第一个参数。
文章通过深入探讨Linux内核的源代码,详细解释了如何在Linux上加载和执行程序,包括execve
系统调用的内部工作机制以及如何处理脚本和二进制格式。这些信息对于理解操作系统的工作原理非常有用。