1. 程式人生 > >進程基本概念理解

進程基本概念理解

進程概念

一、進程和程序

1.進程的基本概念

所謂進程是由正文段用戶數據段以及系統數據段共同組成的一個執行環境,是一個動態實體。

2.程序的基本概念

程序只是一個普通文件,是一個機器代碼指令和數據的集合,這些指令和數據存儲在磁盤上的一個可執行映像中,所以,程序是一個靜態的實體。

技術分享

3.進程的組成部分

(1)正文段:存放被執行的機器指令。這個段是只讀的(所以,在這裏不能寫自己能修改的代碼),它允許系統中正在運行的兩個或多個進程之間能夠共享這一代碼。

(2)用戶數據段:存放進程在執行時直接進行操作的所有數據,包括進程使用的全部變量在內。顯然,這裏包含的信息可以被改變。雖然進程之間可以共享正文段,但是每個進程需要有它自己的專用用戶數據段。

(3)系統數據段:該段有效地存放程序運行的環境。事實上,這正是程序和進程的區別所在。作為動態事物,進程是正文段、用戶數據段和系統數據段的信息的交叉綜合體,其中系統數據段是進程實體最重要的一部分,之所以說它有效地存放程序運行的環境,是因為這一部分存放有進程的控制信息。Linux為每個進程建立了task_struct數據結構來容納這些控制信息。

4.進程和程序總結

程序裝入內存後就可以運行了:在指令指針寄存器的控制下,不斷地將指令取至CPU運行。這些指令控制的對象不外乎各種存儲器(內存、外存和各種CPU寄存器等),這些存儲器中保存有待運行的指令和待處理的數據,當然,指令只有到CPU才能發揮其作用。

Linux是一個多任務操作系統,也就是說,可以有多個程序同時裝入內存並運行,操作系統為每個程序建立一個運行環境即創建進程,每個進程擁有自己的虛擬地址空間,它們之間互不幹擾,即使要相互作用(例如多個進程合作完成某個工作),也要通過內核提供的進程間通信機制(IPC)

二、task_struct結構描述

1.Linux中的每個進程由一個task_struct數據結構來描述,task_struct其實就是通常所說的“進程控制塊”即PCB。task_struct容納了一個進程的所有信息,是系統對進程進行控制的唯一手段,也是最有效的手段。

2.task_struct數據結構所有域如下:

*進程狀態(State);

技術分享

僵死狀態:進程雖然已經終止,但由於某種原因,父進程還沒有執行wait()系統調用,終止進程的
信息也還沒有回收。

*進程調度信息(Scheduling Information);


技術分享

技術分享

調度策略

只有root用戶能通過sched_setscheduler()系統調用來改變調度策略。

*各種標識符(Identifiers);

技術分享

*進程通信有關信息(IPC,Inter_Process Communication);

Linux 支持多種不同形式的通信機制。它支持典型的UNIX通信機制(IPC Mechanisms):信號、管道,也支持System V通信機制:共享內存、信號量和消息隊列。

技術分享

*時間和定時器信息(Times and Timers);

一個進程從創建到終止叫做該進程的生存期(lifetime)。進程在其生存期內使用 CPU的時間,內核都要進行記錄,以便進行統計、計費等有關操作。進程耗費CPU的時間由兩部分組成:一是在用戶模式(或稱為用戶態)下耗費的時間、一是在系統模式(或稱為系統態)下耗費的時間。每個時鐘滴答,也就是每個時鐘中斷,內核都要更新當前進程耗費CPU的時間信息。

技術分享

建立了“時間”的概念,“定時”就是輕而易舉的了,無非是判斷系統時間是否到達某個時刻,然後執行相關的操作而已。Linux提供了許多種定時方式,用戶可以靈活使用這些方式來為自己的程序定時。

技術分享

*進程鏈接信息(Links);

程序創建的進程具有父/子關系。因為一個進程能創建幾個子進程,而子進程之間有兄弟關系,在 task_struct結構中有幾個域來表示這種關系。

在Linux系統中,除了初始化進程init,其他進程都有一個父進程或稱為雙親進程。 可以通過 fork()或 clone()系統調用來創建子進程, 除了進程標識符(PID)等要的信息外,子進程的task_struct結構中的絕大部分的信息都是從父進程中拷貝,或說“克隆”過來的。系統有必要記錄這種“親屬”關系,使進程之間的協作更加方便,例如父進程給子進程發送殺死(kill)信號、父子進程通信等,就可以用這種關系很方便地實現。

技術分享

*文件系統信息(File System);

進程可以打開或關閉文件,文件屬於系統資源,Linux內核要對進程使用文件的情況進行記錄。task_struct結構中有兩個數據結構用於描述進程與文件相關的信息。其中,fs_struct中描述了兩個 VFS索引節點,這兩個索引節點叫做root和pwd,分別指向進程的可執行映像所對應的根目錄和當前目錄或工作目錄。file_struct結構用來記錄了進程打開的文件的描述符。

在文件系統中,每個VFS索引節點唯一描述一個文件或目錄,同時該節點也是向更低層的文件系統提供的統一的接口。

技術分享

