1. 程式人生 > >分析linus進程模型

分析linus進程模型

事件 而不是 原型 不同類 context 當前 兩種 signed 麻煩

1.進程與線

1.0什麽是進程

?計算機上所有可運行的軟件,通常也包括操作系統,被組織成若幹順序進程,簡稱進程。

?一個進程就是一個正在執行程序的實例,包括程序計數器,寄存器和變量的當前值。從概念上說,每個進程都擁有它自己的虛擬cpu.

1.1什麽是線程

?在傳統操作系統中,每個進程有一個地址空間和一個控制線程

?線程從體量上看表現為輕量級的進程

1.2進程與線程的關系

?從需求上看,在許多應用中同時發生著多種活動。其中某些活動隨著時間的推移會被堵塞。通過將這些應用程序分解成可以準並行運行的多個順序線程,程序設計模型會變得更簡單。

?從實現上因為線程比進程更加輕量所以創建簡單,撤除也很快速。

2.0linus對進程的定義

2.0 linus中進程的表示

?在linux系統中,進程由一個叫task_struct的結構體描述,也就是說linux中的每個進程對應一個task_struct結構體。該結構體記錄了進程的一切。

struct task_struct
 

{
 

 //這個是進程的運行狀態,-1代表不可運行,0代表可運行,>0代表已經停止。
 

    volatile long state;
  /*        flags是進程當前的狀態標誌,具體如下:
 

            0x0000 0002表示進程正在被創建
 

            0x0000  0004表示進程正準備退出
 

            0x0000  0040表示此進程被fork出,但是並沒有執行exec
 

            0x0000   0400表示此進程由於其他進程發送相關信號而被殺死
 

    
*/ unsigned int flags; //表示此進程的運行優先級 unsigned int rt_priority //該結構體記錄了進程內存使用的相關情況 struct mm_struct *mm; //進程號,是進程的唯一標識 pid_t pid; //進程組號 pid_t tgid; //real_parent是該進程的"親生父親",不管其是否被"寄養" struct task_struct *real_parent;
//parent是該進程現在的父進程,有可能是"繼父" struct task_struct *parent; //這裏children指的是該進程孩子的鏈表,可以得到所有孩子的進程描述符 struct list_head children; //同理,sibling該進程兄弟的鏈表,也就是其父進程的所有孩子的鏈表 struct list_head sibling; //這個是主線程的進程描述符,也許你會奇怪,為什麽線程用進程描敘符表示,因為linux並沒有單獨實現線程的相關結構體,只用一 個進程來代替線程,然後對其做一些特殊的處理。 struct task_struct *group_leader; //這個是該進程所有線程的鏈表 struct list_head thread_group; //這個是該進程使用cpu時間的信息,utime是在用戶態下執行的時間,stime 是在內核態下執行的時間 cputime_t utime,stime; //comm是保存該進程名字的字符數組,長度最長為15,因為TASK_COMM_LEN為16 char comm[TASK_COMM_LEN]; //打開的文件相關信息結構體 struct files_struct *files; //信號相關信息的句柄 struct signal_struct *signal; struct sigband_struct *sighand; };

?進程號(pid),就像我們的身份證ID一樣,每個人的都不一樣。進程ID也是,是其唯一標示。

?進程的狀態,標識進程是處於運行態,等待態,停止態,還是死亡態

A.運行態:此時進程 或者正在運行,或者準備運行

B.等待態:此時進程在等待一個事件發生或某種系統資源

C.停止態:此時進程被終止

D.死亡態:這是一個已終止的進程,但還在進程向量數組中,占有一個task_struct結構。

?進程的優先級和時間片。不同有優先的進程,被調度運行的次序不一樣,一般是高優先級的進程先運行。時間片標識一個進程將被處理器運行的時間

?虛擬內存 大多數進程有一些虛擬內存(內核線程和守護進程沒有) ,並且Linux必須跟蹤內存如何映射到系統物理內存。

?處理器相關上下文,一個進程可以被認為是系統當前狀態的總和。每當一個進程運行時,它要使用處理器的寄存器、棧等,這是進程的上下文(context)。並且,每當一個進程被暫停時,所有的CPU相關上下文必須保存在該進程的task_struct中。當進程被調度器重新啟動時其上下文將從這裏恢復

3.0linus進程的生命周期

3.0進程創建:

?通過函數產生如clone(),fork(),vfork()

?其中fork()與vfork()都是系統調用clone()實現的,clone()函數原型:
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);其中flags 規定了父進程與子進程的關系

3.1進程切換:

?進程切換統一發生在schedule()函數中,其中,switch_to()是一個宏,其定義如下:

