fork();用于创建进程;返回值是整型:若是在子进程中则是返回0,父进程中返回子进程的PID
【例1】
void fork17()
{
if (fork() == 0) {
printf("Child: pid=%d pgrp=%d\n",
getpid(), getpgrp());
}
else {
printf("Parent: pid=%d pgrp=%d\n",
getpid(), getpgrp());
}
while(1);
}
运行结果:
fork();创建了一个子进程;子进程,父进程分别打印了各自的pid与pgid
pid=4847和pid=4848属于同一进程组,组长是父进程pid=4847
值得注意的是程序“卡”住了,明显是由于了while(1);这条指令使程序(两个)停止在了这个位置
这时候,ctrl+z使进程挂起,再用ps指令查看当前正在运行的进程:
发现有两个forks.o进程,一个父进程一个子进程
杀死pid值为4847的forks.o进程,发现pid为4848的进程也同时消失了,说明pid=4847的进程使4848进程的父进程,4847在被杀死之前回收了其子进程
但是,如果在如果用ctrl+c替换前文提到的ctrl+z,会有不一样的结果:
原因使ctrl+z意为“挂起”,即将程序只是表面上退出,但是保持其进程继续在后台运行,只是在我们“眼前消失”了
而ctrl+c表示终止,将终止所有进程,后台也不保持运行
【例2】
void fork16()
{
if (fork() == 0) {
printf("Child1: pid=%d pgrp=%d\n",
getpid(), getpgrp());
if (fork() == 0)
printf("Child2: pid=%d pgrp=%d\n",
getpid(), getpgrp());
while(1);
}
}
运行结果:
按理说1.应该有三个进程 2.而且程序并没有被在while(1);处停住,而是似乎自动被挂起了。可以执行其他命令
进程树如下:
最初的父进程最终终止了,而两个子进程都陷入了while(1);而没有终止,因此父进程在终止的时候不会回收其子进程
于是两个子进程都变成了孤儿进程,由inti进程收养这时要用kill命令杀死进程:
【例3】
#define N 5
int ccount = 0;
void fork15()
{
pid_t pid[N];
int i;
ccount = N;
signal(SIGCHLD, child_handler2);
for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0) {
sleep(1);
exit(0); /* Child: Exit */
}
while (ccount > 0) {
pause();
}
}
void child_handler2(int sig)
{
int child_status;
pid_t pid;
while ((pid = wait(&child_status)) > 0) {
ccount--;
printf("Received signal %d from process %d\n", sig, pid); /* Unsafe */
fflush(stdout); /* Unsafe */
}
}
运行结果:
在函数fork15中有一条语句:signal(SIGCHLD, child_handler2); 意思即为在以后的进程中,如果进程一旦捕获了一个类型为SIGCHLD的信号则会调用*child_handler这个信号处理程序来处理信号
SIGCHLD(==17):一个子进程停止或者终止
再来关注child_handler这个函数:里面用wait函数做了一个循环条件,因为在调用signal之前就已经把所以的子进程创建完成,所以signal中的while循环会等待所有的子进程结束才退出循环,wait的默认状态是挂起调用进程,只有当有某一子进程结束时才会返回这个子进程的pid,没有未返回子进程则返回-1
进程树如下:
让子程序sleep(1)的原因是保证在任一子进程结束时,父进程已经进入pause。
当接收到子进程的终止信号SIGCHLD时会停止pause,继而执行调用child_handler,打印相关信息,从child_handler返回之后ccount已经减到了0,退出循环,退出程序
【例4】
#define N 5
int ccount = 0;
void child_handler(int sig)
{
int child_status;
pid_t pid = wait(&child_status);
ccount--;
printf("Received SIGCHLD signal %d for process %d\n", sig, pid); /* Unsafe */
fflush(stdout); /* Unsafe */
}
/*
* fork14 - Signal funkiness: Pending signals are not queued
*/
void fork14()
{
pid_t pid[N];
int i;
ccount = N;
signal(SIGCHLD, child_handler);
for (i = 0; i < N; i++) {
if ((pid[i] = fork()) == 0) {
sleep(1);
exit(0); /* Child: Exit */
}
}
while (ccount > 0)
;
}
运行结果:
与例3类似,程序会先创建好5个子进程,然后在while(ccount>0);停住,接收SIGCHLD之后调用child_handler。但是在信号处理程序中只wait了一次,所以内核也只会从系统中删除一个子进程。剩下的四个均成为了僵死进程,从child_handler返回之后程序继续停在while(ccount>0);
ctrl+z将进程挂起,ps查看运行进程,发现正是有一个父进程,和四个僵死子进程。
此时只需将父进程杀死,就可以一并回收僵死子进程:
【例5】
#define N 5
void int_handler(int sig)
{
printf("Process %d received signal %d\n", getpid(), sig); /* Unsafe */
exit(0);
}
/*
* fork13 - Simple signal handler example
*/
void fork13()
{
pid_t pid[N];
int i;
int child_status;
signal(SIGINT, int_handler);
for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0) {
/* Child: Infinite Loop */
while(1)
;
}
for (i = 0; i < N; i++) {
printf("Killing process %d\n", pid[i]);
kill(pid[i], SIGINT);
}
for (i = 0; i < N; i++) {
pid_t wpid = wait(&child_status);
if (WIFEXITED(child_status))
printf("Child %d terminated with exit status %d\n",
wpid, WEXITSTATUS(child_status));
else
printf("Child %d terminated abnormally\n", wpid);
}
}
运行结果:
程序中signal(SIGINT, int_handler);指每当进程接受到SIGINT信号就会调用*int_handler信号处理函数。
第一个循环为同一个父进程创建了5个子进程,并且让子进程们在while(1);处停住
第二个循环又在父进程中将这五个子进程杀死。每当一个子进程收到终止信号就会调用信号处理函数,打印“Process xxxx received signal 2”(SIGINT==2)
kill(pid[i], SIGINT);——将信号SIGINT传给 进程ID=pid[i]的子进程,使相应子进程终止
第三个循环检查子进程的退出状态
子进程退出会设置child_status的值
如果正常退出,WIFEXITED(child_status)返回为真
只有正常退出才会定义WEXITSTATUS这个状态
状态码(转载)