1、粉絲提問
fork出的進程的父進程是從哪來的?linux
粉絲提問,一口君必須知足web
2、解答
這個問題看上去很簡單,可是要想把進程的父進程相關的全部知識點搞清楚,仍是有點難度的,下面咱們稍微拓展下,分幾點來說解這個知識點。算法
1. 如何查看進程ID
每一個linux進程都必定有一個惟一的數字標識符,稱爲進程ID(process ID),進程ID老是一非負整數,它的父進程叫PPID。bash
查看進程ID命令:微信
ps -ef
也可使用函數來得到進程ID:多線程
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); 返回:調用進程的進程ID
pid_t getppid(void); 返回:調用進程的父進程ID
2. 第一個進程init
Linux內核啓動以後,會建立第一個用戶級進程init,由上圖可知, init 進程 (pid=1) 是除了 idle 進程 (pid=0,也就是 init_task) 以外另外一個比較特殊的進程,它是 Linux 內核開始創建起進程概念時第一個經過 kernel_thread 產生的進程,其開始在內核態執行,而後經過一個系統調用,開始執行用戶空間的 / sbin/init 程序。app
3. fork函數
建立一個進程很簡單,先來認識一下fork函數:編輯器
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
返回:子進程中爲0,父進程中爲子進程I D,出錯爲-1
由fork建立的新進程被稱爲子進程( child process)。該函數被調用一次,但返回兩次,兩次返回的區別是子進程的返回值是0,而父進程的返回值則是子進程的進程ID。函數
通常來講,在f o r k以後是父進程先執行仍是子進程先執行是不肯定的。這取決於內核所使用的調度算法。學習
「舉例:」
#include <unistd.h>
int main()
{
pid_t pid;
if ((pid = fork()) == -1) {
perror("fork");
return -1;
} else if (pid == 0) {
/* this is child process */
printf("The return value is %d In child process!! My PID is %d, My PPID is %d\n", pid,getpid(), getppid());
} else {
/* this is parent */
printf("The return value is %d In parent process!! My PID is %d, My PPID is %d\n", pid,getpid(), getppid());
}
return 0;
}
執行結果:
由上圖可知,fork被調用了一次,返回了兩次。
【拓展】
使用fork函數獲得的子進程是父進程的處繼承了整個進程的地址空間,包括:「進程上下文、進程堆棧、內存信息、打開的文件描述符、信號控制設置、進程優先級、進程組號、當前工做目錄、根目錄、資源限制、控制終端」等。
fork出的子進程會集成父進程的文件描述符,若是讀寫文件,父子進程之間會互相影響。
4. ./run 運行的程序父進程是誰?
咱們來編寫一個例子:
int main(int argc, const char *argv[])
{
while(1);
return 0;
}
編譯執行
gcc test.c
./a.out
例子很簡單,就是建立一個死循環的進程。
ps -ef 查看進程ID
由上圖可知,a.out進程的進程ID是2991,父進程ID是2675,即進程bash:
bash的父進程是gnome-terminal,因此你們應該明白,咱們打開1個Linux終端,其實就是啓動了1個gnome-terminal進程。咱們在這個終端上執行./a.out其實就是利用gnome-terminal的子進程bash經過execve()將建立的子進程裝入a.out:
5. 進程和終端的關係
關於進程和終端的關係能夠參考如下文章:
《進程組、會話、控制終端概念,如何建立守護進程?》
6. 父進程死了,子進程怎麼辦?
1) 殭屍進程
如上圖所示,
-
父進程Process A建立子進程Process B,當子進程退出時會給父進程發送信號SIGCHLD; -
若是父進程沒有調用wait等待子進程結束,退出狀態丟失,轉換成僵死狀態,子進程會變成一個殭屍進程。
當父進程調用wait,殭屍子進程的結束狀態被提取出來,子進程被刪除,而且wait函數馬上返回。
實例
#include <sys/types.h>
#include <unistd.h>
/* create a ZOMBIE
* ps -ax | grep a.out to show the zombie
*/
int main()
{
if(fork()) {
//父進程
while(1){
sleep(1);
}
}
//子進程
}
上述程序會保證父進程不退出,一直在while(1)中無限循環,而子進程會馬上退出。
由上圖可知,父進程是3096,子進程是3097,子進程由於退出後父進程沒有調用wait回收子進程資源,因此子進程3097變成殭屍進程defunct。
ps -ax | grep a.out 查看進程狀態
2) 孤兒進程
若是父進程退出,而且沒有調用wait函數,它的子進程就變成孤兒進程,會被一個特殊進程繼承,這就是init進程,init 進程會自動清理全部它繼承的殭屍進程。
實例代碼:
#include <sys/types.h>
#include <unistd.h>
int main()
{
if(fork()) {
//父進程
}else{
//子進程
while(1){
sleep(1);
}
}
}
上述程序會保證子進程不退出,一直在while(1)中無限循環,而父進程會馬上退出。
孤兒進程:
./a.out的父進程ID變成1,因此該子進程被init進程繼承。
3、其餘啓動進程的方法
1. exec族函數
-
fork函數用於建立一個子進程,該子進程幾乎拷貝了父進程的所有內容。 -
exec函數族提供了一種在進程中啓動另外一個程序執行的方法。它能夠根據指定的文件名或目錄名找到可執行文件,並用它來取代原調用進程的數據段、代碼段和堆棧段。在執行完以後,原調用進程的內容除了進程號外,其餘所有都被替換了。 -
可執行文件既能夠是二進制文件,也能夠是任何Linux下可執行的腳本文件。
每當進程調用一種exec函數時,該進程徹底由新程序代換,而新程序從main函數開始執行。Exec並不建立新進程,因此先後進程ID也不會變。Exec只是用另外一個新程序替換了當前進程的正文、數據、堆和棧段。
「什麼時候使用?」
-
當進程認爲本身不能再爲系統和用戶作出任何貢獻了時就能夠調用exec函數,讓本身執行新的程序 -
若是某個進程想同時執行另外一個程序,它就能夠調用fork函數建立子進程,而後在子進程中調用任何一個exec函數。這樣看起來就好像經過執行應用程序而產生了一個新進程同樣。
「函數原型」
2. cron命令
在Linux系統中,計劃任務通常是由cron承擔,咱們能夠把cron設置爲開機時自動啓動。cron啓動後,它會讀取它的全部配置文件(全局性配置文件/etc/crontab,以及每一個用戶的計劃任務配置文件),而後cron會根據命令和執行時間來按時來調用度工做任務。
檢查cron是否安裝:
ps -ef | grep cron
crontab -u //設定某個用戶的cron服務,通常root用戶在執行這個命令的時候須要此參數
crontab -l //列出某個用戶cron服務的詳細內容
crontab -r //刪除某個用戶的cron服務
crontab -e //編輯某個用戶的cron服務
root查看本身的cron設置:
crontab -u root -l
或者直接看本身名下的任務:
crontab -l
建立任務:
crontab -e
打開默認編輯器編輯後保存退出便可
編輯基本格式 :
*****command
分 時 日 月 周 命令
第1列表示分鐘1~59 每分鐘用*或者 */1表示
第2列表示小時1~23(0表示0點)
第3列表示日期1~31
第4列表示月份1~12
第5列標識號星期0~6(0表示星期天)
第6列要運行的命令
若是寫爲*, 表示每X
若是想定義間隔,在X後加"/"和間隔的數字
每隔一分鐘打印一下系統時間
*/1 * * * * date >> ~/t.log //>> means append
3. at
在linux系統若是你想要讓本身設計的備份程序能夠自動在某個時間點開始在系統底下運行,而不須要手動來啓動它,又該如何處置呢?
這些例行的工做可能又分爲一次性定時工做與循環定時工做,在系統內又是哪些服務在負責?
還有,若是你想要每一年在老婆的生日前一天就發出一封信件提醒本身不要忘記,linux系統下該怎麼作呢?
可是crontab 主要用來提交不斷循環執行的job, 而at 用來提交一段時間後執行的job(執行完就自動刪除整個job)
「舉例:」
1) 首先檢查atd服務有無開啓 在一個指定的時間執行一個指定任務,只能執行一次,且須要開啓atd進程
ps -ef | grep atd查看,
開啓用/etc/init.d/atd start or restart;
開機即啓動則須要運行 chkconfig --level 2345 atd on
2) 定時在11:30am用ls列出當前目錄內容並寫入~/log文件
cd ~
at 11:30am today
at>ls > ~/t.log
at> <EOT> //按Ctl-D退出
其餘網友提問彙總
推薦閱讀
【5】 4. 從0開始學ARM-ARM彙編指令其實很簡單
進羣,請加一口君我的微信,帶你嵌入式入門進階。
點擊「閱讀原文」查看更多分享,歡迎點分享、收藏、點贊、在看。
本文分享自微信公衆號 - 一口Linux(yikoulinux)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。