Linux多任務編程(一)—任務、進程、線程

來源:互聯網
上載者:User

Linux下多任務介紹

  首先,先簡單的介紹一下什麼叫多任務系統?任務、進程、線程分別是什嗎?它們之間的區別是什嗎?,從而可以宏觀的瞭解一下這三者,然後再針對每一個仔細的講解。

   什麼叫多任務系統?多任務系統指可以同一時間內運行多個應用程式,每個應用程式被稱作一個任務。

   任務定義:任務是一個邏輯概念,指由一個軟體完成的任務,或者是一系列共同達到某一目的的操作。

   進程定義:進程是指一個具有獨立功能的程式在某個資料集上的一次動態執行過程,它是系統進行資源分派和調度的最小單元。

   線程定義:線程是進程內獨立的一條運行路線,是處理器調度的最小單元,也可以成為輕量級進程。

   看了定義,有點暈,還是通俗的說一下它們的區別吧。①通常一個任務是一個程式的一次執行,一個任務包含一個或多個完成獨立功能的子任務,這個獨立的子任務就是進程或線程。②一個進程可以擁有多個線程,每個線程必須有一個父進程。

任務

    任務是一個邏輯概念,指由一個軟體完成的任務,或者是一系列共同達到某一目的的操作。通常一個任務是一個程式的一次執行,一個任務包含一個或多個完成獨立功能的子任務,這個獨立的子任務就是進程或線程。例如,一個殺毒軟體的一次運行是一個任務,目的是從各種病毒的侵害中保護電腦系統,這個任務包含多個獨立功能的子任務(進程或線程),包括即時監控功能、定時查殺功能、防火牆功能及使用者互動功能等。任務、進程和線程之間的關係1所示

   

進程

進程的基本概念

   進程是指一個具有獨立功能的程式在某個資料集上的一次動態執行過程,它是系統進行資源分派和調度的基本單元。一次任務的運行可以並發啟用多個進程,這些進程相互合作來完成該任務的一個最終目標。

   進程具有並發性、動態性、互動性、獨立性和非同步性等主要特性。

   進程和程式是有本質區別的:程式是靜態一段代碼,是一些儲存在非易失性儲存空間的指令的有序集合,沒有任何執行的概念;而進程是一個動態概念,它是程式執行的過程,包括動態建立、調度和消亡的整個過程,它是程式執行和資源管理的最小單位。

