学习自李慧琴老师
1 main函数
当前进程的出入口
2 进程的终止方式,十分重要,一定记清!
正常终止:
从main函数返回: return 0
调用exit : exit(0) / exit(1) ... 是库函数
调用 _exit 或 _Exit 是系统调用
最后一个线程从其启动例程返回
最后一个线程调用 pthread_exit
异常终止:
调用 abort,发送一个 abort 信号给当前进程,杀掉当前进程
接到一个信号并终止
最后一个线程对其取消请求作出响应
atexit() 钩子函数
NAME
exit - cause normal process termination,导致正常进程终止
SYNOPSIS
#include <stdlib.h>
void exit(int status);
DESCRIPTION
The exit() function causes normal process termination and the value of status & 0377 is returned to the parent (see wait(2)).
All functions registered with atexit(3) and on_exit(3) are called, in the reverse order of their registration.
在调用 exit() 正常结束进程的时候, 所有用atexit() 和 on_exit() 钩子函数注册的函数都会被调用,以他们当时被注册的逆顺序被调用,
0377 八进制 11 111 111,保留低八位,即char 大小,即返回值是: -128 ~~ +127
钩子函数
atexit - register a function to be called at normal process termination
在进程正常终止的时候,该函数将会被调用。
有点像C++的析构函数
SYNOPSIS
#include <stdlib.h>
int atexit(void (*function)(void));
实验; 钩子函数
#include <stdio.h>
#include <stdlib.h>
static void f1(void)
{
puts("f1() is working!");
}
static void f2(void)
{
puts("f2() is working!");
}
static void f3(void)
{
puts("f3() is working!");
}
int main()
{
puts("Begin!");
// 此处并不是代表函数调用,只是将目标函数挂在钩子函数上
// 并不实现调用,而是在在进程正常终止的时候,即exit()的时候逆序调用
atexit(f1);
atexit(f2);
atexit(f3);
puts("End!");
exit(0);
}
mhr@ubuntu:~/work/linux/进程环境$ gcc test.c
mhr@ubuntu:~/work/linux/进程环境$ ./a.out
Begin!
End!
f3() is working!
f2() is working!
f1() is working!
mhr@ubuntu:~/work/linux/进程环境$
钩子函数存在的意义在于,在打开多个文件的时候,为了防止内存泄漏,通常做法是,当 当前文件打开失败的时候 需要关闭之前打开的所有文件,如果之前打开的文件过多,那么要关闭太多文件,很不方便,故可以用钩子函数解决,在每次成功打开一个文件之后 立即 将关闭操作挂载atexit()上,这样 只要遇到 exit() 就会自动关闭之前的文件。
_EXIT(2) Linux Programmer’s Manual _EXIT(2)
NAME
_exit, _Exit - terminate the calling process
SYNOPSIS
#include <unistd.h>
void _exit(int status);
#include <stdlib.h>
void _Exit(int status);
_exit VS exit
图中虚线框代表进程空间。
执行 exit(),会执行钩子函数,IO清理,最后调用 _exit() 结束当前进程,是库函数
执行 _exit() 会直接结束当前进程,是系统调用

那么什么时候用 exit() 什么时候用 _exit() ?
伪代码:
int func()
{
return 0/1/2; 返回 0 或 1 或 2
}
int main()
{
int f;
f = func();
......
......
switch(f)
{
case 0:
case 1:
case 2:
default:
}
}
假设 此时我们的switch(f) ,没有走1 2 3 ,出现了第四种可能,那么就会走 default分支,很多人会直接在 default 分支 直接 调用exit() 结束进程。但是往往事情没那么简单,因为出现这种情况的原因有可能是 中间的代码出现了问题,很有可能是 写越界,把我的变量f 的空间覆盖写了。那么在这种情况下,如果在default 分支直接调用 exit(),那么 众多钩子函数将会被调用,众多IO 将会刷新或者同步,这样就会将当前的脏数据刷新到文件当中,并且钩子函数又释放或者更新了一些内容,进一步扩散了当前错误!!!。所以这种情况 default 分支 想都不要想,直接 调用 _exit() 退出进程,任何动作都不要做了,IO清理,钩子函数 都不要调用了,直接退出当前进程。或者 abort() 给当前进程发送一个信号,把当前的自己kill 掉,顺便得到一个出错现场。
本文深入解析进程的正常与异常终止方式,包括main函数返回、exit与_exit调用的区别,及钩子函数atexit的作用与应用场景,帮助理解进程生命周期的管理。
1967

被折叠的 条评论
为什么被折叠?



