Linux進程講解--操作系統/task_struct/殭屍狀態/孤兒狀態/IO資訊&記賬資訊/進程優先順序(PR)/fork/虛擬地址空間/ 分頁式...

2020-08-08 21:06:40

1 進程

馮諾依曼體系結構
馮諾依曼:計算機之父,在物理、化學、數學、也具有很大貢獻,開始是研究原子彈的。
馮諾依曼體系結構圖
在这里插入图片描述
關於馮諾依曼,必須強調幾點:

  • 這裏的記憶體指的是記憶體,且所有數據在記憶體中儲存的時候,採用2進位制的方式進行儲存;
  • 不考慮快取情況,這裏的cpu能且只能對記憶體進行讀寫,不能存取外設(輸入或者輸出裝置);
  • 外設要輸入或者輸出數據,也只能寫入記憶體或者從記憶體中讀取,且中央處理器的數據來源於記憶體。
  • 一句話,所有裝置都只能直接和記憶體打交道。

  

2 操作系統(Operator System)

概念
  任何計算機系統都包含一個基本的程式集合,稱爲操作系統(OS)。籠統的理解,操作系統包括:內核(進程管理,記憶體管理,檔案管理,驅動管理);其他程式(例如函數庫,shell程式等等)。
  操作系統本身就是一個軟體,管理計算機的軟硬體資源。原生的操作系統 = 操作系統內核 + 一組應用。

設計OS的目的

  • 與硬體互動,管理所有的軟硬體資源
  • 爲使用者程式(應用程式)提供一個良好的執行環境
  • 操作系統的簡單流程圖,由下到上依次執行
               在这里插入图片描述

定位
  在整個計算機軟硬體架構中,操作系統的定位是:一款純正的「搞管理」的軟體。如何理解管理?管理 = 描述 + 組織。

總結
  計算機管理硬體:描述 + 組織。描述用struct結構體,組織用鏈表或其他高效的數據結構。

  

3 進程

基本概念
  操作系統是通過結構體來描述進程,通過雙向鏈表來組織進程。如下圖所示:
      在这里插入图片描述
進程和程式的區別
  程式:程式就是一個靜態的文字檔案,例如產生了一個靜態的out文字檔案。
  進程:進程執行起來就是一個範例。從內核中分析在操作系統內核當中,爲進程建立了一個task_struct結構體;或稱之爲過程控制塊PCB(process control block),可以理解爲進程屬性的資訊。 task_struct是Linux內核的一種數據結構,它會被裝在到RAM記憶體中並且包含進程的資訊。可以理解爲,建立一個進程 ,就是建立了一個test_struct結構體, 也就是過程控制塊 。
  

瞭解task_struct結構體當中的內容

struct task_struct
{
  進程識別符號(PID):在當前的操作系統中唯一標識當前進程—進程識別符號相當於「身份證」
  記憶體指針:指向程式的地址空間,如下圖所示:
        在这里插入图片描述

進程當中的相關指令

  • ps aux:檢視當前操作系統當中(進程)的資訊
  • ps aux | grep 「XXX」:在ps aux的結果當中過濾字串「XXX」
  • find: 在操作系統當中查詢一個檔案 ;語法:find [path] -name [filename]
  • 例如查詢內核原始碼,內核原始碼一般在根目錄下的usr目錄下,可以用find命令查詢。
  • 舉例查詢sched.h命令:find /usr -name sched.h ,其中檢視一條結果如下:
  • /src/kernels/3.10.0-1127.18.2.el7.x86_64/include/linux/sched.h
  • 使用vim /src/kernels/3.10.0-1127.18.2.el7.x86_64/include/linux/sched.h開啓此檔案,裏面就有task_struct的定義,在vim命令欄下,輸入/task_struct即可搜尋帶有task_struct的文字資訊。

進程狀態 :進程中分爲3種狀態
     1. 執行狀態:可執行程式正在CPU上面進行運算;
     2. 就緒狀態:程式已經準備好,在就緒佇列當中等待獲取CPU的資源;
     3. 阻塞狀態:可執行程式有時候需要等待輸入裝置提供數據,等待IO就緒;
併發執行:多個進程使用一個CPU,每一個進程都獨佔CPU一小會,然後讓出CPU資源,供其他進程執行。
並行執行:多個進程,在同一時刻每一個進程都佔有一個CPU進程運算。

進程中狀態的表示規則

  • R:表示進程中的執行狀態
    範例:生成test可執行程式,過濾字串"./test",並進行搜尋,搜尋結果如下圖所示:
    在这里插入图片描述
  • S:可中斷睡眠狀態
  • D:磁碟睡眠狀態,不可被打斷。範例如下:
    在这里插入图片描述
  • T:暫停狀態。又分爲前臺進程(「+」代表前臺進程)+後臺進程(沒有「+」代表後臺進程)。
    前臺進程中例如打開了qq介面,後臺進程中例如後臺執行着微信。
    範例:./ test執行一個死回圈程式,此時狀態和上一幅圖一樣,都是前臺進程。輸入命令Ctrl+z後暫停進程,ps aux | grep ./test重新檢視進程如下圖所示:
    在这里插入图片描述此時前臺進程轉化到後臺進程狀態。若想在轉化到前臺進程,輸入fg,此時又會轉化到前臺進程中,繼續進行死回圈。
  • t:跟蹤狀態 – gdb偵錯程式設計師的時候可以發現程式是t狀態。
    範例:先開啓進程,檢視進程ip。如下所示:
    在这里插入图片描述
    讓進程進入gdb偵錯模式---->gdb -p 8419(此時進入了偵錯模式)
    在这里插入图片描述
    檢視進程模式狀態,輸入ps aux | grep ./test檢視結果如下:
    在这里插入图片描述
    此時進程在跟蹤狀態中進行偵錯。結束後又進入原先狀態。
  • X:死亡狀態
  • Z:殭屍狀態 殭屍狀態詳細介紹可以看這篇部落格:殭屍狀態和孤兒狀態分析

程式計數器:儲存進程即將要執行的下一條指令。
上下文數據:儲存上一次執行的時候,暫存器當中的值。

