详解Linux下的_exit(),exit()和atexit()函数
前言
最近发现在linux下Vim是真的好用,再也不用gedit text editor了。
正文
当内核使用一个一个exec函数执行c程序时,在调用main函数之前先调用一个特殊的启动例程,可执行程序需将此例程指定为程序的起始地址。启动例程从内核获取命令行参数和环境变量,然后为调用mian函数做好准备。
fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。
那么在linux中调用exec函数有两种情况:
- 当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。
- 如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。
进程终止
进程终止(包括线程)的方式有8种,前5种为正常终止,后3种为异常终止:
1 从main函数返回
2 调⽤用exit函数;
3 调⽤用_exit或_Exit;
4 最后⼀一个线程从启动例程返回;
5 最后⼀一个线程调⽤用pthread_exit;
6 调⽤用abort函数;
7 接到⼀一个信号并终⽌止;
8 最后⼀一个线程对取消请求做出响应。
一般我们都是使用exit函数正常终止进程
#include <stdlib.h>
void exit( int status );
void _Exit( int status );
#include <unistd.h>
void _exit( int status );
_exit和_Exit立即进入内核,而exit要先做一些清理工作(调用执行个终止处理程序,关闭所有标准I/O流),再进入内核。三个函数所带的整形参数称为终止状态或退出状态,如果
(a)调用这些函数不带参数
(b)main函数中的return 语句无返回值
(c)main函数没有声明返回类型为整形
则进程的终止状态是未定义的。main函数返回一个整形值与用该值调用exit是等价的。
exit()和_exit()
exit函数运行时首先会执行由atexit()函数登记的函数,然后会做一些自身的清理工作,同时刷新所有输出流、关闭所有打开的流并且关闭通过标准I/O函数tmpfile()创建的临时文件。
1._exit函数
#include <unistd.h>
void _exit(int status);
status定义了进程终止的状态,父进程可以使用wait()来获取状态。
2.exit函数
#include <unistd.h>
void exit(int status);
实际上exit函数最终还是要调用_exit()
执行用户通过调用atexit函数或on_exit函数定义的清理函数----->关闭打开的流,所有缓冲区均被写入,临时文件被删除————>调用_exit
如果在退出之前,想增加一些用户的额外功能,可以通过atexit函数来实现功能调用。否则,退出仅仅是将缓冲区中的内容清空,并回收其他系统资源。
##缓冲区(协调不同设备之间的速度差)
无缓冲区(_INOBF):没有缓冲区,每次调用glibc库中的函数时,会立即调用write/read系统调用(对设备的读写不经过缓冲,立即执行数据交换)
行缓冲区(_IIOBF):对于输出流,在收到回车换行符(\n)之前,一律进行数据冲刷,除非缓冲区满。即在向设备输出数据时,在没有遇到回车换行符号(\n),数据只是写入缓冲区。在遇到(\n)时,才将数据写入外设。对于输入流,每次读入一行数据。
全缓冲(_IFBF):在系统缓冲满之前,都可以不停地向缓冲区进行IO操作,而不会立即调用write/read系统调用向外设进行数据读写。系统缓冲区的大小默认是8192字节。
exit是正常退出,在退出可以调用用户自定义函数进行执行用户任务,然后会清空缓冲区中的内容,最后释放所有资源
_exit是强制退出,不在退出时调用其他函数,也不会将缓冲区中的内容不清空。直接强制退出。
exit(int n)、_exit(int n)、return n
n为返回给调用者的退出状态码。调用者通过wait或waitpid函数来接收退出码。
n值最好不要大于128
0代表正常成功退出
1-125代表程序执行出现异常,具体含义可以自己定义
126找到了可执行文件,但无法运行
127找不到文件
大于等于128表示进程因为接收到信号而结束
atexit函数
atexit函数是一个特殊的函数,它是在正常程序退出时调用的函数,我们把他叫为登记函数
(函数原型:int atexit (void (*)(void)))
一个进程可以登记若32个函数,这些函数由exit自动调用,这些函数被称为终止处理函数,atexit函数可以登记这些函数。exit调用终止处理函数的顺序和atexit登记的顺序相反,如果一个函数被多次登记,也会被多次调用。
exit(),_exit()以及atexit()函数实例分析
源码如下
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<wait.h>
void fun1(){
fprintf(stderr,"myFun1 says bye!\n");
}
void fun2(){
fprintf(stderr,"myFun2 says bye!\n");
}
int main(int argc,char *argv[]){
atexit(fun1);
atexit(fun2);
fprintf(stderr,"Oops~~~fogot a newline!\n");
sleep(2);
pid_t result;
result=fork();
if(result==-1){
perror("创建子进程失败!");
exit(0);
}else if(result==0)
{
printf("测试终止进程的_exit函数!\n");
printf("子进程: 这一行我们用缓存");
_exit(0);
}
else{
printf("测试终止进程的exit函数!\n");
printf("父进程: 这一行我们用缓存");
wait(5);
exit(0);
}
return 0;
}
运行结果
后记
30号是女朋友的20岁生日,我决定送出人生中的第一封情书给她,看来是不是得向老爸问下当年他写给老妈的情书是咋样的