fork函数和wait/waitpid函数

复制一个进程映象fork

使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。

子进程与父进程的区别在于:

1、父进程设置的锁,子进程不继承

2、各自的进程ID和父进程ID不同

3、子进程的未决告警被清除;

4、子进程的未决信号集设置为空集。


fork系统调用

包含头文件 <sys/types.h> <unistd.h>

函数功能:

创建一个子进程

函数原型

         pid_t  fork(void);

参数:无参数。

返回值:

如果成功创建一个子进程,对于父进程来说返回子进程ID

如果成功创建一个子进程,对于子进程来说返回值为0

如果为-1表示创建失败

 

代码示例

 

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

pid_t pid;

char *message;

int n;

pid = fork();//在这里fork调用一次,返回2次  

if (pid < 0)

{

perror("fork failed");

exit(1);

}

if (pid == 0)//子进程开始执行

{

message = "This is the child\n";

n = 6;

}

  else//父进程开始执行

{

message = "This is the parent\n";

n = 3;

}

//子进程打印了6次父进程打印了3次

for(; n > 0; n--)

{

printf(message);

sleep(1);

}

return 0;

}

 

打印的信息

./a

This is the parent

This is the child

This is the parent

This is the child

This is the parent

This is the child

This is the child

This is the child

This is the child

 


现在我们已经理解fork系统调用了,但是我们想获得该进程的id号 则需要使用下列函数

#include <sys/types.h>

#include <unistd.h>

pid_t getpid(void); //返回调用进程的PID号

pid_t getppid(void); //返回调用进程父进程的PID号

案例

#include <stdio.h>

#include <unistd.h>

 

int main()

{

pid_t pid;

printf("before fork()\n");


pid = fork();

if (pid == 0)

{//打印子进程id号

printf("this is child, pid = %d\n", getpid());

}

else if (pid > 0)

{//打印父进程id号

printf("this parent, ppid = %d\n", getppid());

}

 

return 0;

}

 

打印结果:

before fork()

this parent, ppid = 2737

this is child, pid = 4790

注意

你不能预计父进程是在它的子进程之前还是之后运行,它的执行是无序的,是异步的。

fork的异步行为意味着你不应该在子进程中执行依赖与父进程的代码,反之亦然。

fork调用可能失败,原因是系统上已经运行了太多进程,已经超过了允许它执行的最大进程数。

fork执行失败,会向父进程返回-1,而且不创建子进程。

 

Wait函数解释

我们用fork启动一个进程时,子进程就有了自己的生命,并将独立地运行。有时,我们需要知道某个子进程是否已经结束了,我们可以通过wait函数安排父进程在子进程之后结束

 

Wait函数

头文件<sys/types.h><sys/wait.h>

函数原型

pid_t wait(int *status);

函数参数

status:该参数可以获得你等待子进程的信息

返回值:

成功等待子进程函数返回等待子进程的ID

wait系统调用会使父进程暂停执行,直到它的一个子进程结束为止。返回的是子进程的PID,它通常是结束的子进程状态信息允许父进程判定子进程的退出状态,即从子进程的main函数返回的值或子进程中exit语句的退出码。如果status不是一个空指针,状态信息将被写入它指向的位置

 

Wait获取status后检测处理

宏定义 描述

WIFEXITED(status) 如果子进程正常结束,返回一个非零值

WEXITSTATUS(status) 如果WIFEXITED非零,返回子进程退出码

WIFSIGNALED(status) 子进程因为捕获信号而终止,返回非零值

WTERMSIG(status) 如果WIFSIGNALED非零,返回信号代码

WIFSTOPPED(status) 如果子进程被暂停,返回一个非零值

WSTOPSIG(status) 如果WIFSTOPPED非零,返回一个信号代码

 

案例

通过这个小例子可以捕获子进程的返回值

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

 

int main(int arg, char *args[])

