Skip to content

第三章:运行程序

这篇文章是关于如何在Linux操作系统上执行程序的详细解析。

1. 背景知识:

  • 文章首先回顾了CPU执行机器码、基于环的安全模型以及系统调用(syscalls)的基础知识。

2. Linux内核与x86-64架构:

  • 专注于Linux操作系统在x86-64架构上的程序加载和运行机制。
  • Linux是开源的,易于通过阅读源代码进行研究。
  • x86-64架构是现代桌面计算机常用的架构,也是许多代码的目标架构。

3. exec系统调用(Syscalls):

  • execve是用于加载程序并替换当前进程的系统调用。
  • 其他如execlpexecvpe等系统调用都是建立在execve之上的。

4. execve的工作原理:

  • execve调用签名:
    c
    int execve(const char *filename, char *const argv[], char *const envp[]);
  • filename参数指定要运行的程序路径。
  • argv是程序参数的空终止列表。
  • envp包含环境变量的空终止列表。

5. execve的内部实现:

  • SYSCALL_DEFINE3宏定义了一个三参数系统调用的代码。
  • getname()函数用于将用户空间的字符串复制到内核空间。

6. linux_binprm结构体:

  • 用于准备新程序的虚拟内存管理。
  • 存储argcenvcfilenameinterp和缓冲区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系统调用的内部工作机制以及如何处理脚本和二进制格式。这些信息对于理解操作系统的工作原理非常有用。

基于 知识共享 CC BY-NC-SA 许可发布