  • 進程在執行的時候,進程會進行切換,切換是系統排程的;
  • 在被切換出去的時候,該進程當中的程式計數器會儲存程式要執行的下一條指令,上下文資訊會儲存暫存器當中的值;
  • 再次切換回來的時候,通過程式計數器和上下文資訊來恢復之前的場景,繼續運算。

IO資訊&記賬資訊
當前進程啓動的時間(15:39);當前程式佔據CPU的時間(0:00)
在这里插入图片描述
開啓檔案的資訊:ll /proc/ [PID]/fd
舉例:ll /proc/12341/ fd 開啓結果如下:其中0、1、2、代表標準輸入,標準輸出、標準錯誤。
在这里插入图片描述
進程優先順序(PR): - - 瞭解
PR值越小優先順序越低,反之優先順序越高。
PR(new) = PR(old) + NI;NI值時修正PR值得一個參數 。輸入r可進行修改, 輸入top可檢視系統資源消耗情況:
在这里插入图片描述

通過系統呼叫建立進程–fork初識

講解前提:
  PCB(task_struct)中儲存有程式計數器和上下文資訊,用來執行和儲存命令,並且一塊PCB指向一塊程式的地址空間。
我們在建立子進程也就是新的進程中,需要呼叫fork函數。其命令如下:

#include <unistd.h>
peid_t fork(void);(peid_t是int型別)

  其中fork函數是有返回值的,失敗返回值小於;成功返回兩次。父進程返回值大於0,子進程返回0。

  1. 父進程呼叫fork函數,建立子進程,子進程的PCB(task_struct)是拷貝父進程的。實驗程式碼如下:
#include <stdio.h>
#include <unistd.h>

int main()
{
	printf("-----begin-----\n");
	fork();
	printf("linux84 666\n");
	return 0;
}

輸出結果:
在这里插入图片描述
執行邏輯:
  在父進程中,先開始列印begin,然後父進程呼叫fork函數,子進程拷貝父進程PCB(task_struct),其中拷貝過去task_struct中的程式計數器儲存的是下一次進程即將要執行的命令,在本例也就是列印printf函數中的「linux84 666」,因此父進程和子進程會列印兩個「linux84 666」。

通過ps -ef可以檢視進程的pid和進程的父進程的pid,具體程式碼和例子看本篇部落格:父進程和子進程關係例題分析

在進程中還有和進程無關的知識點,但比較重要,想要閱讀的讀者可以參考這篇部落格:Linux中環境變數詳解

進程的虛擬地址空間

前提知識:父子進程,輸出地址是一致的,但是變數內容不一樣!能得出如下結論:

  • 變數內容不一樣,所以父子進程輸出的變數絕對不是同一個變數;
  • 但地址值是一樣的,說明,該地址絕對不是實體地址!
  • 在Linux地址下,這種地址叫做虛擬地址;
  • 我們在用C/C++語言所看到的地址,全部都是虛擬地址!實體地址,使用者一概看不到,由OS統一管理OS必須負責將虛擬地址轉化成實體地址。

進程地址空間
所以之前說‘程式的地址空間’是不準 不準確的,準確的應該說成進程地址空間,那該如何理解呢?看圖:
在这里插入图片描述
說名當fork完成之後,父進程之前的數據會被子進程通過頁表結構對映到同一塊實體記憶體上。
寫時拷貝:

  • 當fork的時候,如果父子進程不修改數據,則頁表的對映關係不會改變。
  • 當其中有一方修改數據的時候嗎,爲了防止導致另一方讀取到的數據是錯誤的,所以需要在實體記憶體中重新開闢一段空間,儲存修改後的值,並且將修改後的進程的頁表結構當中的對映關係重新指向新的實體記憶體。由上圖可知20(g_val)是新開闢的一塊實體記憶體空間,10(g_val)是以前父進程和子進程共同指向的一塊實體記憶體空間。

分頁式

前提內容:

  • 進程虛擬地址空間分成了一頁一頁的小塊,實體記憶體分成了一塊一塊的小塊,一頁的大小 = 一塊的大小 = 4096k

  • 頁表維護了頁和快的關係。

概念:
  計算虛擬地址通過頁表對映到哪一個實體記憶體地址上。
在这里插入图片描述
注意: 上圖中的虛擬地址空間指的是一塊虛擬地址空間,虛擬地址空間由頁號和頁內偏移構成,頁號通過頁表上的頁號找到頁表中的快號,快號的起始地址加上頁內偏移找到實體記憶體上的地址。
計算規則:

  • 頁號 = 虛擬地址/快的大小;
  • 頁內偏移 = 虛擬地址 % 快的大小;
  • 快的起始地址 = 快號 * 快大小;
  • 實體地址 = 快的起始位置 + 頁內偏移。

分段式

虛擬地址的構成 = 短號 + 段內偏移
在这里插入图片描述

段頁式

在这里插入图片描述

  • 通過段號找到頁的起始位置;
  • 通過頁的起始位置,找到對應的頁表結構;
  • 通過頁號,找到對應的快號,通過快號,計算出快的起始位置;
  • 快的起始地址加上頁內偏移計算出實體地址。
    }