1. 程式人生 > >第一次作業:深入源碼分析進程模型

第一次作業:深入源碼分析進程模型

family 訪問權限 創建 表示 函數 文本 執行 超過 分析

前言:

這是一篇關於linux操作系統的簡單介紹。linux本身不能算是操作系統,只是一個內核,基於linux內核的操作系統有很多,比如流行的android,ubuntu,紅旗linux等等。Linux以它的高效性和靈活性著稱。它能夠在PC計算機上實現全部的Unix特性,具有多任務、多用戶的能力。Linux是在GNU公共許可權限下免費獲得的,是一個符合POSIX標準的操作系統。Linux操作系統軟件包不僅包括完整的Linux操作系統,而且還包括了文本編輯器、高級語言編譯器等應用軟件。它還包括帶有多個窗口管理器的X-Windows圖形用戶界面,如同我們使用Windows NT一樣,允許我們使用窗口、圖標和菜單對系統進行操作。

.操作系統是怎麽組織進程的?

1.1 進程的概念

一個進程是一個程序的一次執行的過程。它和程序不同,程序是靜態的,它是一些保存在磁盤上的可執行的代碼和數據集合;而進程是一個動態的概念,它是Linux系統的基本的調度單位。一個進程由如下元素組成:

1、 程序的讀取上下文,它表示程序讀取執行的狀態。

2、 程序當前執行目錄。

3、 程序服務的文件和目錄。

4、 程序的訪問權限。

5、 內存和其他分配給進程的系統資源。

1.2 進程的創建

fork()提供了創建進程的基本操作,可以說它是Linux系統多任務的基礎。 其實fork、vfork和clone三個系統調用都可以創建一個新進程,而且都是通過調用do_fork來實現進程的創建。Linux通過復制父進程來創建一個新進程,那麽這就給我們理解這一個過程提供一個想象的框架:

  • 復制一個PCB——task_struct

err = arch_dup_task_struct(tsk, orig);
  • 給新進程分配一個新的內核堆棧
ti = alloc_thread_info_node(tsk, node);
tsk
->stack = ti; setup_thread_stack(tsk, orig); //這裏只是復制thread_info,而非復制內核堆棧
  • 修改復制過來的進程數據,比如pid、進程鏈表等等
*childregs = *current_pt_regs(); //復制內核堆棧
 childregs->ax = 0; //子進程的fork返回0
p->thread.sp = (unsigned long) childregs; //調度到子進程時的內核棧頂
p->thread.ip = (unsigned long) ret_from_fork; //調度到子進程時的第一條指令地址

1.3 進程的組織

1.3.1等待隊列

等待隊列在內核中有很多用途,尤其用在中斷處理,進程同步及定時。等待隊列還實現了在時間上的條件等待:希望等待特定事件的進程把自己放進合適的等待隊列,並放棄控制權。因此,等待隊列表示一組睡眠的進程,當某一條件變為真時,由內核喚醒它們。等待隊列數據結構為雙向鏈表,其元素包括指向進程描述符的指針。列頭是一個類型為wait_queue_head_t的數據結構:

struct _ _wait_queue_head {
      spinlock_t lock;
      struct list_head task_list;
};
typedf struct_ _wait_queue_head wait_queue_head_t;

等待隊列是由中斷處理程序和主要內核函數修改的,因此必須對其雙向連表進行保護以免對其進行同時訪問,因為同時訪問會導致不可預測的後果。同步是通過等待隊列頭中的Lock自旋鎖打到的。task_list字段是等待進程聊表的頭。

二.進程狀態如何轉換?

技術分享圖片

◆運行狀態(TASK_RUNNING)

指正在被CPU運行或者就緒的狀態。這樣的進程被成為runnning進程。運行態的進程可以分為3種情況:內核運行態、用戶運行態、就緒態。

◆可中斷睡眠狀態(TASK_INTERRUPTIBLE)

處於等待狀態中的進程,一旦被該進程等待的資源被釋放,那麽該進程就會進入運行狀態。

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

該狀態的進程只能用wake_up()函數喚醒。

◆暫停狀態(TASK_STOPPED)

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

◆僵死狀態(TASK_ZOMBIE)

當進程已經終止運行,但是父進程還沒有詢問其狀態的情況。

三.進程是如何調度的?

  • 在 Linux中,進程的運行時間不可能超過分配給他們的時間片,他們采用的是 搶占式多任務處理,所以進程之間的掛起和繼續運行無需彼此之間的協作。在一個如 linux這樣的多任務系統中,多個程序可能會競爭使用同一個資源,在這種情況下,我們認為,執行短期的突發性工作並暫停運行以等待輸入的程序,要比持續占用處理器以進行計算或不斷輪詢系統以查看是否有輸入到達的程序要更好。我們稱表現好的程序為 nice程序,而且在某種意義上,這個nice 是可以被計算出來的。 操作系統根據進程的 nice值來決定它的優先級,一個進程的nice值默認為0並將根據這個程序的表現不斷變化。長期不間斷運行的程序的優先級一般會比較低。進程狀態這個是最優先考慮的,也就是說優先級最高的。下面是linux中進程的狀態:

