Linux源码解读(七):文件系统——可执行文件的加载和执行
- 本文目录导读:
- 1、ELF格式
- 2、可执行文件加载
- 3、可执行文件执行
- 4、总结
在前面的文章中,我们已经讲解了Linux文件系统的基本概念、目录结构以及各种类型的文件。今天,我们将深入探讨一个非常重要的主题:可执行文件的加载和执行过程。
作为一名程序员,我们每天都会编写各种应用程序或者脚本,并通过编译器生成可执行文件。但是,在运行这些程序之前,操作系统需要对其进行加载并分配内存空间。那么,在Linux中,这个过程具体是如何实现的呢?
一、ELF格式
在介绍可执行文件加载和执行之前,先来了解一下ELF格式。ELF(Executable and Linkable Format)即“可执行与链接格式”,它是Unix类操作系统上二进制目标代码所采用的标准格式。
ELF不仅支持静态库和动态库等多种形式组合成符号表等信息,并且还可以指定段权限、节区属性以及调试信息等内容。
当然,在使用GCC编译工具时,默认就会生成一个符合ELF规范的二进制目标代码。
二、可执行文件加载
1. 加载方式
当用户从shell命令行输入某个命令时(例如ls),内核第一会去搜索环境变量PATH指定路径下是否有该命令的可执行文件。
如果找到了,就会将该可执行文件加载到内存中。这个过程主要包括以下几个步骤:
(1)检查权限:内核第一会检查用户对该文件是否有读取和执行权限,只有具备这些权限才能够继续进行下一步操作。
(2)分配空间:在物理内存上为新进程分配一个虚拟地址空间,并根据程序头部信息确定各段的大小、属性以及所需对齐方式等内容。同时,在用户态和内核态之间建立好映射关系。
(3)载入代码段:将可执行文件中的代码段复制到刚刚分配好的虚拟地址空间中,并设置相应页表项标志位。
(4)载入数据段:同样地,将可执行文件中的数据段复制到虚拟地址空间中,并设置相应页表项标志位。
2. 加载器
那么,在Linux系统中,是哪个程序负责完成上述工作呢?答案是ld-linux.so.x或者其它类似名称的动态链接器库。当我们在shell命令行输入某个命令时,实际上是由此启动了一个新进程,并且通过动态链接器来完成相关工作并运行用户指定程序。
三、可执行文件执行
1. 入口函数
当我们成功加载完毕某个可执行文件后,接下来就是真正的程序运行过程。在C语言中,每个程序都必须包含一个入口函数main()。
当操作系统将控制权交给该程序时,会第一跳转到该入口地址,并开始执行相关代码。此时,用户指定的参数也会被传递进去。
2. 系统调用
除了main()函数之外,在Linux内核中还提供了许多常用的系统调用API供我们使用(例如open、read、write等),以便于对各种资源进行读写和管理等操作。
这些系统调用实际上是由用户态向内核态发起请求并获取返回结果的过程。而在ELF格式的可执行文件中,则需要通过PLT表和GOT表来完成动态链接库函数的加载和绑定等工作。
3. 信号处理
在某些特殊情况下(例如按下Ctrl+C组合键或者遇到非法指令等错误),操作系统可能会向当前进程发送一条信号通知其停止或者重新启动等操作。
此时,我们需要编写相应信号处理函数,并且注册到signal() API中以便于捕获相关事件并做出相应响应。
四、总结
本文主要介绍了Linux源码解读系列第七篇:文件系统——可执行文件的加载和执行过程。通过深入剖析ELF格式及其规范约束,我们了解了Linux系统如何在内存中为可执行文件分配虚拟地址空间,并且将其载入到物理内存中。
同时,在介绍动态链接库的加载和使用过程时,我们也深刻认识到操作系统与用户程序之间紧密相连的关系。最后,希望本文能够对大家有所启发并提供一些参考价值。