歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

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 isexited.\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;
}

 

  <defunct>僵屍進程   孤兒進程由init處理,並不會有什麼危害。但是僵屍進程的大量存在會占用PID等資源,可能會導致系統無法產生新的進程。任何一個子進程(init除外)在exit()之後,並非馬上就消失掉,而是留下一個稱為僵屍進程(Zombie)的數據結構,等待父進程處理。這是每個 子進程在結束時都要經過的階段。如果子進程在exit()之後,父進程沒有來得及處理,這時用ps命令就能看到子進程的狀態是“Z”。如果父進程能及時 處理,可能用ps命令就來不及看到子進程的僵屍狀態,但這並不等於子進程不經過僵屍狀態。如果父進程在子進程結束之前退出,則子進程將由init接管。init將會以父進程的身份對僵屍狀態的子進程進行處理   避免僵屍進程    通過信號機制   對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成為僵屍進程(zombie)從而占用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的並發性能。在Linux下可以簡單地將 SIGCHLD信號的操作設為SIG_IGN。   也就是說忽略SIGCHLD信號,這是常用於並發服務器的性能的一個技巧。因為並發服務器常常fork很多子進程,子進程終結之後需要服務器進程去wait清理資源。如果將此信號的處理方式設為忽略,可讓內核把僵屍子進程轉交給init進程去處理,省去了大量僵屍進程占用系統資源。(Linux Only)   測試程序如下所示:  
//示例: 避免僵屍進程
int main(int argc, char *argv[])
{
signal(SIGCHLD, SIG_IGN);
pid_t pid = fork();
if (pid < 0)
err_exit("fork error");
else if (pid == 0)
exit(0);
else
{
sleep(50);
}
exit(0);
}

 

  文件共享    父進程的所有文件描述符都被復制到子進程中, 就好像調用了dup函數, 父進程和子進程每個相同的打開文件描述符共享一個文件表項(因此, 父子進程共享同一個文件偏移量);  
//根據上圖: 理解下面這段程序和下圖的演示
int main(int argc, char *argv[])
{
signal(SIGCHLD, SIG_IGN);

int fd = open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (fd == -1)
err_exit("file open error");

cout << "We Don`t flash memory\n";

char buf[BUFSIZ];
bzero(buf, sizeof(buf));

pid_t pid = fork();
if (pid < 0)
err_exit("fork error");
else if (pid > 0)
{
strcpy(buf, "Parent...");
write(fd, buf, strlen(buf));
close(fd);
cout << "fd = " << fd << endl;
exit(0);
}
else if (pid == 0)
{
strcpy(buf, "Child...");
write(fd, buf, strlen(buf));
close(fd);
cout << "fd = " << fd << endl;
exit(0);
}
}

 

結果是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 )  
Copyright © Linux教程網 All Rights Reserved