Linux進程數據結構詳解


1、Linux的進程簡介:

支持多線程的操作系統中,進程是資源分配的最小單位,線程是調度的基本單位。Linux是現代的32位或64位的支持多線程的操作系統,不過Linux是一種以輕量級進程作為線程,多線程任務中的數個線程以線程的組的方式存在,每個線程以輕量級進程實現。

Linux的輕量級進程沒有獨立的內存空間,進程有獨立的內存空間,其中內核級輕量級進程沒有內存空間,用戶級輕量級進程共享內存空間,進程有自己的mm_struct。

事實上你可以如下理解:linux下,輕量級進程就是指線程,符合POSIX標准規范。至於創建一個線程的細節,你可以參考關於clone這個函數的介紹,除了用clone實現輕量級進程,實際上fork也是調用clone來實現的。

 

2、Linux進程的描述的數據結構

Linux中的進程以及輕量級進程(以下簡稱為線程)使用同樣的描述符數據結構task_struct(可以在一定程度上理解為PCB)。

task_struct的數據成員主要有:進程狀態、內核棧信息、進程使用狀態、PID、優先級、鎖、時間片、隊列、信號量、內存管理信息、文件列表等等與進程管理、調度密切相關的信息。

詳細源碼參照:內核3.0.6_task_struct源碼.txt文件

 

 

3、Linux的進程的組織結構、狀態以及轉換

Linux中,進程的組織結構是雙向鏈表的形式,task中有prev和next的指針,其中鏈表表頭head的位置是0號進程或者說是swapper進程的task_struct。SET_LINKS 和 REMOVE_LINKS 宏分別用於從進程鏈表中插入和刪除一個進程描述符。這些宏考慮了進程間的父子關系。,

進程狀態轉換圖:

◆運行狀態(TASK_RUNNING)

當進程正在被CPU執行,或已經准備就緒隨時可由調度程序執行,則稱該進程為處於運行狀態(running)。進程可以在內核態運行,也可以在用戶態運行。當系統資源已經可用時,進程就被喚醒而進入准備運行狀態,該狀態稱為就緒態。這些狀態(圖中中間一列)在內核中表示方法相同,都被成為處於TASK_RUNNING狀態。

◆可中斷睡眠狀態(TASK_INTERRUPTIBLE)

當進程處於可中斷等待狀態時,系統不會調度該進程執行。當系統產生一個中斷或者釋放了進程正在等待的資源,或者進程收到一個信號,都可以喚醒進程轉換到就緒狀態(運行狀態)。

◆不可中斷睡眠狀態(TASK_UNINTERRUPTIBLE)

與可中斷睡眠狀態類似。但處於該狀態的進程只有被使用wake_up()函數明確喚醒時才能轉換到可運行的就緒狀態。

◆暫停狀態(TASK_STOPPED)

當進程收到信號SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU時就會進入暫停狀態。可向其發送SIGCONT信號讓進程轉換到可運行狀態。在Linux 0.11中,還未實現對該狀態的轉換處理。處於該狀態的進程將被作為進程終止來處理。

◆僵死狀態(TASK_ZOMBIE)

當進程已停止運行,但其父進程還沒有詢問其狀態時,則稱該進程處於僵死狀態。

當一個進程的運行時間片用完,系統就會使用調度程序強制切換到其它的進程去執行。另外,如果進程在內核態執行時需要等待系統的某個資源,此時該進程就會調用sleep_on()或sleep_on_interruptible()自願地放棄CPU的使用權,而讓調度程序去執行其它進程。進程則進入睡眠狀態(TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE)。

只有當進程從“內核運行態”轉移到“睡眠狀態”時,內核才會進行進程切換操作。在內核態下運行的進程不能被其它進程搶占,而且一個進程不能改變另一個進程的狀態。為了避免進程切換時造成內核數據錯誤,內核在執行臨界區代碼時會禁止一切中斷。

4、0號進程的秘密

傳說中的swapper進程,其描述符是提前設計好的,進程0創建時,系統設置了時鍾,便於進程的調度輪詢,process0,以下簡稱p0。p0具備處理系統調用,這需要通過set_system_gate和system_call(系統調用的總入口)與IDT掛接。

此外,p0還需要與task[64]、GDT、LDT、TSS掛接,並把優先級由0調整為3。Linux規定所有進程(p0除外)必須有其他進程在3特權級別下創建。因而p0需要調用move_to_user_mode()進行優先級翻轉。

優先級翻轉在響應中斷時完成,在響應中斷時,系統的各個寄存器的數據按順序壓棧(SS、ESP、EFLAGS、CS、EIP),恢復時按照反序出棧,在相應中斷過程中,set_system_gate就是設置int 0x80中斷,由3級(iret)反轉到0級(iret),同理也可以由0級(iret)翻轉到3級(iret)。注:

0級:執行系統代碼

3級:執行進程代碼

0號進程不是由3級翻轉到0級的,沒有預先的壓棧數據,所以要手工模擬壓棧,表示該屬性的ss字段的后兩位必須是11(3),總值:0x17。

 

 

5、1號與2號進程

1號進程由0號進程創建,2號進程由1好進程創建。

2號進程的執行,將意味着shell開始執行,boot過程結束,設備實現怠速。

創建進程過程:

(1)調用fork()函數:

將相應的信息壓棧:以便於調用copy_process后初始化p1的TSS。壓棧:fork函數偏移值(2)賦給eax,SS、ESP、EFLAGS、CS、EIP入棧,然后DS、ES、FS、EDX、ECX、EBX入棧。

(2)開始執行fork()

在task[64]除[0]以外的數組項初始化清空,調用下面的函數:find_empty_process()獲得一個PID和數組中的一個位置。接下來在父進程的內核中繼續壓棧,5個寄存器的值入棧,為調用copy_process()做准備。

(3)調用copy_process()

創建子進程的task_struct;

復制父進程的task_struct信息到子進程的task_struct之中;

為子進程做struct和tss的個性化設置;

創建子進程的頁表,復制父進程頁表項到子進程;

共享文件;

設置GDT,將子進程的狀態設置為就緒;

注:task_struct和內核棧共同構成task_union:正好占據一頁。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM