1. 关于fork

fork()函数:

   用于创建一个进程,所创建的进程复制父进程的代码段/数据段/BSS段/堆/栈等所有用户空间信息;在内核中操作系统重新为其申请了一个PCB,并使用父进程的PCB进行初始化;

  1. #include <iostream>
  2. #include <unistd.h>
  3. using namespace std;
  4. int val = 10;
  5. int main(int argc, char *argv[])
  6. {
  7. pid_t pid;
  8. int lval = 20;

  9. pid = fork();

  10. if(pid == 0){
  11. val += 2;
  12. lval += 5;
  13. }else{
  14. val -= 2;
  15. lval += 5;
  16. }

  17. if(pid == 0){
  18. cout << "val:" << val << ", lval = " << lval << endl;
  19. }else{
  20. cout << "val:" << val << ", lval = " << lval << endl;
  21. }
  22. return 0;
  23. }

对于父进程而言,fork()函数返回子进程的ID(子进程的PID);而对于子进程而言,fork函数返回0。

僵尸进程

  父进程创建子进程后,子进程运行到终止时刻(例如,调用exit()函数,或者运行到main中的return语句时,都会将返回的值传递给 操作系统),此时如果父进程还在运行,子进程并不会立即被销毁,直到这些值传到了产生该子进程的父进程。也就是说,如果父进程没有主动要求获得子进程的结束状态值,操作系统就会一直保存该进程的相关信息,并让子进程长时间处于僵尸状态,例如下面程序:

  1. int main(){
  2. pid_t pid = fork();
  3. if(pid == 0){
  4. cout << "I am a Child Process." <<endl;
  5. }else{
  6. cout << "I am a Father Process and Child Process is " << pid << endl;
  7. sleep(30); //让父进程休眠30秒,此时便于观察子进程的状态
  8. }
  9. if(pid == 0){
  10. cout << " Child Process exits " << endl;
  11. }else{
  12. cout << "Father Process exits " << endl;
  13. }
  14. return 0;
  15. }

此时,运行该程序,查看后台进程可知(test16是该测试程序的名称,defunct表示僵尸进程):

  1. gqx@gqx-Lenovo-Product:~$ ps -au
  2. USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
  3. root 0.6 0.9 tty7 Ssl+ 409 : /usr/lib/xorg/
  4. root 0.0 0.0 tty1 Ss+ 409 : /sbin/agetty -
  5. ...
  6. gqx 0.0 0.0 pts/ Z+ : : [tes16] <defunct>
  7. gqx 0.0 0.0 pts/ R+ : : ps -au

僵尸进程的消除

方法一:调用wait()函数:

  1. /* Wait for a child to die. When one does, put its status in *STAT_LOC
  2. and return its process ID. For errors, return (pid_t) -1.

  3. This function is a cancellation point and therefore not marked with
  4. __THROW. */
  5. extern __pid_t wait (__WAIT_STATUS __stat_loc);

成功返回终止的进程ID,失败返回-1;子进程的最终返回值将指向该函数参数所指向的内存空间,但函数所指向的内存单元总还含有其他的信息,需要使用宏进行分离。

  1. # define WIFEXITED(status) __WIFEXITED (__WAIT_INT (status)) //子进程正常终止返回"true"
  2. # define WEXITSTATUS(status) __WEXITSTATUS (__WAIT_INT (status)) //返回子进程的返回值

要注意的是:如果没有已终止的子进程,那么程序将被阻塞,直到有子进程终止。

方法二:调用waitpid()函数

  1. /* Wait for a child matching PID to die.
  2. If PID is greater than 0, match any process whose process ID is PID.
  3. If PID is (pid_t) -1, match any process.
  4. If PID is (pid_t) 0, match any process with the
  5. same process group as the current process.
  6. If PID is less than -1, match any process whose
  7. process group is the absolute value of PID.
  8. If the WNOHANG bit is set in OPTIONS, and that child
  9. is not already dead, return (pid_t) 0. If successful,
  10. return PID and store the dead child's status in STAT_LOC.
  11. Return (pid_t) -1 for errors. If the WUNTRACED bit is
  12. set in OPTIONS, return status for stopped children; otherwise don't.

  13. This function is a cancellation point and therefore not marked with
  14. __THROW. */
  15. extern __pid_t waitpid (__pid_t __pid, int *__stat_loc, int __options);

第一个参数:如果__pid的值是-1,则与wait()函数相同,可以等待任意的子程序终止;如果是0,则等待进程组识别码与目前进程相同的任何子进程;如果pid>0,则等待任何子进程识别码为 pid 的子进程。

第二个参数:与前一个函数wait()的参数意义相同。

第三个参数:常用WNOHANG——若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。

示例程序如下:

  1. #include <iostream>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <sys/wait.h>
  6. using namespace std;

  7. void read_childproc(int sig){
  8. int status;
  9. pid_t id = waitpid(-1, &status, WNOHANG);
  10. if(WIFEXITED(status)){
  11. printf("Remove proc id: %d \n", id);
  12. printf("Child send: %d \n", WEXITSTATUS(status));
  13. }
  14. }

  15. int main(){
  16. pid_t pid;
  17. struct sigaction act;
  18. act.sa_handler = read_childproc;
  19. sigemptyset(&act.sa_mask);
  20. act.sa_flags = 0;
  21. sigaction(SIGCHLD, &act, 0);

  22. pid = fork();

  23. if(pid == 0){
  24. puts("Hi, I am a child process!");
  25. sleep(6);
  26. return 12;
  27. }else{
  28. printf("Child proc id: %d \n", pid);
  29. pid = fork();
  30. if(pid == 0){
  31. puts("Hi, I am a child process!");
  32. sleep(13);
  33. exit(24);
  34. }else{
  35. int i;
  36. printf("Child proc id: %d \n", pid);
  37. for(i = 0; i < 4; i++){
  38. puts("wait...");
  39. sleep(5);
  40. }
  41. }
  42. }
  43. return 0;
  44. }

  