Linux下的進程結構

  進程不但包括程式的指令和資料,而且包括程式計數器和處理器的所有寄存器及儲存臨時資料的進程堆棧,因此,正在執行的進程包括處理器當前的一切活動。

  因為linux是一個多任務多進程的作業系統,所以其他的進程必須等到系統將處理器使用權分配給自己之後才能運行。當正在啟動並執行進程等待其他的系統資源時,linux核心將取得處理器的控制權,並將處理器分配給其他正在等待的進程,他按照核心中的調度演算法決定將處理器分配給哪一個進程,也就是說,核心不會讓處理器閑著。

  核心將所有進程存放在雙向迴圈鏈表(進程鏈表)中,其中鏈表的頭是 init_task 描述符。鏈表的每一項都是類型為 task_struct,稱為進程描述符的結構,該結構包含了一個進程相關的所有資訊,定義在<include/linux/sched.h>檔案中。task_struct核心結構比較大,它能完整的描述一個進程,如進程的狀態、進程的基本資料、進程標識符、記憶體相關資訊、父進程相關資訊、與進程相關的終端資訊、當前工作目錄、開啟的檔案資訊、所接收的訊號資訊等。

  下面詳細講解task_struct結構中最為重要的兩個域:state(進程狀態)和pid(進程標識符)。如果想具體瞭解task_struct,請點這裡。

 (1)進程狀態

   Linux中的進程有以下幾種狀態。

   ● 運行狀態(TASK_RUNNING):進程當前正在運行,或者正在運行隊列中等待調度。

   ● 可中斷的阻塞狀態(TASK_INTERRUPTIBLE):進程處於阻塞(睡眠)狀態,正在等待某些事件發生或能夠佔用某些資源。處在這種狀態下的進程可以被訊號中斷。接收到訊號或被顯式的喚醒呼叫(如調用 wake_up 系列宏:wake_up、wake_up_interruptible等)喚醒之後,進程將轉變為 TASK_RUNNING 狀態。

   ● 不可中斷的阻塞狀態(TASK_UNINTERRUPTIBLE):此進程狀態類似於可中斷的阻塞狀態(TASK_INTERRUPTIBLE),只是它不會處理訊號,把訊號傳遞到這種狀態下的進程不能改變它的狀態。在一些特定的情況下(進程必須等待,直到某些不能被中斷的事件發生),這種狀態是很有用的。只有在它所等待的事件發生時,進程才被顯示的喚醒呼叫喚醒。

   ● 可終止的阻塞狀態(TASK_KILLABLE):該狀態的運行機制類似於TASK_UNINTERRUPTIBLE,只不過處在該狀態下的進程可以響應致命訊號。它可以替代有效但可能無法終止的不可中斷的阻塞狀態(TASK_UNINTERRUPTIBLE),以及易於喚醒但安全性欠佳的可中斷的阻塞狀態TASK_INTERRUPTIBLE)。

   ● 暫停狀態(TASK_STOPPED):進程的執行被暫停,當進程收到 SIGSTOP、SIGSTP、SIGTTIN、SIGTTOU等訊號時,就會進入暫停狀態。

   ● 跟蹤狀態(TASK_TRACED):進程的執行被調試器暫停。當一個進程被另一個監控時(如調試器使用ptrace()系統調用監控測試程式),任何訊號都可以把這個進程置於跟蹤狀態。

   ● 殭屍狀態(EXIT_ZOMBIE):進程運行結束,父進程尚未使用 wait 函數族(如調用 waitpid()函數)等系統調用來“收屍”,即等待父進程銷毀它。處在該狀態下的進程“屍體”已經放棄了幾乎所有的記憶體空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的推出狀態等資訊供其他進程收集。

   ● 殭屍撤銷狀態(EXIT_DEAD):這是最終狀態,父進程調用 wait 函數族“收屍”後,進程徹底由系統刪除。

   它們之間的轉換關係2所示:

    

    進程可以使用 set_task_state 和 set_current_state 宏來改變指定進程的狀態資訊和當前進程的狀態。

(2)進程標識符

   Linux核心通過唯一的進程標識符 PID 來標識每個進程(就和檔案描述符一樣)。PID存放在進程描述符的 pid 欄位中,新建立的 PID 通常是前一個進程的 PID 加1,不過PID的值有上限(最大值=PID_MAX_DEFAULT-1,通常為32767),讀者可以查看/proc/sys/kernel/pid_max 來確定該系統的進程數上限。

   當系統啟動後,核心通常作為某一個進程的代表。一個指向task_struct的宏current用來記錄正在啟動並執行進程。current經常作為進程描述符結構指標的形式出現在核心代碼中,例如,current->pid 表示處理器正在執行的進程的PID。當系統需要查看所有的進程時,則調用for_each_process()宏,這將比系統搜尋數組的速度要快的多。

   在Linux中獲得當前進程號的(PID)和父進程號(PPID)的系統調用函數分別為 getpid() 和 getppid()。

進程的建立、執行、終止

  (1)進程的建立和執行

   咱們首先得知道啥是建立,啥是執行哈!我剛開始看的時候沒懂。建立進程就是產生一個新的進程,這個大家都知道。而進程的執行,前邊講進程的的定義的時候,就說了正在啟動並執行子任務,說白了,進程執行也就是讓產生的這個進程幹點什麼事,別佔著那啥不拉那啥。

   許多作業系統提供的都是產生進程的機制,也就是說,首先在新的地址空間裡建立進程、讀入可執行檔,最後再開始執行。Linux 中進程的建立很特別,它把上述的步驟分解到兩個單獨的函數中去執行:fork()函數和exec函數族。首先,fork()函數通過複製當前進程建立一個子進程(注意此時資源還沒有被複製過來,去瞭解一下寫時複製頁技術吧),子進程於父進程的區別僅僅在於不同的PID、PPID和某些資源及統計量。exec函數族負責讀取可執行檔並將其載入地址空間開始運行。

  (2)進程的終止

   進程終結也需要很多繁瑣的工作,系統必須保證回收進程所佔用的資源,並通知父進程。Linux首先把終止的進程設定為殭屍狀態,這時,進程無法投入運行,它的存在只為父進程提供資訊,申請死亡。父進程得到資訊後,開始調用 wait 函數族,最後終止子進程,子進程佔用的所有資源被全部釋放。

