exit函数
终止方式分为两种:
- 正常终止:
- 在
main
函数中执行return
- 调用
exit
函数,该函数会关闭所有标准I/O
流 - 调用
_exit
系统调用函数,此函数由exit
函数调用
- 在
- 异常终止
- 调用
abort
,它产生一个SIGABRT
信号 - 当进程接收到某个信号时
- 调用
不管进程如何终止,最后都会执行同一行代码,这段代码会为相应进程关闭所有打开的描述符,释放它所使用的存储器
进程为了通知它的父进程自己是如何终止的,在终止的时候会返回一个退出状态或终止状态,父进程可以使用wait
或waitpid
函数来获得其子进程的终止状态
退出状态和终止状态是有区别的:
在最后调用_exit
时内核将其退出状态转换成终止状态
一个C
程序是如何启动和终止的:
内核使程序执行的唯一方法是调用一个exec
函数
进程自愿终止的唯一方式时调用一个_exit
(exit)函数
atexit函数
从入中可以看到,进程在调用exit
函数时,exit
函数又调用了一系列
终止处理程,这些终止处理程序是由atexit
函数负责注册的,ANSI C
规定一个进程至多注册32
个函数,这些函数由exit
自动调用
atexit
函数:
#include <stdlib.h>
int atexit(void (*func)(void)) ;
该函数的参数是一个函数地址,作为参数的那个函数地址所指向的函数无参且无返回值,atexit
成功返回0
,否则为1
atexit
函数使用示例:
#include "ourhdr.h"
#include "my_err.h"
static void my_exit1(void);
static void my_exit2(void);
int main(void)
{
if(atexit(my_exit2) != 0)
err_sys("can't register my_exit2");
if(atexit(my_exit1) != 0)
err_sys("can't register my_exit1");
printf("main is done\n");
return 0;
}
static void my_exit1(void)
{
printf("first exit handler\n");
}
static void my_exit2(void)
{
printf("second exit handler\n");
}
现在回来接着说exit
函数,上面说父进程接收子进程的退出状态,但是如果父进程先于子进程结束呢?
- 在这种情况下,子进程的父进程会自动改变为
init
进程,它的PID
为1
在UNIX
中,一个已经终止,但是其父进程却没有给它收尸的进程,被称作僵尸进程(Zombie),这里说的没有收尸,意思就是父进程没有调用wait
或waitpid
函数来对子进程进行回收操作,回收操作具体为:
获取终止子进程的有关信息、释放它仍占用的资源
wait、waitpid函数
现在我们来说一下wait
和waitpid
函数
当进程终止时(不管是正常终止还是异常终止),内核都会向其父进程发送SIGCHLD
信号
因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生 ),所以这种信号也是内核向父进程发的异步通知
对于该信号,父进程有两种处理选项
- 不处理,忽略信号
- 为该信号提供一个处理函数
调用wait
或者waitpid
的进程会出现以下这三种状况:
- 阻塞(如果其所有子进程都还在运行)
- 带着子进程的终止状态立即返回(如果一个子进程已终止,正等待父进程存取其终止状态)
- 出错立即返回(如果它没有任何子进程)
如果我们随意调用wait
函数,很可能会导致进程阻塞,因为此时并不一定有子进程结束,也就是说没有SIGCHLD
信号发出
wait函数和waitpid函数的区别
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
两个函数返回:若成功则为进程ID,若出错则为-1
- 在一个子进程终止前,wait 使其调用者阻塞,
而waitpid
有一选择项,可使调用者不阻塞 waitpid
并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的进程
它俩有一个共同的参数statloc
,一个int
指针,它所指向的内存单元是用来存放子进程的终止状态的,如果不关心子进程的终止状态,则设为null
即可
在UNIX
中,使用以下三个宏来获取终止状态,他们定义在<sys/wait.h>
头文件中,均以WIF
开头
宏 | 说 明 |
---|---|
WIFEXITED(status) | 若为正常终止子进程返回的状态,则为真。对于这种情况可执行WEXITSTATUS(status) 取子进程传送给exit 或_exit 参数的低8 位 |
WIFSIGNALED(status) | 若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号)。对于这种情况,可执行WTERMSIG(status) 取使子进程终止的信号编号。另外,SVR4和4.3+ BSD(但是,非POSIX.1 )定义宏:WCOREDUMP(status) ,若已产生终止进程的core 文件,则它返回真 |
WIFSTOPPED(status) | 若为当前暂停子进程的返回的状态,则为真。对于这种情况,可执行WSTOPSIG(status) 取使子进程暂停的信号编号 |
下面是关于wait
函数和这三个宏的示例程序:
#include <sys/wait.h>
#include "ourhdr.h"
#include "my_err.h"
void pr_exit(int status)
{
if(WIFEXITED(status))
printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
else if(WIFSIGNALED(status))
printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
#ifdef WCOREDUMP
WCOREDUMP(status) ? " (core file.generated)" : "");
#else
"");
#endif
else if(WIFSTOPPED(status))
printf("child stopped, signal number = %d\n", WSTOPSIG(status));
}
int main(void)
{
pid_t pid;
int status;
if((pid =fork()) < 0)
err_sys("fork error");
else if(pid == 0)
exit(7);
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
if((pid = fork) < 0)
err_sys("fork error");
else if(pid == 0)
abort();
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
if((pid = fork()) < 0)
err_sys("fork error");
else if(pid == 0)
status /= 0;
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
exit(0);
}