{

pid_t pid = fork();

int status;

if (pid == -1)

{

printf("fork failed\n");

return 0;

}

if (pid == 0)

{

printf("child pid = %d\n", getpid());//打印子进程的pid

printf("child process start\n");

sleep(2);

printf("child process end\n");

return 11;

}

else

{

printf("parent process start\n");

pid_t child = wait(&status);//调用wait函数阻塞等待子进程结束才开始执行下面的代码

printf("child = %d\n", child);//通过wait函数的返回值打印子进程的pid

printf("status = %d\n", WEXITSTATUS(status));//通过这个宏WEXITSTATUS可以得到子进程的返回值

printf("parent process end\n");


return 0;

}

}

测试结果如下:

parent process start

child pid = 5629

child process start

child process end

child = 5629

status = 11

parent process end

 

Waitpid函数解释

函数功能:

用来等待某个特定进程的结束

函数原型:

pid_t waitpid(pid_t pid, int *status, int options)

 参数:

status:如果不是空,会把状态信息写到它指向的位置

options:允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起

返回值:如果成功返回等待子进程的ID,失败返回-1

 

对于waitpid的pid参数的解释与其值有关:

pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。

pid > 0 等待其进程I D与pid相等的子进程。

pid ==0 等待其组I D等于调用进程的组I D的任一子进程。换句话说是与调用者进程同在一个组的进程。

pid < -1 等待其组I D等于pid的绝对值的任一子进程。

 

案例

#include <sys/wait.h>

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

 

int main(int arg, char *args[])

{

pid_t pid = fork();

int status;

if (pid == -1)

{

printf("fork failed\n");

return 0;

}

if (pid == 0)

{

printf("child process start\n");

printf("child pid = %d\n", getpid());

sleep(2);

printf("child process end\n");

return 10;

}

else

{

printf("parent process start\n");

pid_t chil = waitpid(pid, &status, 0);

printf("chil = %d\n", chil);

printf("status = %d\n", WEXITSTATUS(status));

printf("parent process end\n");

return 0;

}

 

return 0;

}

测试结果如下:

parent process start

child process start

child pid = 5869

child process end

chil = 5869

status = 10

parent process end

 

waittwaitpid区别和联系

在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。

waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的特定进程。

实际上wait函数是waitpid函数的一个特例。

 

什么是僵死进程?

一个僵死进程是在父进程有机会用wait或者waitpid收集它退出状态之前就终止的子程。之所以被称为僵死进程是因为他虽然死掉了,但依然在进程表中存在。

子进程退出后分配给它的内存和其他资源都被释放,但它还是在内核进程表中保留了一条,内核在父进程回收子进程的退出状态前一直保留它。

有一两个僵死进程不算什么问题,但一旦一个程序频繁执行fork或者exec却又不能收集退出状态,那么最终将会填满进程表,这会影响性能,可能导致系统重新启动

 

什么是孤儿进程(orphan process

孤儿进程是一个父进程在调用wait或者waitpid之前就已经退出的子进程。此时init进程成为子进程的父进程。init进程为子进程的父进程收集退出状态,从而避免出现僵死进程。

 

下面这个例子可以查看到僵尸进程

#include <unistd.h>

#include <stdlib.h>

int main(void)

{

pid_t pid=fork();

if(pid<0)

{

perror("fork");

exit(1);

 }

if(pid>0)

{ //父进程一直执行死循环

while(1);

}

//子进程直接就退出了,此时就产生僵尸进程


return 0;

}

 

重新启动一个终端在命令行下:ps -ajx

2737  5964  5964  2737 pts/4     5964 R+    1000   5:30 ./a

 5964  5965  5964  2737 pts/4     5964 Z+    1000   0:00 [a] <defunct>(僵尸进程)

 

通过上面的例子我们可以查看到僵尸进程,所以我们可以通过waitwaitpid2个函数来避免产生僵尸进程。当然啦,子进程正常或者异常终止,内核就会向其父进程发送一个SIGCHLD信号,在这里就不做详细解释

 

 

 

 

 

 

 

 

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值