*虛擬內存信息(Virtual Memory);
*頁面管理信息(page);
*對稱多處理器(SMP)信息;
*和處理器相關的環境(上下文)信息(Processor Specific Context);

*其他信息。

三、task_struct 結構在內存中的存放

內核棧:每個進程都有自己的內核棧。當進程從用戶態進入內核態時,CPU就自動地設置該進程的內核棧。

技術分享

從這個結構可以看出,內核棧占8KB 的內存區。實際上,進程的task_struct結構所占的內存是由內核動態分配的,更確切地說,內核根本不給task_struct分配內存,而僅僅給內核棧分配8KB的內存,並把其中的一部分給task_struct使用。

task_struct結構大約占1K字節左右,其具體數字與內核版本有關,因為不同的版本其域稍有不同。因此,內核棧的大小不能超過7KB,否則,內核棧會覆蓋task_struct結構,從而導致內核崩潰。不過,7KB大小對內核棧已足夠。

把task_struct結構與內核棧放在一起具有以下好處:
*內核可以方便而快速地找到這個結構,用偽代碼描述如下:
task_struct = (struct task_struct *) STACK_POINTER & 0xffffe000(8K)
*避免在創建進程時動態分配額外的內存。
*task_struct 結構的起始地址總是開始於頁大小(PAGE_SIZE)的邊界。

四、進程的組織方式

1.哈希表

哈希表是進行快速查找的一種有效的組織方式。Linux在進程中引入的哈希表叫做pidhash,在include/linux/sched.h 中定義。

2.雙向循環鏈表

哈希表的主要作用是根據進程的pid可以快速地找到對應的進程,但它沒有反映進程創建的順序,也無法反映進程之間的親屬關系,因此引入雙向循環鏈表。每個進程task_struct結構中的prev_task和next_task域用來實現這種鏈表。

3.運行隊列

當內核要尋找一個新的進程在CPU上運行時,必須只考慮處於可運行狀態的進程(即在TASK_RUNNING 狀態的進程) ,因為掃描整個進程鏈表是相當低效的,所以引入了可運行狀態進程的雙向循環鏈表,也叫運行隊列(runqueue)。

4.進程的運行隊列鏈表

該隊列通過task_struct結構中的兩個指針run_list鏈表來維持。隊列的標誌有兩個:一個是“空進程”idle_task,一個是隊列的長度。

空進程是個比較特殊的進程,只有系統中沒有進程可運行時它才會被執行,Linux將它看作運行隊列的頭,當調度程序遍歷運行隊列,是從idle_task開始、至idle_task結束的,在調度程序運行過程中,允許隊列中加入新出現的可運行進程,新出現的可運行進程插入到隊尾,這樣的好處是不會影響到調度程序所要遍歷的隊列成員,可見,idle_task是運行隊列很重要的標誌。另一個重要標誌是隊列長度,也就是系統中處於可運行狀態(TASK_RUNNING)的進程數目,用全局整型變量nr_running表示,在/kernel/fork.c 中定義。

5.等待隊列

顧名思義,就是等待執行的進程所組成的隊列。等待隊列在內核中有很多用途,尤其對中斷處理、進程同步及定時用處更大。

五、內核線程

內核線程或叫守護進程,在操作系統中占據相當大的比例,你可以用“ps”命令查看系統中的進程,這時會發現很多以“d”結尾的進程名,這些進程就是內核線程。內核線程是由kernel_thread ( )函數在內核態下創建。

內核線程也可以叫內核任務,它們周期性地執行,例如,磁盤高速緩存的刷新,網絡連接的維護,頁面的換入換出等。在Linux中,內核線程與普通進程有一些本質的區別,從以下幾個方面可以看出二者之間的差異。
*內核線程執行的是內核中的函數,而普通進程只有通過系統調用才能執行內核中的函數。
*內核線程只運行在內核態,而普通進程既可以運行在用戶態,也可以運行在內核態。
*因為內核線程指只運行在內核態,因此,它只能使用大於PAGE_OFFSET(3G)的地址空間。另一方面,不管在用戶態還是內核態,普通進程可以使用4GB的地址空間。

由於kernel_thread包含大部分匯編代碼,大致和下面代碼等價:

int kernel_thread(int(*fn)(void *),void * arg,unsigned long flags)
{
    pid_t p ;
    p = clone(0, flags | CLONE_VM);
    if(p) /* parent */
    return p;
    else{ /* child */
    fn(arg) ;
    exit( ) ;
    }
}

六、進程的權能

Linux 用“權能(capability)”表示一進程所具有的權力。一種權能僅僅是一個標誌,它表明是否允許進程執行一個特定的操作或一組特定的操作。這個模型不同於傳統的“超級用戶對普通戶”模型,在後一種模型中,一個進程要麽能做任何事情,要麽什麽也不能做,這取決於它的有效 UID。

任何時候,每個進程只需要有限種權能,這是其主要優勢。因此,即使一位有惡意的用戶使用有潛在錯誤程序,他也只能非法地執行有限個操作類型。

具體權能列表在博客:http://blog.csdn.net/tq08g2z/article/details/77311787?locationNum=3&fps=1


本文出自 “三天不讀書,智商不如豬!” 博客,謝絕轉載!

進程基本概念理解