main函数:
C程序或C++程序总是从main函数开始执行的,其中这个总是从main函数开始执行是我们人为约定的,当然现在从main函数开始执行已经成为语言标准了,在汇编层次,我们可以把程序起始执行地址指向一个自定义的名字。
书本上7.2节这里的翻译很是生硬,字面意思直接翻译过来,让人不太好理解,原文如下:
When a C program is executed by the kernel—by one of the exec functions, which
we describe in Section 8.10—a special start-up routine is called before the main
function is called. The executable program file specifies this routine as the starting
address for the program; this is set up by the link editor when it is invoked by the C
compiler. This start-up routine takes values from the kernel—the command-line
arguments and the environment — and sets things up so that the main function is called
as shown earlier.
中文翻译:
当内核执行C程序时(使用一个exec函数,8.10节将说明exec函数),在调用main前先调用一个特殊的启动例程。可执行程序文件将此启动例程指定为程序的起始地址——这是由连接编辑器设置的,而连接编辑器则由C编译器调用。启动例程从内核取得命令行参数和环境变量值,然后为按上述方式调用main函数做好安排。
这段话中的“启动例程”是什么意思?启动例程即原版中的start-up routine,拿掉形容词修饰start-up只剩下真正的关键词routine,routine是值什么?routine 或 subroutine是近义词,在不同的编程语言中,称呼不太一样,但意思大致系统,在C/C++语言中,它的意思是function函数,在Java中它的意思是method方法。
因此原版的这段话正确翻译是:当C程序要被内核执行时,是通过一个叫做exec的函数来加载的,exec将在8.10节说明。在C程序的main被调用执行起来之前,会先调用一个特殊的启动函数(它的名字是_start,可在汇编层次看到)。可执行程序文件(也即前面的C程序二进制文件)将该启动函数作为程序启动的入口地址,这个启动地址是C编译器在链接阶段配置的。这个启动函数负责从内核那里接收命令行参数和环境变量,设置好这些之后再调用main函数。
进程正常终止:
1.从main函数返回,即retrun 0;
2.调用exit,即在main函数内或者其他会被main调用的函数体内调用exit();
3.调用_exit或_Exit,即在main函数内或者其他会被main调用的函数体内调用_exit或_Exit;
4.最后一个线程从其所在进程返回;
5.最后一个线程在其所在进程调用pthread_exit。
进程异常终止:
6.调用abort;
7.进程接收到信号;
8.进程中最后一个线程最取消做出响应。
C/C++程序的正常启动结束过程使用C语言代码的形式接近于:exit(main(argc,argv)) ,实际上这段过程一般是用汇编来写,流程大致如下图:
上图中的清理函数是通过atexit函数来登记的,atexit函数的头文件和原形如下:
#include <stdlib.h>
int atexit(void (*func)(void));
atexit函数的参数是一个函数指针,该指针指向的函数不需要参数,也不返回任何值。
atexit函数可以登记多个清理函数,清理函数先后顺序是以栈的形式压入,即LIFO(last in first out)。
命令行参数是例程调用main函数时传递的。
除了传递命令行参数给main函数,main程序也可以从环境表中获取环境变量,环境变量是存储在environ指针中的。