Linux作業系統中的行程
- 1 認識馮諾依曼系統
- 2 作業系統
- 2.1 概念
- 2.2 設計OS的目的
- 2.3 定位
- 3 行程
- 3.1 行程的基本概念和描述行程
- 3.2 如何組織行程
- 3.3 行程的基本操作
- 3.3.1 查看行程
- 3.3.2 創建行程
- 3.3.3 行程的狀態
1 認識馮諾依曼系統
我們常見的計算機筆記本和不常見的計算機服務器大部分都遵循馮諾依曼系統,
我們所認識的計算機,都是由一個個的硬體組件組成,
- 輸入單元:鍵盤滑鼠等
- 中央處理器(CPU):包含運算器和控制器等
- 輸出單元:顯示幕,列印機等
關于馮諾依曼 - 存盤器指的是記憶體
- 不考慮快取情況,CPU只能對記憶體進行讀寫,不能訪問外設(輸入和輸出設備)
- 外設要輸出或者輸出資料,也只能寫入記憶體或者從記憶體中讀取,
- 所有設備都只能和記憶體打交道,
2 作業系統
2.1 概念
任何計算機系統都包含一個基本的程式集合,稱為作業系統(OS),簡單來說OS包括
內核(行程管理、記憶體管理、檔案管理、驅動管理)
其他程式(函式庫、shell程式等)
2.2 設計OS的目的
對上為用戶程式(應用程式)提供一個良好的執行環境,
對下與硬體互動,管理所有的軟硬體資源

2.3 定位
在整個計算機軟硬體架構中,作業系統的定位是:搞管理的軟體,那么類似學生的例子,比如要管理學生,
是要把學生描述起來,比如描述一個學生可以有學號、籍貫、電話等,那么管理學生其實并不是直接管理而是
把學生的資訊錄入學生管理系統,其實用面向物件的思想可以概括為:把學生共有的特性抽象出來,每個學生
其實是一個物件,
3 行程
3.1 行程的基本概念和描述行程
當我們把C/C++源代碼編譯運行是生成可執行程式,那么行程就是:行程對應的可執行程式 + 描述該行程各種屬性資訊的資料結構,
行程資訊被放在一個叫做行程控制塊的資料結構中,可以看做是行程屬性的集合,
課本上稱之為PCB(process control block),Linux作業系統下的PCB是:task_struct
task_struct是Linux內核的一種資料結構,它會被裝載到記憶體里并且包含著屬性資訊,
task_ struct內容分類
標示符: 描述本行程的唯一標示符,用來區別其他行程,
狀態: 任務狀態,退出代碼,退出信號等,
優先級: 相對于其他行程的優先級,
程式計數器: 程式中即將被執行的下一條指令的地址,
記憶體指標: 包括程式代碼和行程相關資料的指標,還有和其他行程共享的記憶體塊的指標
背景關系資料: 行程執行時處理器的暫存器中的資料[休學例子,要加圖CPU,暫存器], I/O狀態資訊: 包括顯示的>I/O請求,分配給行程的I/O設備和被行程使用的檔案串列,
等,
可以在內核源代碼里找到它,所有運行在系統里的行程都以task_struct鏈表的形式存在內核里,
3.2 如何組織行程
可以在內核源代碼中找到它,所有運行在系統里的行程都以task_struct鏈表的形式存在內核里,
這個結構體是非常巨大的,
3.3 行程的基本操作
3.3.1 查看行程
1、ps aux

2、 ls /proc/
在/proc下查看具體某個行程的全部屬性資訊
3、 通過系統呼叫獲得行程識別符號
3.3.2 創建行程
通過系統呼叫fork(),創建行程,
在Linux下運行man fork可以認識下fork
fork沒有引數
fork有兩個回傳值,這個很重要,如果創建成功給父行程(當前行程)回傳創建的子行程的行程ID,給子行程回傳0值,如果創建行程失敗,給父行程回傳值-1,
父行程和子行程代碼是共享的,資料各自開辟空間私有一份(采用寫時拷貝)
fork之后通常要用if進行分流,之后就可以不同的行程執行不同的任務,實作分流
下面是原汁原味的英文檔案:

用系統呼叫fork創建行程的代碼如下:

運行結果如下:

