1.進程
1.1基本概念
課本概念 :進程是程序的一個執行實例,是正在執行的程序。當程序被執行時,系統會為其創建一個進程,包含程序代碼、數據以及運行時所需的資源。
內核觀點 :進程是擔當分配系統資源(CPU 時間、內存)的實體。操作系統內核通過進程來管理和調度系統資源,確保每個進程都能合理地使用 CPU、內存等硬件資源。
1.2 描述進程-PCB?
1.2.1 基本概念
進程控制塊(PCB) :進程的信息被存放在一個名為進程控制塊的數據結構中,它是進程屬性的集合,就好比一個檔案袋,里面裝著進程的各種信息,比如進程的狀態、優先級、內存分配情況等。
Linux 中的 PCB(task_struct
) :在 Linux 操作系統下,PCB 是通過 task_struct
這個結構體來實現的。
1.2.2 task_struct
?相關內容
task_struct 的定義:task_struct
是 Linux 內核中定義的一個復雜的數據結構,包含在內核源代碼的 include/linux/sched.h
文件中。它是內核用來表示和管理進程的核心數據結構,包含進程的各種信息,如進程標識符、狀態、內存管理信息、I/O 信息、調度參數等。
task_struct
的作用 :用于在 Linux 中描述進程的結構體,它承載著進程相關的各類信息,是內核管理和調度進程的關鍵依據。
存儲位置 :task_struct
是 Linux 內核的一種數據結構,會被裝載到 RAM(內存)里,這樣內核可以快速地訪問和修改進程的信息,以實現對進程的有效管理,比如進程的創建、切換、銷毀等操作都依賴于對 task_struct
的操作。
?1.2.3 內容分類
1)標示符
作用 :用于唯一標識一個進程,區分系統中的不同進程。
具體信息 :通常包括進程 ID(PID)、父進程 ID(PPID)等信息。
2)狀態
作用 :反映進程當前的執行狀態,以及進程退出時的相關信息。
具體信息 :包括進程是處于運行態、就緒態、等待態等,還有進程的退出代碼(用于表示進程正常結束或異常終止的原因)、退出信號(用于通知其他進程該進程已結束)等。
3)優先級
作用 :決定進程調度的優先順序,優先級高的進程更容易獲得 CPU 資源進行執行。
具體信息 :包括動態優先級和靜態優先級,不同的進程調度算法會根據優先級來安排進程的執行順序。
4)程序計數器
作用 :指示處理器當前正在執行的指令位置,控制程序的執行流程。
具體信息 :指向進程在程序中即將被執行的下一條指令的內存地址,當進程被調度執行時,處理器根據程序計數器的值來獲取并執行相應的指令。
5)內存指針
作用 :描述進程在內存中的布局和內存資源的使用情況,以及與其他進程共享內存的區域
具體信息 :包括指向進程的程序代碼段、數據段、堆、棧等內存區域的指針。還有指向與其他進程共享的內存塊的指針,用于實現進程間通信(IPC)和共享資源等功能。
6)上下文數據
作用 :保存進程執行時處理器的寄存器狀態,用于在進程切換時恢復進程的執行環境。
具體信息 :包括處理器的各種寄存器(如通用寄存器、控制寄存器、狀態寄存器等)的值,當進程被中斷或切換時,系統會保存當前的上下文數據,以便在該進程再次被調度執行時能夠恢復到之前的狀態繼續執行。
7)I/O 狀態信息
作用 :記錄進程的 I/O 操作相關信息,方便系統進行 I/O 管理和調度。
具體信息 :包括進程已發出的 I/O 請求、分配給進程的 I/O 設備(如磁盤、網絡設備等)以及進程打開和使用的文件列表等。
8)記賬信息
作用 :用于記錄進程的資源使用情況,可用于系統資源管理和計費等目的。
具體信息 :包括進程使用的處理器時間總和(如用戶態和內核態的 CPU 時間)、使用的時鐘數總和、時間限制(如超時時間)、記賬號等。
9)其他信息
作用 :包含一些其他與進程相關的信息,這些信息可能因不同的內核版本或特定的系統配置而有所不同。
具體信息 :例如進程的創建時間、所屬的用戶和組、安全上下文信息(在支持安全模塊的系統中)等。
1.3 進程的查看
1.3.1? ?PID與PPID
1) PID(Process ID)
-
定義
-
PID 是操作系統分配給每個進程的唯一標識符,用于區分系統中的不同進程,是進程存在的標志。
-
-
作用
-
唯一標識進程 :在 Linux 系統中,每個進程都有一個唯一的 PID,通過 PID 可以準確地識別和操作特定的進程。
-
進程管理 :系統和用戶可以根據 PID 對進程進行管理,如發送信號、終止進程、獲取進程信息等。
-
資源分配 :操作系統利用 PID 來管理進程的資源分配,如內存、CPU 時間等。
-
-
范圍
-
在 Linux 系統中,PID 是一個正整數。傳統的 PID 范圍是 1 到 32768,但具體范圍可能因系統配置而有所不同。可以通過查看
/proc/sys/kernel/pid_max
文件來確定系統中 PID 的最大值。
-
2) PPID(Parent Process ID)
-
定義
-
PPID 是創建該進程的父進程的 ID。每個進程(除了 init 進程)都有一個父進程,PPID 用于標識這個父進程。
-
-
作用
-
父子進程關系 :PPID 建立了進程之間的父子關系,有助于理解進程的層次結構和來源。例如,通過查看 PPID 可以知道某個進程是由哪個父進程啟動的。
-
進程管理 :在進程管理中,父進程可以利用 PPID 來管理和控制其子進程,如等待子進程結束、獲取子進程狀態等。
-
孤兒進程處理 :當父進程終止時,其子進程將成為孤兒進程。系統會將孤兒進程的 PPID 改為 1,由 init 進程(PID 為 1)收養這些孤兒進程,確保它們能夠正常結束?
-
3)?創建子進程的方式
在 Linux 系統中,父進程通過 fork()
系統調用創建子進程。
fork()
的功能是創建一個與父進程幾乎完全相同的子進程。子進程會復制父進程的地址空間、環境變量、打開的文件等。
1.3. 2?查看進程的可執行文件
1)使用 /proc
文件系統:
/proc
文件系統提供了關于進程的信息,每個進程在 /proc
中有一個以 PID 命名的目錄。可以使用 ls -l /proc/<PID>/exe
查看進程對應的可執行文件的鏈接。?
2)示例:
假設有一個名為 myprocess
的進程,其 PID 為 1234,可以使用以下命令查看其可執行文件:
ls -l /proc/1234/exe
輸出可能如下:
lrwxrwxrwx 1 root root 0 Mar 31 20:03 /proc/1234/exe -> /home/user/bin/myprocess
這表明進程 1234 的可執行文件位于 /home/user/bin/myprocess
。
1.3.3 查看進程及父進程
使用?getpid
?和?getppid
?函數
-
功能:
-
getpid
函數返回當前進程的 ID(PID)。 getppid
函數返回當前進程的父進程 ID(PPID)。
?函數原型:
#include <sys/types.h>
#include <unistd.h>pid_t getpid(void);
pid_t getppid(void);
?應用:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main() {pid_t pid = getpid(); // 獲取當前進程的 PIDpid_t ppid = getppid(); // 獲取當前進程的 PPIDprintf("Current process ID: %d\n", pid);printf("Parent process ID: %d\n", ppid);return 0;
}
?最后附上老師板書:
1.3.4 fork
?1)?基本概念
fork
的作用是創建一個新進程,這個新進程稱為子進程,而調用 fork
的進程稱為父進程。子進程是父進程的副本,它繼承了父進程的幾乎所有資源和狀態。
2)工作原理
復制父進程資源
當 fork
被調用時,操作系統會創建一個新進程,并將父進程的代碼段、數據段、堆、棧等資源復制到子進程中。子進程在創建時幾乎與父進程完全相同,包括代碼、變量、文件描述符、環境變量等。但是,子進程有自己獨立的進程標識符(PID),并且它與父進程是兩個獨立的進程實體
寫時拷貝
現代操作系統通常采用“寫時復制”技術來優化 fork
的性能。在 fork
調用時,操作系統并不會立即復制父進程的所有資源,而是將父進程的資源標記為“共享”。只有當父進程或子進程對這些資源進行寫操作時,操作系統才會真正復制被修改的部分資源,從而避免了不必要的復制操作,節省了時間和內存。
3)?返回值
fork
的返回值用于區分父進程和子進程
在父進程中
fork
返回子進程的進程標識符(PID),這是一個正整數。父進程可以通過這個 PID 來監控子進程的狀態,例如使用 wait
或 waitpid
等系統調用等待子進程結束。
在子進程中
fork
返回 0。子進程可以通過返回值為 0 來判斷自己是子進程,并執行相應的邏輯。
出錯時
如果 fork
調用失敗(例如系統資源不足或超出進程限制),它會返回 -1,并設置錯誤碼 errno
。
?4)代碼示例
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main()
{pid_t pid = fork(); // 調用 fork 創建子進程if (pid < 0) {// fork 失敗perror("fork failed");return 1;}else if (pid == 0) {// 子進程printf("I am the child process, my PID is %d\n", getpid());} else {// 父進程printf("I am the parent process, my PID is %d, my child's PID is %d\n", getpid(), pid); wait(NULL); // 等待子進程結束}return 0;
}
假設父進程的 PID 是 1234,子進程的 PID 是 1235,程序的輸出可能是:
?也許此時會有人疑惑,為什么if和else都進去了??難道是程序出錯了嗎?還是說我們碰到了編程領域的量子力學了??實際上,這里是多線程編程,可以同時都進的。
?5)代碼詳解
fork
調用后,程序會分為兩部分繼續執行:一部分在父進程中運行,另一部分在子進程中運行。因此,if
和 else
語句分別對應父進程和子進程的邏輯。
當程序執行到 pid_t pid = fork();
時,操作系統會創建一個子進程。在調用 fork
之前,父進程和子進程是同一個進程,共享相同的代碼和數據。調用 fork
之后,程序會分為兩部分繼續執行:父進程和子進程。
在父進程中,fork
返回子進程的進程標識符(PID),這是一個正整數。因此,pid > 0
,程序會進入 else
分支。
在子進程中,fork
返回 0。因此,pid == 0
,程序會進入 else if
分支。
實際上,if
和 else
并不是同時進入的,而是分別在父進程和子進程中執行不同的分支。
父進程 執行 else
分支。子進程 執行 else if
分支。
從程序的整體運行角度來看,if
和 else
的邏輯分別在兩個不同的進程中執行,因此看起來像是“都進去了”,但實際上它們是在不同的上下文中運行的。
有一點值得注意的是:當我們創建出子進程后,父子兩個進程,誰先運行不確定,具體由OS的調度原則來確定。