目錄
一,操作系統(Operator System)
1-1概念
1-2設計操作系統的目的
1-3核心功能
1-4系統調用和庫函數概念?
二,進程(Process)
2-1進程概念與基本操作
?2-2task_struct結構體內容
2-3查看進程
2-4通過系統調用創建進程?
三,進程狀態
?運行狀態(running)
阻塞狀態(sleeping)
掛起狀態
z(zombie)僵尸狀態
僵尸進程危害
孤兒進程
一,操作系統(Operator System)
1-1概念
任何計算機內部都包含一個基本的程序集合,成為操作系統(OS)。
操作系統包括:
- 內核(進程管理,進程調度,文件管理,驅動管理等)
- 其他程序(例如函數庫,shell程序等)
1-2設計操作系統的目的
對下,與硬件交互,管理所有的軟硬件資源
對上,為用戶程序(應用層程序)提供一個良好的執行環境
1-3核心功能
操作系統“管理”硬件。
如何管理:
- 先描述起來,用struct結構體描述信息
- 再組織,用鏈表或者其他高效的數據結構將struct對象組織起來
1-4系統調用和庫函數概念?
在開發角度,操作系統對外會表現為一個整體,但是會暴露自身的部分接口,供上層開發使用,這部分由操作系統提供的接口,稱為系統調用。
系統調用在使用上,功能比較基礎,對用戶的要求相對較高,所以,有心的開發者會對部分系統調用做封裝,從而形成庫,有了庫,就很方便上層用戶和開發者進行二次開發。
二,進程(Process)
2-1進程概念與基本操作
概念:進程是指程序的一個執行實例,正在執行的程序等。
內核層面概念:每個進程擁有獨立的內存空間,系統資源和執行狀態,是操作系統進行資源分配和調度和核心對象。
操作系統描述進程的的數據結構—PCB(process control block)
PCB基本概念:進程信息被放在一個叫做進程控制塊的數據結構中,可以理解為進程屬性的集合。
- 在Linux操作系統下的PCB是:task_struct
- task_struct是Linux內核的?種數據結構,它會被裝載到RAM(內存)?并且包含著進程的信息。
進程=內核數據結構+自己的代碼和數據。
進程=task_struct+自己的代碼和數據。?
同一時刻,可能會有多個可執行程序被加載到內存,而操作系統要對這些可執行程序進行管理,?在操作系統層面上,就會在內部會建立一個task_stuct 對象,里面保存了該可執行程序的代碼地址,數據地址等各種信息。將這些task_struct使用鏈表或者其他數據結構管理起來,那么操作系統對進程的管理就轉化為對鏈表的管理。
?2-2task_struct結構體內容
- 標識符:描述本進程的唯一標識符,用來區別其他進程
- 狀態:任務狀態,退出碼,退出信號等。
- 優先級:相對于其他進程的優先級
- 內存指針:包括程序代碼和進程相關數據的指針,還有和其他進程共享的內存塊的指針
- 上下文數據: 進程執?時處理器的寄存器中的數據
- IO狀態信息: 包括顯?的IO請求,分配給進程的IO設備和被進程使用的文件列表。
- 其他信息......
2-3查看進程
1,進程的信息可以通過/proc系統文件夾查看
2,大多數進程信息同樣可以使用top和ps這些用戶及工具來獲取
ps aux? #顯示所有進程信息
top? #動態查看進程資源占用
3,通過系統調用獲取進程標識符?
系統調用
getpid()? ?#當前進程ID(PID)
getppid() #父進程ID(PPID)
?
2-4通過系統調用創建進程?
- fork有兩個返回值
- 父子進程共享代碼,數據格子開辟空間,私有一份(采用寫時拷貝)
- fork之后通常要用if進行分流
創建一個子進程,將父進程的task_struct會拷貝一份給子進程,但是會有部分的數據時需要修改的,比如進程的唯一標識符pid。子進程會共享父進程的代碼數據,相當于發生了淺拷貝。
?pid_t pid = fork();
if (pid == 0) {
? ? // 子進程代碼
} else {
? ? // 父進程代碼
}
三,進程狀態
操作系統中會存在多個進程。每個進程可能會存在不同的狀態,有的進程正在運行,有的進程正在被調度,有的進程處于掛起等等。CPU要執行這些進程,這些進程的task_struct中會保存指向代碼和數據的指針。操作系統會維護一個運行隊列(running queue),存放要運行的task_struct,這個隊列就叫做調度隊列,其中的一個個task_struct就是要被調度的進程。
本質上,進程狀態就是task_struct結構體中的一個變量,用來記錄當前進程的狀態,在操作系統內部通過宏定義的方式,標識不同的狀態。
例如,下面的狀態在Kernel源代碼的定義:
static const char *const task_state_array[] = {
"R (running)", /*0 */
"S (sleeping)", /*1 */
"D (disk sleep)", /*2 */
"T (stopped)", /*4 */
"t (tracing stop)", /*8 */
"X (dead)", /*16 */
"Z (zombie)", /*32 */
};
- R運行狀態(running): 并不意味著進程一定在運行中,它表明進程要么是在運?中要么在運? 隊列里。
- S睡眠狀態(sleeping): 可中斷休眠,淺度睡眠,就是阻塞狀態。該狀態下的進程是可以被殺掉的。
- D(Disk sleep):不可中斷休眠,Disk是磁盤的意思。該狀態下的進程是無法被殺掉的。比如一些進程在進行IO的時候,是不允許被殺掉的。在這個 狀態的進程通常會等待IO的結束。
- T停止狀態(stopped): 可以通過發送 SIGSTOP 信號給進程來停?(T)進程。這個被暫停的 進程可以通過發送 SIGCONT 信號讓進程繼續運行。
- X死亡狀態(dead):這個狀態只是?個返回狀態,你不會在任務列表里看到這個狀態。
- t(trancing stop):追蹤狀態,對我們的程序進行debug調試時的狀態。打端點,進程就被暫停了。
?運行狀態(running)
只要進程在調度隊列中,都處于運行狀態。
阻塞狀態(sleeping)
以scanf為例,當執行scanf從鍵盤讀取數據時,未輸入數據時,操作系統會將當前進程的task_struct從調度隊列中移除,放入設備的阻塞隊列中,此時,該進程就處于阻塞狀態。直到鍵盤響應,會將該進程重新放入調度隊列的尾部,繼續運行。
掛起狀態
在磁盤上,會存在一塊特定的分區,叫做swap分區。當內存資源嚴重不足時,操作系統會將一些不會被調度的進程的代碼和數據交換到磁盤的swap分區,在操作系統內部只保留task_struct部分。此時的狀態稱為掛起狀態。
阻塞掛起狀態:將阻塞隊列中的進程 喚出到磁盤的swap分區上
阻塞運行狀態: 將運行隊列末端的進程喚出到磁盤的swap分區上
z(zombie)僵尸狀態
我們創建子進程的目的,是為了讓子進程完成某種工作的。子進程完成后,父進程需要直到完成的信息。所以子進程在執行完后,必須要等待父進程獲取它的退出信息,獲取之前,該子進程的狀態就是Z狀態,僵尸狀態。
當一個進程結束后,它的代碼和數據就會被釋放,所以它的退出信息只會保存在task_struct中。