- 那么下面有幾個問題需要搞清楚,
- 1、如何深刻理解行程創建呢?
一個行程在系統中被創建了,系統多了一個行程,就多了一個管理改行程的資料結構(OS內核做的事情) + 該行程對應的代碼和資料(用戶做的事)
- 2、fork為什么會有兩個回傳值,如何深刻理解?

- 3、fork后父子行程的執行順序,代碼資料在父子行程之間是怎樣分配的?
行程資料 = 代碼 + 資料
父行程創建子行程的時候代碼是共享的,資料是各自私有一份的
代碼是邏輯,一般是不能被修改的,資料是可讀可寫的,這也證實了上面說的fork那的的運行結果,
父和子誰先運行是由調度器決定的,看誰先排隊,而且是有變化的,是由調度器的演算法決定的,

3.3.3 行程的狀態
為了搞清楚正在運行的行程是什么意思,我們需要知道行程的不同狀態,一個行程可以有幾個狀態,在linux內核里,行程有時候也叫做任務,
行程的狀態在Linux內核的源代碼是是這樣定義的:

- 1、R運行狀態(running):并不意味著行程一定在運行中,它表明行程要么是在運行中要么是在運行佇列里,也就是說R狀態的行程可以不在CPU上,R代表可以直接被CPU調度,
- 2、S睡眠狀態(sleeping):意味著行程在等待事件完成(這里的睡眠也叫做可中斷睡眠(interruptible sleep)),舉個栗子,看下面的操作圖,


行程proc的狀態是S(+代表不是后臺行程可以被Ctrl + c終止),因為printf()這個系統呼叫要把資料列印到顯示幕這個外設上,而顯示幕外設的速度是相當低的,CPU的速度是非常快的,那么大部分時間CPU都是在等待顯示幕完成作業,


上面這個情況就變成了R,因為死回圈只在CPU上跑,不和外設打交道,
3、D磁盤休眠狀態(Disk sleep):有時也叫做不可中斷睡眠(uninterruptible sleep),也可叫做深度睡眠,S為淺度睡眠,比如行程要讀取硬碟的資料(IO操作),先要找到資料,然后要拷貝一份給行程進入記憶體,此時行程在等待資料準備完事才可以操作,此時萬一記憶體要是被其他行程占用完了,OS為了不讓自己掛掉,要開始殺行程了,但是不能殺掉,D這個情況的行程,因為殺掉了,IO操作發生錯誤,為了避免這個情況發生,OS把D這個情況的行程標記為不可以殺掉的行程,這是一個硬碟不給我資料至死不休的狀態,S,R都是可以被殺掉的,
4、T停止狀態(stopped):可以通過發送SIGSTOP信號給行程,來停止行程,這個被暫停的行程可以通過接受SIGCONT信號讓行程繼續進行,
可以通過kill -l 查看信號代碼

下面給出操作:后臺運行行程proc 然后暫停使其狀態變為T,再變為R





5、X死亡狀態(dead):這個狀態只是一個回傳狀態,你不會在任務串列里看到這個狀態,
6、Z(zombine)僵尸行程:僵死狀態是一個比較特殊的狀態,當行程退出并且父行程,沒有讀取到子行程退出的回傳碼是就會產生僵尸行程,
僵死(尸)行程會以終止狀態保持在行程表時,并且會一直等待父行程讀取退出狀態碼,
所以,只要子行程退出,父行程還在進行,但父行程沒有讀取子行程狀態,子行程會進入Z狀態,
危害:
行程的退出狀態必須被維持下去,因為他要告訴關心它的行程(父行程),你交給我的任務,我辦的怎
么樣了,可父行程如果一直不讀取,那子行程就一直處于Z狀態?是的!
那一個父行程創建了很多子行程,要是不回收,就會造成記憶體資源的泄露,那么既然有危害OS為什么還允許這個存在呢,因為父行程要讀取子行程的退出資訊不能立刻把子行程的資源還給OS,
總結一下,子行程退出,父行程還沒讀取子行程的退出狀態的這段時間就是Z,
7 孤兒行程:父行程如果提前退出,子行程就稱之為"孤兒行程",
孤兒行程被1號init行程領養
來一張總結圖

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/257457.html
標籤:其他
下一篇:用位運算進行大小寫字母轉換







