昨天學了進程控制,就這三種特殊的進程研究了一下,其中也借鑒了一些前人總計的經驗。
1、孤兒進程
如果父進程先退出,子進程還沒退出那么子進程將被 托孤給init進程,這里子進程的父進程就是init進程(1號進程).其實還是很好理解的。
// 父進程先子進程退出
// 子進程會被祖父進程接手并在后臺運行,執行內部的代碼
int main()
{pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0: // 子進程close (1);// 創建一個文件用來保存輸出的文字int fd = open ("child", O_RDWR|O_CREAT, 0777);printf ("我是子進程,我的ID是%d\n", getpid());while (1){printf ("找爸爸\n");fflush (stdout);sleep (2);}break;default: // 父進程printf ("我是父進程:ID = %d\n", getpid());printf ("我走啦\n");while (1);break;}return 0;
}
在這里我們運行了程序,可以在另一終端看到有2個a.out在運行,我們將父進程終止,子進程并沒有退出,而是在后臺繼續運行,并向child文件中輸出文字。
2、僵尸進程
如果我們了解過Linux進程狀態及轉換關系,我們應該知道進程這么多狀態中有一種狀態是僵死狀態,就是進程終止后進入僵死狀態(zombie),等待告知父進程自己終止,后才能完全消失.但是如果一個進程已經終止了,但是其父進程還沒有獲取其狀態,那么這個進程就稱之為僵尸進程.僵尸進程還會消耗一定的系統資源,并且還保留一些概要信息供父進程查詢子進程的狀態可以提供父進程想要的信息.一旦父進程得到想要的信息,僵尸進程就會結束.
// 子進程比父進程先退出
int main()
{int count = 5;while (count--){//signal(SIGCHLD,SIG_IGN);pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0: // 子進程printf ("我是子進程,我的ID是%d\n", getpid());printf ("我走啦\n");exit (0);default: // 父進程printf ("我是父進程,我的ID是%d\n", getpid());//while(1);break;}}while(1);return 0;
}
signal(SIGCHLD,SIG_IGN);加上這行代碼后僵尸進程都消失了。
通過ps -ef | grep a.out 我們可以得知進程信息和進程pid,可以看到子進程就是處于defunct狀態.這時我們肯定想要怎么才能避免僵尸進程呢?看程序被我注釋的那句signal(SIGCHLD,SIG_IGN),加上就不會出現僵尸進程了.那我們就加點篇幅講一下為什么就可以避免僵尸進程呢?
這是signal()函數的聲明sighandler_t signal(int signum, sighandler_t handler),我們可以得出,signal函數的第一個函數是linux支持的信號,第二個參數是對信號的操作 ,是系統默認還是忽略或捕獲.我們這是就可以知道signal(SIGCHLD,SIG_IGN)是選擇對子程序終止信號選擇忽略,這是僵尸進程就是交個內核自己處理,并不會產生僵尸進程.
3、守護進程
同樣我們需要了解一下什么是守護進程,守護進程就是在后臺運行,不與任何終端關聯的進程,通常情況下守護進程在系統啟動時就在運行,它們以root用戶或者其他特殊用戶(apache和postfix)運行,并能處理一些系統級的任務.習慣上守護進程的名字通常以d結尾(sshd),但這些不是必須的.
下面介紹一下創建守護進程的步驟:
· 調用fork(),創建新進程,它會是將來的守護進程.
· 在父進程中調用exit,保證子進程不是進程組長
· 調用setsid()創建新的會話區
· 將當前目錄改成跟目錄(如果把當前目錄作為守護進程的目錄,當前 目錄不能被卸載他作為守護進程的工作目錄)
· 將標準輸入,標注輸出,標準錯誤重定向到/dev/null
// 守護進程
int daemonize (int nochdir, int noclose)
{// 創建子進程,關閉父進程pid_t pid = fork();if (pid > 0) // 父進程{exit (0);}else if (pid < 0){return -1;}// 2、設置文件的掩碼,mode & ~umaskumask (0);// 3、設置新的會話:脫離當前會話和終端的控制if (setsid() < 0){return -1;}// 當nochdir為0時,daemon將更改進程的根目錄為rootif (0 == nochdir){// 改變當前的工作目錄if (chdir ("/") < 0){return -1;}}// 標準輸入、關閉標準輸出、標準錯誤close (STDIN_FILENO);close (STDOUT_FILENO);close (STDERR_FILENO);if (0 == noclose){// 重定向標準輸入、關閉標準輸出、標準錯誤open ("dev/null", O_RDONLY); // 0open ("dev/null", O_RDWR); // 1open ("dev/null", O_RDWR); // 2}return 0;
}int main()
{daemonize (0, 0);// daemon (0,0); // 系統自帶守護進程while (1);return 0;
}