1.fork函數建立一個新程序
#include <unistd.h>
pid_t fork(void);
傳回值:子程序中傳回0,父程序中傳回子程序ID,出錯傳回-1
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1)
{
perror("fork error");
}
else if(pid == 0)
{
printf("this is child process\n");
printf("child process gets parent processID by getpid() %d\n",getpid());
}
else
{
printf("this is parent process\n");
printf("parent process gets parent processID by pid %d\n",pid);
}
pause();
return 0;
}
結論:父程序的pid和子程序getpid相同
2.fork與僵屍程序
僵屍程序:簡單來記憶就是父程序沒有給子程序收屍
僵屍程序:一個程序使用fork建立子程序,如果子程序退出,而父程序并沒有調用wait或waitpid擷取子程序的狀态資訊,那麼子程序的程序描述符仍然儲存在系統中。這種程序稱之為僵死程序。
轉載部分了解:
僵屍程序的産生:
當一個程序建立了一個子程序時,他們的運作時異步的。即父程序無法預知子程序會在什麼時候結束,那麼如果父程序很繁忙來不及wait 子程序時,那麼當子程序結束時,會不會丢失子程序的結束時的狀态資訊呢?處于這種考慮unix提供了一種機制可以保證隻要父程序想知道子程序結束時的資訊,它就可以得到。(不管父程序先于還是後于子程序調用wait,系統都會告訴你子程序的資訊)
這種機制是:在每個程序退出的時候,核心釋放該程序所有的資源,包括打開的檔案,占用的記憶體。但是仍然保留了一些資訊(如程序号pid 退出狀态 運作時間等)。這些保留的資訊直到程序通過調用wait/waitpid時才會釋放。這樣就導緻了一個問題,如果沒有調用wait/waitpid的話,那麼保留的資訊就不會釋放。比如程序号就會被一直占用了。但系統所能使用的程序号的有限的,如果産生大量的僵屍程序,将導緻系統沒有可用的程序号而導緻系統不能建立程序。是以我們應該避免僵屍程序
這裡有一個需要注意的地方。如果子程序先結束而父程序後結束,即子程序結束後,父程序還在繼續運作但是并未調用wait/waitpid那子程序就會成為僵屍程序。
但如果子程序後結束,即父程序先結束了,但沒有調用wait/waitpid來等待子程序的結束,此時子程序還在運作,父程序已經結束。那麼并不會産生僵屍程序。因為每個程序結束時,系統都會掃描目前系統中運作的所有程序,看看有沒有哪個程序是剛剛結束的這個程序的子程序,如果有,就有init來接管它,成為它的父程序。
同樣的在産生僵屍程序的那種情況下,即子程序結束了但父程序還在繼續運作(并未調用wait/waitpid)這段期間,假如父程序異常終止了,那麼該子程序就會自動被init接管。那麼它就不再是僵屍程序了。應為intit會發現并釋放它所占有的資源。(當然如果程序表越大,init發現它接管僵屍程序這個過程就會變得越慢,是以在init為發現他們之前,僵屍程序依舊消耗着系統的資源)
一個僵屍程序的例子:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <signal.h>
int main(void )
{
pid_t pid;
printf("befor fork pid:%d \n", getpid());
int abc = 10;
pid = fork(); //errno
if (pid == -1)
{
perror("fork error");
return -1;
}
if (pid > 0)
{
abc ++;
printf("parent: pid:%d \n", getpid());
printf("abc: %d \n", abc);
sleep(20);//父程序沒有調用wait方法去收屍,故這20s内,子程序是僵屍程序
}
else if (pid == 0)
{
abc ++;
printf("child: %d, parent: %d \n", getpid(), getppid());
printf("abc: %d \n", abc);
}
printf("fork after....\n");
return 0;
}
在fork調用之後,子程序和父程序繼續執行fork調用之後的指令。子程序獲得父程序資料空間、堆和棧的副本,以及程序控制塊PCB,但是并不共享這些存儲空間部分。
fork的一個特性是父程序的所有打開檔案描述符都被複制到子程序中。父、子程序的每個相同的打開描述符共享一個檔案表項。
這種共享檔案的方式使父、子程序對同一檔案使用了一個檔案偏移量
這裡需要注意一下,父、子程序是共享正文段的,但是fork之後,子程序擷取到的PC(程式計數器)已經指向了fork之後的内容,是以,子程序隻執行fork之後的代碼。
fork有兩種用法:
(1)一個父程序希望複制自己,使父、子程序同時執行不同的代碼段。例如,父程序等待用戶端的服務請求,然後fork一個子程序處理這個請求,自己則繼續等待下一個服務請求。當請求達到時,父程序調用fork,子程序處理此請求。父程序繼續等待下一個服務請求。
(2)一個程序要執行一個不同的程式,這對shell是常見的情況。在這種情況下,子程序從fork傳回後立即調用exec。
fork的特殊應用:fork兩次可以避免僵死程序,父程序先fork一個子程序,子程序繼續fork一個孫子程序,然後就直接退出。這樣,父程序就可以很快的wait到子程序,釋放其資源,不需要阻塞,繼續自己的操作;子程序先于孫程序退出也不會産生僵屍程序,孫子程序交由了init程序托管,執行自己的操作而不用擔心了。
子程序成全了父程序,因為子程序的退出父程序不會阻塞在那兒,父程序成功收屍并退出;
子程序成全了孫程序,因為子程序先于孫程序退出,故孫程序托孤給init程序。
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1)
{
perror("fork error");
}
else if(pid == 0)
{
printf("this is child process\n");
printf("child process gets parent processID by getpid() %d\n",getpid());
pid = fork();
if(pid == -1)
{
perror("fork error");
}
else if(pid == 0)
{
while(1)
{
sleep(1);
printf("this is child's child process\n");
}
}
else
{
//子程序先有孫程序退出,孫程序托孤給init
exit(110);
}
}
else
{
printf("this is parent process\n");
printf("parent process gets parent processID by pid %d\n",pid);
}
//父程序等待子程序退出
wait(NULL);
return 0;
}
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status)
程序一旦調用了wait,就立即阻塞自己,由wait自動分析是否目前程序的某個子程序已經退出,如果讓它找到了這樣一個已經變成僵屍的子程序,wait就會收集這個子程序的資訊,并把它徹底銷毀後傳回;如果沒有找到這樣一個子程序,wait就會一直阻塞在這裡,直到有一個出現為止。
參數status用來儲存被收集程序退出時的一些狀态,它是一個指向int類型的指針。但如果我們對這個子程序是如何死掉的毫不在意,隻想把這個僵屍程序消滅掉,(事實上絕大多數情況下,我們都會這樣想),我們就可以設定這個參數為NULL,就象下面這樣:
pid = wait(NULL);
如果成功,wait會傳回被收集的子程序的程序ID,如果調用程序沒有子程序,調用就會失敗,此時wait傳回-1,同時errno被置為ECHILD。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
pid = fork();
int status = 0;
if (pid < 0)
{
printf("error occurred!\n");
}
else if (pid == 0)
{
printf("This is child \n");
exit(10);
}
else
{
sleep(5);
pid_t pc = wait(&status);//就算子程序在父程序前面退出了,wait依然可以收集到子程序的狀态,讓子程序不在是僵屍狀态
//5秒前子程序是僵屍程序,但是5秒後不是
//傳回的值為子程序的ID
printf("status is %d,pro = %d", WEXITSTATUS(status),pc);
sleep(20);
}
}
/*編寫一個孤兒程序,這個孤兒程序可以同時建立100個僵死程序。
* fork.c
*
* Created on: 2015年12月6日
* Author: Administrator
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int argc, char* argv[]) {
pid_t pid = fork();
if (pid == -1) {
printf("fork failed %s\n", strerror(errno));
return -1;
}
int i = 0;
if (pid == 0) {
for (i = 0; i < 100; i++) {
pid_t tmp_pid = fork();
if (tmp_pid == 0) {
exit(0);
}
}
sleep(10);//10秒後父程序退出,又父程序建立的已經僵屍的程序也會被系統回收
//exit(0);
} else if (pid > 0) {
exit(0);
}
return 0;
}