#define switch_to(prev,next,last) do { \
unsigned long esi,edi; asm volatile("pushfl\n\t"     "pushl %%ebp\n\t"     "movl %%esp,%0\n\t" /* save ESP */     "movl %5,%%esp\n\t" /* restore ESP */     "movl $1f,%1\n\t" /* save EIP */     "pushl %6\n\t" /* restore EIP */     "jmp __switch_to\n"     "1:\t"      "popl %%ebp\n\t"     "popfl"      :"=m" (prev->thread.esp),"=m" (prev->thread.eip),      "=a" (last),"=S" (esi),"=D" (edi)     :"m" (next->thread.esp),"m" (next->thread.eip),      "2" (prev), "d" (next)); } while (0)

?prev存放的是當前的current進程描述符指針。
next存放的是需要被替換進來的進程的描述符指針。
last比較麻煩,在一次進程被切換出去又切換進來的過程中,一般會涉及到3個進程,進程A,B,C,當進程A運行時,切換到進程B,然後再系統運行一段時間後,系統中運行的進程變為了進程C,然後由進程調度程序調度進程A運行,當調度進程A運行時,進程A使用的是自己的內核棧,裏面的prev和next就還是從A切換到B時的prev, next,即prev = A, next=B.然而本次從C切換到A應該將prev改變為C才對,而不是A了

4.0linus進程的六種狀態

?運行狀態(TASK_RUNNING)

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

?可中斷睡眠狀態(TASK_INTERRUPTIBLE)

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

?暫停狀態(TASK_STOPPED)

?當進程收到信號SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU時就會進入暫停狀態。可向其發送SIGCONT信號讓進程轉換到可運行狀態。

?僵死狀態(TASK_ZOMBIE)

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

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

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

?睡眠狀TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE)。

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

技術分享圖片

5.0linus進程的調度算法

5.1進程轉換的過程

?進程創建時的狀態為不可打斷睡眠,在do —fork() 結束前被父進程喚醒後。變為執行狀態,處於執行狀態的進程被移到run —queue 就緒任務隊列中等待調度。適當時候由schedule0 按調度算法選中,獲得CPU ,若采用輪轉法,即時,由時鐘中斷觸發timer —interrupt() ,其內部調用schedule() ,引起新一輪調度,當前進程的狀態仍處於執行狀態,因而把當前進程掛蟄Jruil —queue 隊尾。獲得CPU 且正在運行的進程若申請不到某資源。則調用sleep —on() 或interruptible —sleep —on() 睡眠,其task —struct 進程控制塊掛到相應資源的wait —queue 等待隊列如果調用sleep —on() 。則其狀態變為不可打斷睡眠,如果調用interruptible —sleep —on() ,則其狀態變為可打斷睡眠,Sleep—on() 或interruptible —sleep —on() 將調用schedule() 函數把睡眠進程釋放。

5.2進程調度策略

技術分享圖片

?Linux 系統中,為了高效地調度進程,將進程分威兩類:實時進程和普通進程( 又稱非實時進程或一般進程) ,實時進程的優先級要高於其他進程,如果一個實時進程處於可執行狀態,它將先得到執行.實時進程又有兩種策略:時間片輪轉和先進先出,在時間片輪轉策略中。每個可執行實時進程輪流執行一個時間片,而先進先出策略每個進程按各自在運行隊列中的順序執行且順序不能變化。

?在Linux 中,進程調度策略共定義了3 種:

Linux 系統中的每個進程用task ,struct 結構來描述,進程調度的依據是task —struct 結構中的policy 、priority 、counter 和rt —priority ,PCB 中設置Policy 數據項,其值用於反映針對不同類型的進程而采用的調度策略。SCHED —RR 和SCHED —FIFO 用於實時進程。分別表示輪轉調度策略和先進先出調度策略;SCHED —OTHER 表示普通進程,也按照輪轉調度策略處理。這三類調度策略均基於優先級.PCB 中設置Priority 數據項,其值為普通進程的調度優先級.普通進程的可用時間片的初始值即為該值,該值通過系統調用是可以改變的。PCB 中設置rt ~p riority 數據項,其值是實時進程專用的調度優先級,實時進程的可用時間片的初始值即為該值.該優先級也可以用系統調用來修改,PCB 中設置counter 數據項。用於進程可用時間片時值的計數,初始值為rt —priority 或Priority 。進程啟動後該值隨時鐘周期遞減。

6.0 linus進程學習分析

?通過學習linus進程的加深了對進程這一抽象事物的理解更好地對抽象事物進行認知

7.0參靠鏈接

?http://blog.chinaunix.net/uid-26833883-id-3193588.html

?http://blog.chinaunix.net/uid-23253303-id-3952925.html

?http://blog.chinaunix.net/uid-23253303-id-3952935.html

?https://blog.csdn.net/myarrow/article/details/7035684

?https://blog.csdn.net/deyili/article/details/6420034

分析linus進程模型