《Linux內核設計與實現》第三章學習筆記

第三章 進程管理

【學習時間:1小時30分 撰寫博客時間:2小時】linux

【學習內容:進程描述符、進程的建立及終結、線程的建立】緩存

進程是Unix操做系統抽象概念中最基本的一種。進程管理是全部操做系統的心臟所在。函數

1、進程

1. 進程是處於執行期的程序。除了可執行程序代碼,還包括打開的文件、掛起的信號、內核內部數據、一個或者多個執行線程等多種資源學習

  • 線程是在進程活動中的對象;內核調度的對象是線程而不是進程
  • 在Linux系統中,並不區分線程和進程
  • 可能存在兩個或者多個進程執行的是同一個程序;甚至N個進程共享打開的文件、地址空間之類的資源

2. 線程:是進程中活動的對象。每一個線程都有一個獨立的程序計數器,進程棧和一組進程寄存器。內核調度的對象是線程。spa

3. 在現代操做系統中,進程提供兩種虛擬機制:虛擬處理器和虛擬內存。同一進程中的線程之間能夠共享虛擬內存,可是每一個都擁有本身的虛擬存儲器操作系統

4. 進程的生命週期線程

  • 新建立的進程調用exec()這組函數就能夠建立新的地址空間,並把新的程序載入其中;
  • 程序經過exit()函數能夠退出執行;進程退出執行以後就會變爲殭屍進程,直到父進程調用wait()或waitpid()返回關於終止進程的狀態

5. 進程在建立它的時刻開始存活,這一般是調用fork系統的結果。該系統調用經過複製一個現有進程來建立一個全新的進程。fork系統調用從內核返回兩次,一次到父進程,另外一次回到新產生的子進程。3d

2、進程描述符及任務結構

進程描述符:進程列表存放在任務隊列(task list)這一雙向鏈表中,鏈表的項是task_struct即進程描述符的結構。該類型定義在<linux/sched.h>中。進程描述符包含的數據能完整地描述一個正在執行的程序:指針

  • 它打開的文件
  • 進程的地址空間
  • 掛起的信號
  • 進程的狀態
  • 其餘更多信息

2.1 進程描述符的分配

  • 目的:Linux經過slab分配task_ struct結構,以達到對象複用以及和緩存着色的目的,避免資源動態分配和釋放帶來的資源消耗
  • 如今用slab分配器動態生成task_ struct,因此只須要在棧底(對向下增加的棧來講)或棧頂(對向上增加的棧來講)建立一個新的結構struct thread_ info

  • 每一個任務的堆棧尾端(好比,對於向上增加的堆棧來講,就是在堆棧的棧頂)有結構體thread_ info,它指向了task_ struct結構體

2.2 進程描述符的存放

1. 內核中的大部分處理處理進程的代碼都是經過task_ struct進行的;所以,須要經過current宏查找到當前正在運行進程的進程描述符code

2. X86系統中,current把棧指針的後13個有效位屏蔽掉,用來計算出thread_ info的偏移(經過current_ thread_ info函數)

movl $-8192, %eax
andl %esp,%eax3.進程狀態

 

2.3 進程狀態

進程在任什麼時候刻,都一定處於五種狀態中的一種

  • TASK_RUNNING 運行
  • TASK_INTERRUPT 可中斷
  • TASK_UNINTERRUPT 不可中斷
  • TASK_TRACED 被其餘進程跟蹤的進程
  • TASK_STOPPED 進程中止運行

  • TASK_RUNNING:多是正在運行,也可能表示可執行
  • TASK_ INTERRUPT/TASK_UNINTERRUPT:都表示正在阻塞;然然後者表示的狀態收到信號以後也不會被喚醒

2.4 設置進程當前狀態

調用set_ task_ state(task,state)函數將進程設置爲指定狀態

2.5 進程上下文

  • 可執行代碼從一個可執行文件載入到進程的地址空間執行。當一個程序執行了系統調用,內核就會「表明進程執行」並處於進程上下文中
  • 對比:在中斷上下文中,系統不表明進程執行——不會有進程去幹擾這些中斷處理程序

2.6 進程家族樹

  • 全部的進程都是PID爲1的init進程的後代
  • 對於給定的進程,獲取鏈表中下一個進程:

    list_ entry(task->tasks.prev,struct task_struct,tasks)

3、進程建立

Unix系統的進程建立方式

  • fork()經過拷貝當前進程建立一個子進程
  • exec()負責讀取可執行文件並將其載入地址空間開始運行

3.1 寫時拷貝

  • Linux的fork()使用寫時拷貝推遲甚至免除拷貝。內核在建立新進程的時候並不複製整個地址空間,而是讓父進程和子進程共享同一個拷貝;直到子進程/父進程須要寫入的時候才進行拷貝
  • fork的實際開銷只是複製父進程的頁表以及給子進程建立惟一的進程描述符

3.2 fork()

  1. Linux經過clone系統調用實現fork
  2. 由clone去調用do_fork()
  3. 定義在<kernel/fork.c>中的do_ fork()完成建立中的大部分工做,它調用copy_process函數,而後讓進程開始運行

  最後copy_process返回的就是指向子進程的指針

3.3 vfork()

  除了不拷貝父進程的頁表項外,vfork()系統調用和fork()功能相同。子進程做爲父進程的一個單獨的線程在它的地址空間裏運行,父進程被阻塞,直到子進程退出或執行exec()。

  vfork()系統調用的實現是經過向clone()系統調用傳遞一個特殊標誌來進行的:

4、線程在Linux中的實現

  線程機制提供了在同一程序內共享內存地址空間運行的一組線程。在Linux系統中,線程僅僅被視爲一個與其餘進程共享某些資源的進程。每一個線程都有本身的task_struct。

4.1 建立線程

1. 線程的建立與普通進程相似,只不過在調用clone()的時候須要傳遞一些參數標誌來指明共享的資源

2. 傳遞給clone()的參數標誌決定了新建立進程的行爲方式和父子進程之間共享的資源種類

4.2 內核線程

內核線程與普通進程的區別只在於內核線程沒有獨立的地址空間:

  • 它只能經過其餘內核線程建立;內核經過kthread內核進程衍生全部的內核線程
  • 新建立的線程處於不可運行狀態,直到wake_ up_process()明確地喚醒它

5、進程終結

終結進程大部分依賴於do_exit()來完成:

5.1 刪除進程描述符

  1. 該任務是和清理工做分開進行的,由於這樣在進程終結以後系統仍然能夠得到它的信息
  2. 經過release_task()實現進程描述符的刪除
  3. 至此,全部資源都被釋放了

5.2 解決孤兒進程

1. 孤兒進程:父進程在進程以前退出,就會遺留下子進程,也就是孤兒進程

2. 解決方法:在當前的線程組內給孤兒進程尋找新的父進程;不然直接以init做爲其父進程

  • 調用順序:do_ exit()-->forget_ original_ parent()-->find_ new_ parent()-->ptrace_ exit_ finish()

一旦系統爲進程成功地找到和設置了新的父進程,就不會再有出現駐留僵死進程的危險了。init進程會例行調用wait()來檢查其子進程,清除全部與其相關的僵死進程。

總結

  經過本章的學習,我深刻理解了操做系統的核心概念——進程,以及進程與線程之間的關係。同時掌握了在Linux中使用task_ struct和thread_info存放和表示進程,經過fork()建立進程的具體過程。進程是一個很是基礎的抽象概念,對有關進程調度的理解有相當重要的做用。

相關文章
相關標籤/搜索