fork和僵尸进程的更多相关文章

  1. fork()和僵尸进程

    2018-01-03@望京 关于fork()函数,Unix/Linux提供的fork()系统调用,fork()一次返回两次, 操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在 ...

  2. 为什么fork()2次会避免产生僵尸进程

    什么是僵尸进程:用fork()创建子进程后,子进程已终止但父进程没有对它进行善后处理,那么子进程的进程描述符就一直保存在内存中,子进程就是僵尸进程. 怎么产生僵尸进程: 1.父进程没有SIGCHLD信 ...

  3. 2次使用fork避免产生僵尸进程和不去处理SIGCHLD信号

    1.如下代码所示 #include <unistd.h> #include <sys/types.h> #include <unistd.h> int main(i ...

  4. 【转】Linux杀死fork产生的子进程的僵尸进程defunct

    僵尸进程 就是 已经结束,但是还没有清理出去的.用kill -9 $PID 也无法杀死. 所以程序中应该避免出现僵尸进程. 用fork之后,父进程如果没有wait /waitpid 等待子进程的话,子 ...

  5. 为何要fork()两次来避免产生僵尸进程??

    最近安装书上说的,开始搞多进程了..看到了一个好帖子,学习学习 http://blog.sina.com.cn/s/blog_9f1496990100y420.html 首先我们要明白,为什么要避免僵 ...

  6. 为何要fork()两次来避免产生僵尸进程?

    为何要fork()两次来避免产生僵尸进程?   当我们只fork()一次后,存在父进程和子进程.这时有两种方法来避免产生僵尸进程: 父进程调用waitpid()等函数来接收子进程退出状态. 父进程先结 ...

  7. 1.1 Linux中的进程 --fork、孤儿进程、僵尸进程、文件共享分析

    操作系统经典的三态如下: 1.就绪态 2.等待(阻塞) 3.运行态 其转换状态如下图所示: 操作系统内核中会维护多个队列,将不同状态的进程加入到不同的队列中,其中撤销是进程运行结束后,由内核收回. 以 ...

  8. linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程

    本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生)      使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...

  9. UNIX高级环境编程(9)进程控制(Process Control)- fork,vfork,僵尸进程,wait和waitpid

    本章包含内容有: 创建新进程 程序执行(program execution) 进程终止(process termination) 进程的各种ID   1 进程标识符(Process Identifie ...

随机推荐

  1. C#并发集合

    并发集合   并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组 ...

  2. Windows 下 MySQL-python 的安装

    1. 标准方式 进入终端: > pip install MySQL-python 第一次安装(windows 下安装),可能会出错:缺少 vs 编译器,提示点击如下网站 Download Mic ...

  3. CentOS(一) 最小化安装

    /etc/sysconfig/selinux 关闭selinux /etc/sysconfig/network-scripts/网卡   设置onboot=yes service network re ...

  4. 倒计时的CountDownTimer

    直接看这里吧,我仅仅是搬运工.  定时运行在一段时候后停止的倒计时,在倒计时运行过程中会在固定间隔时间得到通知(译者:触发onTick方法),以下的样例显示在一个文本框中显示一个30s倒计时: , 1 ...

  5. 新版本MenuDemo——使用Duilib模拟Windows本机菜单

    相信玩Duilib朋友已经开始期待一个很长的文章.由于我的文章在一周前公布--"无焦点窗体的实现"里面提到了无焦点窗体在菜单里面的应用,并承诺大家,写一个关于Menu实现的Demo ...

  6. 线程池;java的线程池的实现原理;适用于频繁互动(如电商网站)

    线程池是一种多线程处理形式,处理过程中将任务加入到队列,然后在创建线程后自己主动启动这些任务.线程池线程都是后台线程.每一个线程都使用默认的堆栈大小,以默认的优先级执行.并处于多线程单元中. 假设某个 ...

  7. Qt程序调试之Q_ASSERT断言(条件为真则跳过,否则直接异常+崩溃)

    在使用Qt开发大型软件时,难免要调试程序,以确保程序内的运算结果符合我们的预期.在不符合预期结果时,就直接将程序断下,以便我们修改. 这就用到了Qt中的调试断言 - Q_ASSERT. 用一个小例子来 ...

  8. OpenGL(十八) 顶点数组和抗锯齿(反走样)设置

    顶点数组函数可以在一个数组里包含大量的与顶点相关的数据,并且可以减少函数的调用.使用顶点数组需要先启用顶点数组功能,使用glEnableClientState函数启用顶点数组,参数可以是GL_VERT ...

  9. [bug系列]Method not found: 'Void Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory

    bug由来 最近开始学习NetCore,想通过实战使用NetCore做一个集成数据库存储Redis缓存的WebApi项目,由于MSSQL的庞大体积,最终决定使用轻量级关系型数据库MySql. 所以最终 ...

  10. Java Socket 爬虫

    # 地址 https://github.com/mofadeyunduo/crawler # 前言 1.代码不断优化更新. 2.有建议请留言. # 介绍 1.多线程,基于 ExcutorServcie ...