Linux进程实践(2) --僵尸进程与文件共享

孤儿进程与僵尸进程

孤儿进程:

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

[cpp]  view plain copy
  1. //生成孤儿进程  
  2. int main(int argc, char *argv[])  
  3. {  
  4.     pid_t pid = fork();  
  5.     if (pid < 0)  
  6.         err_exit("fork error");  
  7.     else if (pid > 0)  
  8.         exit(0);  
  9.     else  
  10.     {  
  11.         sleep(10);  
  12.         cout << "Child, ppid = " << getppid() << endl;  
  13.     }  
  14.     exit(0);  
  15. }  

僵尸进程:

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

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

-查询父子进程状态

  ps -le | grep main

 

避免僵尸进程

   signal(SIGCHLD, SIG_IGN);

[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) --僵尸进程与文件共享_第1张图片

[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. }  
Linux进程实践(2) --僵尸进程与文件共享_第2张图片

fork VS vfork

  在UNIX/Linux中的fork还没实现copy on write(写时复制)技术之前。Unix设计者很关心fork之后立刻执行exec所造成的地址空间浪费,所以引入了vfork系统调用。其中,vfork子进程与父进程共享数据段,并不真正复制父进程内存,因此在vfork之后执行exec系列函数,并不会导致地址空间浪费以及无用的空间复制时间.而且,即使fork实现了copy on write,效率也没有vfork高.

  但是,vfork有个限制,子进程必须立刻执行_exit或者exec系列函数。因此我们不推荐使用vfork,因为几乎每一个vfork的实现,都或多或少存在一定的问题(可以尝试在vfork之后的子进程中既不执行_exit,也不执行exec函数)

 

fork与vfork的区别

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

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

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

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

[cpp]  view plain copy
  1. //示例1:vfork出错情况  
  2. //在Linux 2.6内核上会持续执行,不会退出  
  3. //而在Linux 3.13内核上, 则会引发core dump  
  4. int main()  
  5. {  
  6.     int iNumber = 0;  
  7.     pid_t pid = vfork();  
  8.   
  9.     if (pid == -1)  
  10.     {  
  11.         perror("fork");  
  12.         return -1;  
  13.     }  
  14.     else if (pid > 0)  
  15.     {  
  16.         cout << "In Parent Program..." << endl;  
  17.         cout << "iNumber = " << iNumber << endl;  
  18.         cout << "pid = " << static_cast<int>(getpid());  
  19.         cout << "\t ppid = " << static_cast<int>(getppid()) << endl;  
  20.         //_exit(0);  
  21.     }  
  22.     else if (pid == 0)  
  23.     {  
  24.         iNumber ++;  
  25.         cout << "In Child Program..." << endl;  
  26.         cout << "iNumber = " << iNumber << endl;  
  27.         cout << "pid = " << static_cast<int>(getpid());  
  28.         cout << "\t ppid = " << static_cast<int>(getppid()) << endl;  
  29.         //_exit(0);  
  30.     }  
  31.   
  32.     return 0;  
  33. }  
Linux进程实践(2) --僵尸进程与文件共享_第3张图片

[cpp]  view plain copy
  1. //示例2: 父进程/子进程修改全局数据的情况  
  2. int main()  
  3. {  
  4.     int iNumber = 10;  
  5.     cout << "Before vfork, pid = " << getpid() << endl;  
  6.   
  7.     //对比fork()  
  8.     pid_t pid = vfork();  
  9.   
  10.     if (pid == -1)  
  11.         err_exit("fork");  
  12.     else if (pid > 0)  
  13.     {  
  14.         sleep(4);  
  15.         cout << "Parent, iNumber: " << iNumber << endl;  
  16.     }  
  17.     else if (pid == 0)  
  18.     {  
  19.         ++ iNumber;  
  20.         cout << "Child, iNumber = " << iNumber << endl;  
  21.         _exit(0);  
  22.     }  
  23.   
  24.     return 0;  
  25. }  
[cpp]  view plain copy
  1. //示例3:用vfork执行当前目录下的hello程序  
  2. int main()  
  3. {  
  4.     int iNumber = 0;  
  5.     pid_t pid = vfork();  
  6.   
  7.     if (pid == -1)  
  8.     {  
  9.         perror("fork");  
  10.         return -1;  
  11.     }  
  12.     else if (pid > 0)  
  13.     {  
  14.         cout << "In Parent Program..." << endl;  
  15.         cout << "iNumber = " << iNumber << endl;  
  16.         cout << "pid = " << static_cast<int>(getpid());  
  17.         cout << "\t ppid = " << static_cast<int>(getppid()) << endl;  
  18.   
  19.     }  
  20.     else if (pid == 0)  
  21.     {  
  22.         iNumber ++;  
  23.         cout << "In Child Program..." << endl;  
  24.         cout << "iNumber = " << iNumber << endl;  
  25.         cout << "pid = " << static_cast<int>(getpid());  
  26.         cout << "\t ppid = " << static_cast<int>(getppid()) << endl;  
  27.   
  28. //将自己写的程序启动起来  
  29.         execve("./hello",NULL,NULL);  
  30.         _exit(0);  
  31.     }  
  32.   
  33.     return 0;  
  34. }  
Linux进程实践(2) --僵尸进程与文件共享_第4张图片

[cpp]  view plain copy
  1. //测试4,用vfork执行系统命令  
  2. int main()  
  3. {  
  4.     int iNumber = 0;  
  5.     pid_t pid = vfork();  
  6.   
  7.     if (pid == -1)  
  8.     {  
  9.         perror("fork");  
  10.         return -1;  
  11.     }  
  12.     else if (pid > 0)  
  13.     {  
  14.         cout << "In Parent Program..." << endl;  
  15.         cout << "iNumber = " << iNumber << endl;  
  16.         cout << "pid = " << static_cast<int>(getpid());  
  17.         cout << "\t ppid = " << static_cast<int>(getppid()) << endl;  
  18.   
  19.     }  
  20.     else if (pid == 0)  
  21.     {  
  22.         iNumber ++;  
  23.         cout << "In Child Program..." << endl;  
  24.         cout << "iNumber = " << iNumber << endl;  
  25.         cout << "pid = " << static_cast<int>(getpid());  
  26.         cout << "\t ppid = " << static_cast<int>(getppid()) << endl;  
  27.   
  28. //将ls命令启动起来,注意:由于C++严格的类型转换机制,需要在字符串前加(char*)  
  29.         char *const args[] = {(char *)"/bin/ls", (char *)"-l", NULL};  
  30.         int res = execve("/bin/ls",args,NULL);  
  31.         if (res == -1)  
  32.         {  
  33.             perror("execve");  
  34.             _exit(1);  
  35.         }  
  36.         _exit(0);  
  37.     }  
  38.   
  39.     return 0;  
  40. }  
Linux进程实践(2) --僵尸进程与文件共享_第5张图片


你可能感兴趣的:(Linux进程实践(2) --僵尸进程与文件共享)