進程的記憶體結構

   Linux作業系統採用虛擬記憶體管理技術,使得每個進程都有各自互不干涉的進程地址空間。該地址空間是大小為 4GB的線性虛擬空間,使用者所看到和接觸到的都是該虛擬位址,無法看到實際的實體記憶體地址。利用這種虛擬位址不但能起到保護作業系統的效果(使用者不能直接存取物理地址),而且,更重要的是,使用者程式可以使用比實際實體記憶體更大的地址空間。

  4GB的進程地址空間會被分成兩個部分:使用者空間與核心空間。使用者地址空間是從0到3GB(0xC000 0000),核心地址空間佔據3GB到4GB。使用者進程通常情況下只能訪問使用者空間的虛擬位址,不能訪問核心空間的虛擬位址。只有使用者進程使用系統調用(代表使用者進程在核心態執行)時可以訪問到核心空間。每當進程切換時,使用者空間就跟著變化;而核心空間由核心負責映射,它不會跟著進程改變,是固定的。核心空間地址有自己對應的頁表,使用者進程各自有不同的頁表。每個進程的使用者空間都是完全獨立、互不相干的。進程的虛擬記憶體空間3所示,其中使用者空間包括以下幾個功能區域:

   ●  唯讀段: 包含程式碼(.init和.text)和唯讀資料(.rodata)。

   ●  資料區段: 存放的是全域變數和靜態變數。其中可讀可寫資料區段(.data)存放已初始化的全域變數和靜態變數,BSS資料區段(.bss)存放未初始化的全域變數和靜態變數。

   ●  堆: 由系統自動分配釋放,存放函數的參數值、局部變數的值、返回地址等。

   ●  堆棧: 存放動態分配的資料,一般由程式員動態分配和釋放。若程式員不釋放,程式結束時可能由作業系統回收。

   ●  共用庫的記憶體映射地區: 這是Linux動態連結器和其他共用程式碼程式庫代碼的映射地區。

   

    由於在Linux系統中每一個進程都會有/proc檔案系統下與之對應的一個目錄(如將init進程的相關資訊在/proc/1 目錄下的檔案中描述),因此通過 proc 檔案系統可以查看某個進程的地址空間的映射情況。例如,運行一個應用程式,如果它的進程號為13703,則輸入“ cat  /proc/13703/maps”命令,可以查看該進程的記憶體映射情況。

線程

   前面已經講到,進程是系統中程式執行和資源分派的基本單位。每個進程都擁有自己的資料區段、程式碼片段和堆棧段,這就造成了進程在進行切換等動作時需要較複雜的環境切換等動作。為了進一步減少處理機的空閑時間,支援多處理器及減少環境切換開銷,進程在演化中出現了另一個概念---線程。它是進程內獨立的一條運行路線,是處理器調度的最小單元,也可以稱為輕量級線程。線程可以對進程的記憶體空間和資源分派進行訪問,並與同一進程中的其他線程共用。因此,線程的環境切換的開銷比建立進程小得多。

   一個進程可以擁有多個線程,每個線程必須有一個父進程。線程不擁有系統資源,它只具有運行時所必須的一些資料結構,如堆棧/寄存器與線程式控制制塊(TCB),線程與其父進程的其他進程共用該進程所擁有的全部資源。要注意的是,由於線程共用了進程的資源和地址空間,因此,任何線程對系統資源的操作都會給其他進程帶來影響。由此可知,多線程中的同步是一個非常重要的問題。在多線程系統中,進程與線程的關係4所示

 

   在Linux系統中,線程分為3種:①使用者線程 ②輕量級線程 ③核心線程 

   下一節:http://blog.csdn.net/mybelief321/article/details/9054405

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.