程序的开始和结束
编译链接时的引导代码(构建执行环境)调用main函数
①编译链接时的链接器:将编译器中引导代码给连接进去和应用程序一起构成最终的可执行程序。
②运行时的加载器:将这个程序加载到内存中去执行这个程序。
程序在编译连接时用链接器,运行时用加载器,这两个东西对程序运行原理非常重要。
C程序的执行过程:
C编译器调用链接器,链接器设置可执行程序文件的启动起始地址(启动例程),启动例程获得内核传递来的
命令行参数和环境变量值,为调用main函数做准备。【实际上该启动例程常用汇编语言编写】,如果将启动例程换做C语言就是:exit(main(argc,argv));
main(int argc,char *argv[],char *engv[]);argv为指向参数的各个指针所构成的数组。
我们通常认为C语言的起始函数是main函数,实质上一个程序的启动函数并不一定是main函数,这个可以采用链接器来设置,但是gcc中默认main就是C语言的入口函数,在main函数启动之前,内核会调用一个特殊的启动例程,这个启动例程从内核中【取得命令行参数值和环境变量值】,为调用main函数做好准备,因此对应用程序而言main函数并不是起始,但是对应C语言而言,main函数就是入口地址,其他的由链接器帮助我们完成,实际上mian函数的执行是使用了exec函数,这是一个函数族,这也是内核执行一个程序的唯一方法。
程序如何结束
(1)正常终止:
return:会执行终止处理函数
exit:会执行终止处理函数,做一些清理工作,再进入内核
_exit /_EXIT:直接进入内核中。 不执行atexit注册的进程终止处理函数。
(2)非正常终止:自己或他人发信号(类似于电话标志)终止进程,信号也是有优先级的
atexit向操作系统注册进程终止处理函数(即main执行结束后调用的函数)
#include<stdlib.h>
int atexit(void(*func)(void));
atexit负责注册,参数是一个函数地址(或者说是一个函数指针)
进程终止处理函数void(*func)(void)负责进行清理操作
注意:按照ISO C的规定,一个进程可以登记多达32个函数,这些函数将由exit自动调用。atexit()注册的函数类型应为不接受任何参数的void函数,exit调用这些注册函数的顺序与它们 登记时候的顺序相反(压栈过程)。同一个函数如若登记多次,则也会被调用多次。