Linux的進程是怎樣創建的
Linux系統創建進程都是由已存在的進程創建的(除了0號進程),被創建的進程叫做子進程,創建子進程的進程就做父進程。這句話是不是有點熟悉,沒錯,Linux進程串起來也是一顆樹的結構。就像下面這樣:
在Linux中,為了創建一個子進程,父進程用系統調用fork來創建子進程。fork()其實就是把父進程復制了一份(子進程有自己的特性,比如標識、狀態、數據空間等;子進程和父進程共同使用程序代碼、共用時間片等)。
可以看下面這段代碼:
#include
#include
int main()
{
int p_num = 0;
int c_num = 0;
int pid = fork();
if(pid == 0) //返回的pid為0為子進程
{
c_num++;
}
else
{
p_num++; //返回的pid大于0為父進程
}
printf("p_num=%d, c_num=%d
",p_num,c_num);
printf("pid=%d
",pid);
return 0;
}
//運行結果如下所示
p_num=1, c_num=0
pid=36101
p_num=0, c_num=1
pid=0
大家看,代碼中調用了fork以后,之后的程序被執行了兩遍。子進程和父進程各自的變量互相沒有受到干擾。不過子進程和父進程執行的是相同的代碼,子進程和父進程資源占用情況如下圖所示:
大家可以看出,通過fork后,子進程并沒有和父進程獨立開,用的是相同的代碼。另外還有一個問題時,這個時候子進程的時間片是和父進程一分為二來共享的。這樣我創建子進程還有什么意義?為了徹底將父進程和子進程分離開來,就要用到一個系統調用 execv()。
看下面這段代碼:
//process.c
#include
#include
int main()
{
int pid = fork();
if(pid == 0)
{
execv("./test.o",NULL); //test.o是一個經過編譯的c語言文件,這里記得要放test.o的絕對路徑
}
printf("This is parent process
");
return 0;
}
//test.c
#include
int main()
{
printf("This is child process");
return 0;
}
//運行結果如下所示
This is parent process
This is child process
通過上面的代碼可以看出,從系統調用 execv() 后,子進程直接走自己的代碼了,沒有像前一段代碼一樣把后面的代碼執行了兩次。通過調用 execv(),子進程和父進程就基本分離開了。
結合系統繼續看Linux的進程樹是什么樣的
好了,通過上面的介紹,大家應該對進程是怎么創建的有一定的了解。想繼續學習的我們來接著上強度。
我們在 Linux 系統上通過 ps - ef 命令查看系統目前的進程:
/[root@localhost lucas]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 3 21:41 ? 00:02:38 /usr/lib/systemd/systemd --s
root 2 0 0 21:41 ? 00:00:07 [kthreadd]
root 3 2 0 21:41 ? 00:00:00 [rcu_gp]
root 4 2 0 21:41 ? 00:00:00 [rcu_par_gp]
...
rtkit 1151 1 0 21:41 ? 00:00:14 /usr/libexec/rtkit-daemon
root 1152 1 0 21:41 ? 00:00:00 /usr/sbin/ModemManager
avahi 1155 1 0 21:41 ? 00:00:06 avahi-daemon: running [linux
root 1159 1 0 21:41 ? 00:00:02 /usr/lib/systemd/systemd-mac
我來解釋上表是什么意思。
首先,每一個進程都要所屬一個用戶,UID 就是用戶的標識符(通過 root 用戶創建的進程 UID 就是 root,如果我自己創建的話就應該是我的用戶名,比如我的名字 "dabai")。
其次每一個進程都要有一個 ID 來表示這個進程,PID 就表示的是當前進程的 id。
最后,上文提到除了 0 號進程,每一個進程都是由他的父進程創建的,PPID 就表示當前進程的父進程 id。
通過 0 號進程創建 1 號進程和 2 號進程,然后通過 1 號進程去創建用戶態進程,再通過 2 號進程創建內核態進程,就生成了 Linux 進程樹。
「什么是0號進程、1號進程以及2號進程?」。
0號進程
:在內核初始化的過程中,會先通過指令 struct task_struct init_task = INIT_TASK(init_task) 創建 0 號進程。這是唯一一個沒有通過 fork 或者 kernel_thread 產生的進程。是進程列表的第一個。但是這個進程不是實際意義上的進程,類似與鏈表頭。所以雖然 0 號進程是在內核態創建的,但不能說 0 號進程是內核態的第一個進程,反而要說 2 號進程是內核態的第一個進程。
1號進程
:通過調用指令 kernel_thread(kernel_init, NULL, CLONE_FS) 從內核態切換到用戶態來創建的,1號進程是所有用戶態的祖先。
2號進程
:通過調用指令 kernel_thread(kthreadd, NULL, ClONE_FS | CLONE_FILES) 來創建,2號進程負責所有內核態的進程的調度和管理,是內核態所有進程的祖先。(注意,內核態不區分線程和進程,所以說進程和線程都可以,都是任務)
「為什么要先創建 0 號進程,而不直接創建 1 號進程?」
現在對于為什么要先創建 0 號進程而不直接創建1號和2號進程有許多討論。我認為...算了,我不認為了,一展開講這篇文章又收不了尾了,以后可以專門寫一篇文章來論述這里。簡單來說就是Linux 的第一個進程不適合是一個真進程,需要一個沒有數據之類東西的假進程。
「為什么要區分用戶態和內核態?」
因為有了多個進程,對于關鍵資源來說,就會產生爭用以及誤操作破壞資源等情況。這時就需要對資源的訪問權限進行一定的限制。x86 提供了分層的權限機制,內核態具有最高的訪問權限,而用戶態訪問核心資源時必須要切換到內核態才可以訪問。
好了,我看了下字數,這篇文章已經不少了,接下來我還會繼續去分享進程和線程的更多細節,也會根據讀者的反饋在已完成的文章上不斷完善,歡迎大家持續關注呀!
參考資料:
【1】Linux進程的創建與管理:https://blog.csdn.net/qq_38410730/article/details/81193118
【2】極客時間:《趣談Linux操作系統》
-
Linux
+關注
關注
87文章
11345瀏覽量
210399 -
PID
+關注
關注
35文章
1473瀏覽量
85825 -
代碼
+關注
關注
30文章
4827瀏覽量
69054
原文標題:Linux 的進程是怎樣創建的
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論