进程终止:
vfork函数
vfork用于创建一个新进程,而新进程的目的是exec一个新程序,vfork与fork一样都创建子进程,但是并不将父进程的地址空间完全复制到子进程中去,因为子进程会立即调用exec,于是也就不会存访该地址空间。
相反,它在调用exec之前,它在父进程空间运行。
另一个区别是:vfork保证子进程先运行,在它调用exec或exit之后,父进程才可能被调度运行。
#include<stdio.h>
#include"apue.h"
#include<myerror.h>
int glob=6;
char buf[]="a write to stdout\n";
int main(void)
{
int var;
pid_t pid;
var=88;
if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1)
err_sys("write error");
printf("before fork\n");
if((pid=vfork())<0)
{
err_sys("vfork error");
}
else if(pid==0)
{
glob++;
var++;
_exit(0);
}
printf("pid=%d,glob=%d,var=%d\n",getpid(),glob,var);
exit(0);
}
zsj@zsj-virtual-machine:~/ccode$ cc 8-2.c zsj@zsj-virtual-machine:~/ccode$ ./a.out
a write to stdout
before fork
pid=15390,glob=7,var=89
zsj@zsj-virtual-machine:~/ccode$
子进程对变量golb,var做增1操作,结果改变了父进程中的变量值。因为子进程在父进程的地址空间运行,所以这并不令人惊讶。作用的确与fork不同。
调用了_exit而不是exit。_exit并不执行标准I/O冲洗操作。如果调用的是exit,则该程序的输出是不确定的。
exit函数
(1)在main函数内执行return语句。等效于使用exit
(2)在调用exit函数。此函数由ISO C定义,其操作包括调用各终止处理程序。然后关闭所有I/O流等。
(3)调用_exit 或_Exit函数。目的是为进程提供一种无需运行终止处理程序或信号处理程序而终止的方法。
(4)进程的最后一个线程在其启动例程中执行返回语句。
(5)进程中的最后一个线程调用pthread_exit函数。
对于以上的任何终止情形,我们都希望能够通知其父进程它是如何终止的。对于三个终止函数,实现这一点的方法是,将其退出状态作为参数传递给函数。
在任何一种情况下,该终止进程的父进程都能用wait 或waitpid函数取得其终止状态。
在说明fork函数时,显而易见,子进程是调用fork后生成的。上面又说明了子进程将其终止状态返回给父进程。但是如果父进程在子进程之前终止,那么
对于父进程已经终止的所有进程,他们的父进程都改变为init进程。我们称这些进程由init进程领养。
如果子进程在父进程之前终止,那么父进程又如何能在做相应检查时得到子进程的终止状态呢?
答案是内核为每个终止子进程保存了一定量的信息,所以当父进程调用wait或waitpid时,可以得到这些信息。
这些信息至少包括进程ID,进程的终止状态、以及该进程的cpu时间总量。内核可以释放终止进程所有的存储区、关闭所有其打开文件。
一个已经终止、但是其父进程尚未对其进行善后处理的进程称为僵死进程。
一个由init领养的进程终止时不会变成僵死进程。因为只要进程终止,init就会调用一个wait函数取得其终止状态。
wait 和waitpid函数
当一个进程正常或异常终止时,内核就向其父进程发送 SIGCHLD信号。因为子进程终止是个异步事件,所以这种信号也是内核向父进程发的异步通知。
#include<sys/wait.h>
pid_wait(int *statloc);
pid_waitpid(pid_t pid,int *statloc,int options);
参数statloc是一个整型指针,如果staltloc不是一个空指针,则终止进程的终止状态就放在它指向的单元里。