TASK_RUNNING:就緒狀態,得到CPU就可以運行。

TASK_INTERRUPTIBLE:淺度睡眠,資源到位或者受到信號就會變成就緒態。

TASK_UNINTERRUPTIBLE:深度睡眠,資源到位就會進入就緒態,不響應信號。

TASK_ZOMBIE:僵死態,進程exit後。

TASK_STOPPED:暫停態,收到SIG_CONT信號進入就緒態。

  • 同樣的linux也給出了三種的調度策略三種調度策略: SCHED_OTHER、SCHED_FIFO 和 SCHED_RR。

SCHED_OTHER:阻塞和上述相同;被搶占後,其動態優先級會被重新計算,並安排到新的隊列尾部;時間片耗盡後,任務不再存在於活動任務隊列中,而是被放到過期任務隊列中。必須等到過期任務隊列切換為活動任務隊列,才可能被重新調度。

SCHED_FIFO:如果因為阻塞切換出去,則進入阻塞態,不再繼續態列表中,因此不涉及到調度的問題;如果是因為出讓CPU或被搶占切換出去,則直接回到隊列頭中,後面可繼續執行。

SCHED_RR:阻塞和被搶占時的情形與SCHED_FIFO相同;如果是因為時間片耗盡造成的切換,則任務回到隊列尾部。

  • 並且每個CPU擁有獨立的隊列,存在專用的負載均衡算法,可實現多CPU之間的進程調配 。

系統的所有就緒隊列都在runqueues數組中,數組元素個數與cpu個數相關

DECLARE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);

可執行隊列的數據結構為struct runqueue,定義在sched.c中。

struct runqueue {

spinlock_t
lock; /* 保護隊列鎖 */ unsigned long nr_running; /* 可運行的任務數 */ unsigned long expired_timestamp; /* 隊列最近被換出時間 */ unsigned long nr_uninterruptible; /* 處在不可打斷睡眠的任務數 */ prio_array_t *active, *expired, arrays[2]; /* 優先級數組相關 */ … }

運行隊列容納了系統中所有可以運行的進程,它是一個雙向循環隊列。該隊列通過task_struct結構中的兩個指針run_list鏈表來維持。隊列的標誌有兩個:一個是“空進程”idle_task、一個是隊列的長度。有兩個特殊的進程永遠在運行隊列中待著:當前進程和空進程。前面我們討論過,當前進程就是由cureent指針所指向的進程,也就是當前運行著的進程,但是請註意,current指針在調度過程中(調度程序執行時)是沒有意義的,為什麽這麽說呢?調度前,當前進程正在運行,當出現某種調度時機引發了進程調度,先前運行著的進程處於什麽狀態是不可知的,多數情況下處於等待狀態,所以這時候current是沒有意義的,直到調度程序選定某個進程投入運行後,current才真正指向了當前運行進程;空進程是個比較特殊的進程,只有系統中沒有進程可運行時它才會被執行,Linux將它看作運行隊列的頭,當調度程序遍歷運行隊列,是從idle_task開始、至idle_task結束的,在調度程序運行過程中,允許隊列中加入新出現的可運行進程,新出現的可運行進程插入到隊尾,這樣的好處是不會影響到調度程序所要遍歷的隊列成員,可見,idle_task是運行隊列很重要的標誌。

另一個重要標誌是隊列長度,也就是系統中處於可運行狀態(TASK_RUNNING)的進程數目,用全局整型變量nr_running表示,在/kernel/fork.c中定義如下:

int nr_running=1

nr_running為0,就表示隊列中只有空進程。在這裏要說明一下:若nr_running為0,則系統中的當前進程和空進程就是同一個進程。但是Linux會充分利用CPU而盡量避免出現這種情況。

四.談談自己對該操作系統進程模型的看法

在linux操作系統中,往往是就緒的進程數目很多,在某一時刻必定有進程在就緒態等待處理器使用時間。Linux系統的特性在於可以並發的交互執行多個進程。主要分為非搶占式任務系統和搶占式任務系統。在搶占式任務系統中,調度程序決定了一個進程的運行以便其它進程可以分享到處理器使用時間。搶占是調度程序強制掛起某一個進程的行為。進程從被允許運行到被強占之前可以運行的時間成為時間片。時間片的管理屬於進程調度的一部分。有效的管理時間片可以避免個別優先級較高的程序獨占處理器。對非搶占的Linux,除非正在運行的進程自己主動停止運行,否則會一直占據處理器使用權。如果該進程出現錯誤,則整個系統就會崩潰。讓步是進程主動放棄處理的行為。同時,進程的優先級可以被設定,而且是動態的。

第一次作業:深入源碼分析進程模型