c入门第三篇——当你运行./a.out时,发生了什么?

在给师弟讲述完可执行可执行文件之后,师弟追问道:“师兄,你说a.out是可执行文件,那这个可执行文件是怎么运行起来的呢,./a.out做了啥呢?”
我说道:“这是个好问题,开始寻根问底了,好事。”
师弟不好意思的笑了笑。

运行./a.out发生了什么?

  1. Shell 解析命令:Shell(通常是 Bash 或类似的命令行解释器)解析命令行输入,识别并尝试执行 ./a.out。
  2. 执行权限检查:操作系统会检查当前用户对 a.out 可执行文件是否具有执行权限。如果没有执行权限,操作系统会拒绝执行该文件。
#./a.out
-bash: ./a.out: Permission denied
  1. 创建新的进程:如果权限检查通过,操作系统会通过进程创建机制(如 fork() 或 exec())创建一个新的进程。
  2. 配置新进程的地址空间:新进程会被分配一个独立的地址空间,用于存储其代码、数据和堆栈。这个地址空间是与原进程(父进程)相互独立的。
  3. 加载可执行文件:操作系统会从磁盘中读取 a.out 可执行文件,并将其加载到内存中。这包括分配内存空间、将可执行文件的指令(代码段)和静态数据(数据段)加载到合适的内存地址。
  4. 设置运行环境:操作系统会为程序设置运行环境,包括命令行参数、环境变量、当前工作目录等。
  5. 执行程序:操作系统会从可执行文件的入口点开始执行程序的指令(也即main函数)。程序中的代码将被处理器执行,包括各种操作、函数调用和控制流。
  6. 程序结束退出:当程序执行完毕或遇到退出指令时,操作系统会终止程序的执行,同时操作系统会回收程序所占用的内存和其他资源,然后将控制权返回到 Shell 或调用者。所以用户态程序内存泄漏重启程序就可以解决,内核态不行

一般我们都会认为c语言程序都是从main开始的,这只是普通的认识。c语言是支持析构函数的,也即在main之前执行相应的函数。

#include 

__attribute__((constructor)) void print_hello() //析构函数
{
    printf("before hello, world!\n");
}

int main()
{
    printf("hello, world!\n");
    return 0;
}

#./a.out
before hello, world!
hello, world!

操作系统在背后偷偷的干了很多不为人知的事情。

参考阅读:
从c析构函数不起作用,来看gcc中的编译参数–whole-archive和–no-whole-archive

你可能感兴趣的:(c语言入门,c语言,开发语言)