進程表示一個正在運行的程序實例,它是分配資源的最小單位,這種說法特別官方。
進程是一個非常重要的東西,我們運行的系統中同時跑著N個進程,這些進程都在默默的工作著,我們編寫的代碼,經過編譯、運行,也會生成一個進程。這個進程由程序代碼、數據、變量(占用著系統內存)、打開的文件(文件描述符)和環境組成。一般來說,對於Linux系統,系統會在進程之間共享程序代碼和系統函數庫,所以在任何時刻,內存中都只存在代碼的一份副本。
由於進程這個東西如此的重要,這篇文章就對Linux中的進程進行一個基本的總結。
故事還是先從創建一個進程開始。看看下面的代碼:
#include <stdio.h>#include <unistd.h>#include <stdlib.h>int main(){
pid_t pid;
char *msg;
int n;
printf("Fork program starting\n");
pid = fork(); // 創建一個進程
// 創建完成以後,父進程和子進程都從這裡開始運行
switch (pid)
{
case -1:
perror("Fork failed.");
exit(EXIT_FAILURE);
case 0:
// 當fork返回值為0時,表示這是子進程
msg = "This is the child";
n = 2;
break;
default:
// 當fork不為0和-1時,表示這是父進程
msg = "This is the parent";
n = 3;
break;
}
for (; n > 0; --n)
{
printf("%s\n", msg);
sleep(1);
}
exit(EXIT_SUCCESS);}
我們使用fork
函數創建一個子進程,fork
函數的聲明如下:
#include <unistd.h>pid_t fork(void);
fork
函數返回一個pid_t類型的值,pid_t類型就是一個int類型。當調用fork
創建進程成功以後,子進程和父進程同時都從fork
函數之後的代碼開始運行。那麼既然都從同一個地方開始運行,如何區分是子進程還是父進程呢?
Linux中是使用fork
的返回值來進行區分的,方法如下:
在上面的代碼中,我就是根據fork
的返回值來判斷是子進程還是父進程的。怎麼都覺的怪怪的。
再說一次,進程很重要,我們需要去理解,那麼對於進程的結構就不得不說了。那麼我們使用fork
創建了一個進程以後,該進程在內存內部的結構是如何的呢?我們來看看。
如上圖所示,我們在實際編碼時並沒有考慮到圖中所說的這些內容,而上圖中所展示的東西,卻是理解父進程和子進程的關鍵,關於進程的很多問題,我們也是基於上圖出發進行思考的。在後面,我會專門拿出幾個問題來分析為什麼進程內存映像如此重要。
在單個處理器上,同一時間只能有一個進程可以運行,其它進程都處於等待運行狀態。但是,我們實際的感覺是同一時刻有多個進程在“同時”進行運行,這是為什麼呢?
操作系統會分給每個進程一定的運行時間,叫做“時間片”。進程在這個“時間片”內運行,由於“時間片”非常的短,這樣就給人一種多個程序在同時運行的假象。操作系統是如何調度進程的呢?進程調度的規則有很多,比如根據優先級進行調度算法,先進先出調度算法等。
Linux內核進程調度器是根據進程的優先級來進行進程調度的。優先級高的進程運行得更為頻繁。
Linux上進程有5種狀態:
當我們使用ps -aux
命令查看進程狀態時,那些標識進程的字母又是什麼意思呢?,具體的進程狀態的字符標識如下表所示:
狀態標志
狀態描述
D
不可中斷
R
運行
S
中斷
T
停止
Z
僵死
但是,有的時候,我們還會看到一些其它的標識,例如:
狀態標志
狀態描述
W
僵死
<
高優先級進程
N
低優先級進程
L
內存鎖頁
看到上面又是中斷啊,不可中斷啊,停止啊,僵死啊,直接暈死,那麼到底如何理解這些概念呢?
當向進程發送一個SIGSTOP信號,它就會因響應該信號而進入TASK_STOPPED狀態(除非該進程本身處於TASK_UNINTERRUPTIBLE狀態而不響應信號)。(SIGSTOP與SIGKILL信號一樣,是非常強制的。不允許用戶進程通過signal系列的系統調用重新設置對應的信號處理函數。)
向進程發送一個SIGCONT信號,可以讓其從TASK_STOPPED狀態恢復到TASK_RUNNING狀態。
父進程可以通過wait系列的系統調用(如wait4、waitid)來等待某個或某些子進程的退出,並獲取它的退出信息。然後wait系列的系統調用會順便將子進程的屍體(task_struct)也釋放掉。
子進程在退出的過程中,內核會給其父進程發送一個信號,通知父進程來“收屍”。這個信號默認是SIGCHLD,但是在通過clone系統調用創建子進程時,可以設置這個信號。
通過下面的代碼能夠制造一個EXIT_ZOMBIE狀態的進程:
if (fork()) while(1) sleep(100);
使用ps -aux
就會看到如下一條僵死進程的信息。
只要父進程不退出,這個僵屍狀態的子進程就一直存在。那麼如果父進程退出了呢,誰又來給子進程“收屍”?
當進程退出的時候,會將它的所有子進程都托管給別的進程(使之成為別的進程的子進程)。托管給誰呢?可能是退出進程所在進程組的下一個進程(如果存在的話),或者是1號進程。所以每個進程、每時每刻都有父進程存在。除非它是1號進程。