Linux进程理解与实践(二)僵尸&孤儿进程 和文件共享

孤儿进程与僵尸进程

孤儿进程:

   如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程。(注:任何一个进程都必须有父进程)

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

int main()
{
    pid_t pid;
    //创建一个进程
    pid = fork();
    //创建失败
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    //子进程
    if (pid == 0)
    {
        printf("I am the child process.\n");
        //输出进程ID和父进程ID
        printf("pid: %d\tppid:%d\n",getpid(),getppid());
        printf("I will sleep five seconds.\n");
        //睡眠5s,保证父进程先退出
        sleep(5);
        printf("pid: %d\tppid:%d\n",getpid(),getppid());
        printf("child process is exited.\n");
    }
    //父进程
    else
    {
        printf("I am father process.\n");
        //父进程睡眠1s,保证子进程输出进程id
        sleep(1);
        printf("father process is  exited.\n");
    }
    return 0;
}

僵尸进程:

   如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵尸进程。

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    else if (pid == 0)
    {
        printf("I am child process.I am exiting.\n");
        exit(0);
    }
    printf("I am father process.I will sleep two seconds\n");
    //等待子进程先退出
    sleep(2);
    //输出进程信息
    system("ps -o pid,ppid,state,tty,command");
    printf("father process is exiting.\n");
    return 0;
}
Linux进程理解与实践(二)僵尸&孤儿进程 和文件共享_第1张图片

<defunct>僵尸进程

孤儿进程由init处理,并不会有什么危害。但是僵尸进程的大量存在会占用PID等资源,可能会导致系统无法产生新的进程。任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。  如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理

避免僵尸进程

 通过信号机制

  对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN

  也就是说忽略SIGCHLD信号,这是常用于并发服务器的性能的一个技巧。因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。(Linux Only)

测试程序如下所示:

[cpp]  view plain copy
  1. //示例: 避免僵尸进程  
  2. int main(int argc, char *argv[])  
  3. {  
  4.     signal(SIGCHLD, SIG_IGN);  
  5.     pid_t pid = fork();  
  6.     if (pid < 0)  
  7.         err_exit("fork error");  
  8.     else if (pid == 0)  
  9.         exit(0);  
  10.     else  
  11.     {  
  12.         sleep(50);  
  13.     }  
  14.     exit(0);  
  15. }  

文件共享

   父进程的所有文件描述符都被复制到子进程中, 就好像调用了dup函数, 父进程和子进程每个相同的打开文件描述符共享一个文件表项(因此, 父子进程共享同一个文件偏移量);

Linux进程理解与实践(二)僵尸&孤儿进程 和文件共享_第2张图片

[cpp]  view plain copy
  1. //根据上图: 理解下面这段程序和下图的演示  
  2. int main(int argc, char *argv[])  
  3. {  
  4.     signal(SIGCHLD, SIG_IGN);  
  5.   
  6.     int fd = open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);  
  7.     if (fd == -1)  
  8.         err_exit("file open error");  
  9.   
  10.     cout << "We Don`t flash memory\n";  
  11.   
  12.     char buf[BUFSIZ];  
  13.     bzero(buf, sizeof(buf));  
  14.   
  15.     pid_t pid = fork();  
  16.     if (pid < 0)  
  17.         err_exit("fork error");  
  18.     else if (pid > 0)  
  19.     {  
  20.         strcpy(buf, "Parent...");  
  21.         write(fd, buf, strlen(buf));  
  22.         close(fd);  
  23.         cout << "fd = " << fd << endl;  
  24.         exit(0);  
  25.     }  
  26.     else if (pid == 0)  
  27.     {  
  28.         strcpy(buf, "Child...");  
  29.         write(fd, buf, strlen(buf));  
  30.         close(fd);  
  31.         cout << "fd = " << fd << endl;  
  32.         exit(0);  
  33.     }  
  34. }  
结果是fd(文件描述符)的值相等。这说明父子进程共享同一个文件描述符,当然文件偏移量等信息也是共享的。  

fork与vfork的区别

1. fork子进程拷贝父进程的数据段(但是现在提供了写时复制技术,只有当子进程真正需要写内存时,才复制出该内存的一段副本),因此,在父进程/子进程中对全局变量所做的修改并不会影响子进程/父进程的数据内容.

    vfork子进程与父进程共享数据段,因此父子进程对数据的更新是同步的;

2. fork父、子进程的执行次序是未知的,取决于操作系统的调度算法

    vfork:子进程先运行,父进程后运行

3. 如果创建子进程是为了调用exec执行一个新的程序的时候,就应该使用vfork,但是你在vfork后执行其它语句却是非常危险的,因为很容易和父进程产生冲突。

#include <unistd.h>
#include <stdio.h>
int main(void)
{
    pid_t pid;
    int count = 0;
    pid=vfork();
    count++;
    printf("count=%d\n",count);
    return 0;
}
在学习linux进程编程的时候遇到一个问题,就是使用vfork()函数以后本以为下面会打印出1和2,但是结果却出人意料。

打印出的结果是:

count=1
count=1
Segmentation fault

出现了段错误,经过查证得知,vfork()创建子进程成功后是严禁使用return的,只能调用exit()或者exec族的函数,否则后果不可预料,在main函数里return和exit()效果一样是有前提的:没有调用vfork。

(如果return处什么都没有也会出现段错误,结果如下

count=1
count=9
Segmentation fault

)


你可能感兴趣的:(linux,fork,僵尸进程,孤儿进程)