1. 进程终止
有八种方式使进程终止。其中5种是正常,它们是:
- 从main函数返回
- 调用exit
- 调用_exit或_Exit
- 最后一个线程从其启动例程返回
- 从最后一个线程调用pthread_exit
异常终止有三种方式:
- 调用abort
- 收到一个信号
- 最后一个线程对取消请求做出响应
1.1 退出函数
以下三个函数用于正常终止一个程序。其中_exit和_Exit是系统调用,立即进入内核;exit则先执行一些清理工作,然后返回内核。
void _exit(int status);
void _Exit(int status);
void exit(int status);
exit函数总是执行标准I/O库的清理关闭操作,对于所有打开的流调用fclose函数,这会造成输出缓冲中的所有数据被写(冲洗)到文件上。
这三个退出函数都有一个参数,即终止状态(或退出状态)。main函数返回一个整形值与用该值调用exit是等价的。于是在main函数中exit(0);
等同于return 0
;
1.2 atexit函数
一个进程可以登记最多32个函数(一些操作系统实现可能更多)个函数,这些函数将由exit自动调用。我们称这些函数为终止处理程序,通过atexit函数来登记这些函数
int atexit(void (*function)(void));
exit调用这些函数的顺序与登记它们的顺序相反,同一函数如果被登记多次也会被调用多次。
exit函数会先调用各终止处理程序,再fclose所有打开流。然后再调用_exit函数终止进程。
如果程序调用exec函数族,则会清除所有已经注册的终止处理程序。
内核使程序执行的唯一方法是调用一个exec函数。进程自愿终止的唯一方法是显式或隐式(通过exit)调用_exit或_Exit。进程也可以非自愿的由一个信号终止。
2. 命令行参数
调用exec函数的进程可以将命令行参数传递给新程序。
UNIX内核并不查看这些字符串,它们的解释完全取决于各个应用程序,因此需要通过exec将这些参数传递给进程
命令行参数保存在main函数的形参内
int main(int argc, char* argv[]);
其中argc是命令行参数个数,argv是一个指针数组并且该数组以NULL元素结尾。
示例:
int main(int argc, char* argv[]) {
for(int i = 0 ; i < argc ; ++i) {
cout << argv[i] <<" ";
}
cout << endl;
}
$ ./a.out aa bb 123
> ./a.out aa bb 123
可以看出可执行程序名即为第一个命令行参数。
3. 环境表
每一个进程都有一张环境表,该表也是一个字符指针数组,其中每个指针指向一个以null结束的C字符串地址。全局变量environ包含了该指针数组的地址。
extern char **environ;
每一个环境变量由name=value
形式的字符串构成,其中name字段一般是大写字母组成。
当然也可以通过main函数的第三个参数来访问环境变量
int main(int argc, char* argv[], char**envp);
4. C程序的存储空间布局
C程序由以下部分组成:
-
正文段(或代码段).text:
由CPU执行的机器指令部分组成(即程序编译之后,编译器会将代码翻译成二进制的机器码,机器码存储在代码段(.text)中)。通常正文段可以共享,所以即使是频繁执行的程序在存储器中也只需有一个副本。并且正文段通常是只读的,以防止程序由于意外而修