什么是進程?
1.概念
程序:編譯好的可執行文件,存放在磁盤上的指令和數據的有序集合。
由此可見程序是靜態的,沒有執行的概念。
進程:是程序的一次執行的過程,是一個可調度的任務,也是執行一個程序所分配的資源的總稱。
由此可見,進程是動態的,包括創建,調度,執行和消亡。
2.特點
(1)系統會為每一個進程分配0-4G的虛擬空間,0-3G是用戶空間,3-4G是內核空間(這是所有進程共用的)
(2)CPU調度進程時會給進程分配時間片(很短幾毫秒),當時間片用完,CPU就會進行其他進程的調度,實現進程的輪轉,從而實現多任務的操作。(沒有外界干預的時候,進程調度是CPU隨機分配的)
(3)當一個程序開始運行的時候,會從占用一部分用戶空間(取決于你的程序),每個程序所搭建的緩存區也是相互獨立的,三個不同的進程就會在他們自己的用戶空間搭建三個不同的緩存區以供標準IO操作。(所以應用層的緩存區不是只有一個,如果一個進程需要進行標準IO,它就會搭建自己的緩存區來使用)
描述任務執行方式
并行:指同時執行多個任務,通常需要多核或多處理器硬件支持。任務真正同時運行,適用于計算密集型場景。
串行:任務按順序依次執行,前一個任務完成后再開始下一個任務。簡單但效率低。
并發:多個任務交替執行,通過時間片輪轉實現“看似同時”(實際還是串行,進程交替快)。適用于I/O密集型場景,單核即可實現。
進程控制塊(了解)
進程控制塊:是操作系統用于管理和描述進程狀態的核心數據結構。每個進程在創建時都會分配一個唯一的PCB,存儲該進程的所有關鍵信息。PCB是進程存在的唯一標識,也是操作系統進行進程調度和資源分配的依據。
PCB通常包含以下信息:
? ? ? ? 1.進程標識信息
????????????????進程ID(PID):唯一標識進程的編號。
????????????????父進程ID(PPID):創建當前進程的父進程標識。
????????????????用戶標識(UID/GID):進程所屬的用戶或組權限信息。
? ? ? ? 2.進程狀態信息
????????????????進程狀態:如運行(Running)、就緒(Ready)、阻塞(Blocked)等。
????????????????程序計數器(PC):指向下一條待執行的指令地址。
????????????????CPU寄存器值:保存進程切換時的上下文(如通用寄存器、棧指針等)。
? ? ? ? 3.資源管理信息
????????????????內存分配:進程的頁表、段表或內存界限寄存器信息。
????????????????文件描述符表:記錄進程打開的文件或I/O設備。
????????????????資源使用統計:如CPU時間、內存占用等。
? ? ? ? 4.調度信息
????????????????優先級:進程的調度優先級。
????????????????等待時間:進程在就緒隊列中的等待時長。
3 進程段
Linux中的進程大致包含三個段:
數據段:存放的是全局變量、常數以及動態數據分配的數據空間(如malloc函數取得的空間)等。
正文段:存放的是程序中的代碼
堆棧段:存放的是函數的返回地址、函數的參數以及程序中的局部變量 (類比內存的棧區)
4 進程分類
交互式進程:該類進程是由shell控制和運行的。交互進程既可以在前臺運行,也可以在后臺運行。該類進程經常與用戶進行交互,需要等待用戶的輸入,當接收到用戶的輸入后,該類進程會立刻響應,典型的交互式進程有:shell命令進程、文本編輯器等
批處理進程:該類進程不屬于某個終端,它被提交到一個隊列中以便順序執行。(目前接觸不到)
守護進程:該類進程在后臺運行。它一般在Linux啟動時開始執行,系統關閉時才結束。
5.進程狀態
查看運行中的進程:ps -aux
查看進程狀態種類:man ps
D: uninterruptible sleep (usually IO) 不可中斷的睡眠態
R: running or runnable (on run queue) 運行態
S: interruptible sleep (waiting for an event to complete) 可中斷的睡眠態
T: stopped by job control signal 暫停態
t: stopped by debugger during the tracing 因為調試而暫停
X: dead (should never be seen) 死亡態
Z: defunct ("zombie") process, terminated but not reaped by its parent 僵尸態
<: high-priority (not nice to other users) 高優先級
N: low-priority (nice to other users) 低優先級
L: has pages locked into memory (for real-time and custom IO) 鎖在內存中
s: is a session leader 會話組組長
l: is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)多線程
+: is in the foreground process group 前臺進程
注:沒有+時,默認是后臺進程
6.進程狀態切換
狀態轉換:
開始:
進程創建后,進程進入就緒態
就緒態->運行態:
????????當CPU調度到此進程時進入運行態(調度器給進程分配時間片)
運行態->就緒態:
????????當時間片用完時,此進程會進入就緒態
? ? ? ? 被高優先級任務搶占
運行態->阻塞態:
????????如果此進程正在執行一些IO操作(阻塞操作)會進入阻塞態,
阻塞態->就緒態:
????????完成IO操作(阻塞結束)后又可進入就緒態,等待CPU的調度
結束:
????????當進程運行結束即進入結束態。
調度器:
操作系統調度器:負責分配CPU時間片給多個進程或線程,常見調度算法包括:
????????先來先服務(FCFS):按任務到達順序執行。
????????最短作業優先(SJF):優先執行預計耗時最短的任務。
????????時間片輪轉(RR):每個任務分配固定時間片,超時后輪換。
????????多級反饋隊列(MLFQ):結合優先級和時間片的動態調整算法。
分布式系統調度器:如Kubernetes的調度器,將Pod分配到合適的節點,考慮資源需求、親和性等約束。
7 進程相關命令
ps 查看系統中的進程-ax -ef
top動態顯示系統中運行的進程
renice 改變正在運行進程的優先級
nice 以指定優先級啟動進程
fg 將進程切換到前臺執行
bg 將進程切換到后臺執行
jobs 查看當前終端后臺的進程
kill 給進程發送信號
詳細命令可以查看Linux命令和Shell命令(LinuxC語言高級)-CSDN博客
補充:優先級調度
是一種基于任務優先級分配CPU資源的算法,高優先級任務優先執行。
兩種類型:
1.非剝奪式(非搶占式)優先級調度算法。當一個進程正在處理上運行時,即使有某個更為重要或緊迫的進程進入就緒隊列,仍然讓正在進行的進程繼續運行,直到由于其自身原因而主動讓出處理機(任務完成或等待事件),才把處理機分配給更為重要或緊迫的進程。
2.剝奪式(搶占式)優先級調度算法。當一個進程正在處理機上運行時,若有某個更為重要或緊迫的進程進入就緒隊列,則立即暫停正在運行的進程,將處理機分配給更重要或緊迫的進程。
進程函數接口
1 創建進程 fork()
函數:
pid_t fork(void);
頭文件:? ?? ? ?
????????#include <sys/types.h>
? ? ? ? #include <unistd.h>功能:創建子進程
返回值:
????????成功:在父進程中:返回子進程的進程號 >0
? ? ? ? ? ? ? ? ? ?在子進程中:返回值為0
????????失敗:-1并設置errno
例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{int pid = fork(); //創建一個子進程并且接收子進程PID號if(pid < 0){perror("child fork err");return -1;}else if(pid == 0){//getpid是獲取當前進程的PIDprintf("父進程PID:%d 子進程PID:%d\n",getpid(),pid); //while(1);}else{printf("父進程PID:%d 子進程PID:%d\n",getpid(),pid); //while(1);}return 0;
}
為什么會輸出兩個結果?if判斷不是只執行一條語句嗎?
當你運行這個程序的時候會啟動一個進程(父進程),執行到fork()函數時會在當前進程中創造了一個子進程并把代碼以及數據信息拷貝到子進程,這兩個進程只有個別數據例如進程號不一樣,此時這兩個進程由CPU隨機調度。注意!!子進程會得到fork函數返回值然后執行fork之后的代碼,fork函數之前的代碼不會執行。
特點:
1)子進程幾乎拷貝了父進程的全部內容。包括代碼、數據、系統數據段中的pc值、棧中的數據、父進程中打開的文件等;但它們的PID、PPID是不同的。???????
2)父子進程有獨立的地址空間,互不影響;當在相應的進程中改變全局變量、靜態變量,都互不影響。
3)若父進程先結束,子進程成為孤兒進程,被init進程收養,子進程變成后臺進程(R)。
4)若子進程先結束,父進程如果沒有及時回收資源,子進程變成僵尸進程(要避免僵尸進程(Z+)產生)
使用kill -9 PID(你要殺死的進程PID)結束進程
思考一下:
以下程序出來的b是什么值:
子進程的b是3,因為子進程繼承的是父進程創建子進程前的所有變量,父進程開始運行后改變b值,子進程是不變的,已經繼承結束了。
2 回收資源
僵尸態:僵尸態通常指計算機系統中進程或線程因異常終止而無法正常回收資源的狀態。這類進程雖已停止運行,但仍占用系統資源(如內存、進程表項),導致資源泄漏或系統性能下降。
pid_t wait(int *status);
頭文件:#include <sys/types.h>
? ? ? ? ? ? ??#include <sys/wait.h>功能:
阻塞父進程:父進程暫停執行,直到?任一子進程?終止。
回收子進程資源:釋放子進程的進程控制塊,避免僵尸進程。
獲取子進程狀態:通過返回值或參數傳遞子進程的退出狀態(正常終止、信號終止等)。
參數:status:子進程退出狀態,不接受子進程狀態設為NULL
返回值:成功:回收的子進程的進程號
??????????????失敗:-1
pid_t waitpid(pid_t pid, int *status, int options);
功能:回收子進程資源
頭文件:#include <sys/types.h>
? ? ? ? ? ? ??#include <sys/wait.h>參數:
????????pid:>0 指定子進程進程號
????????????????=-1 任意子進程
????????????????=0 等待其組ID等于調用進程的組ID的任一子進程
????????????????<-1 等待其組ID等于pid的絕對值的任一子進程
????????status:子進程退出狀態
????????options:0:阻塞 WNOHANG:非阻塞
返回值:正常:結束的子進程的進程號
? ? ? ? ? ? ? ? ? ? ? ? ?當使用選項WNOHANG且沒有子進程結束時:0
??????????????出錯:-1
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main(int argc, const char *argv[])
{int pid = fork();if(pid < 0){perror("fork err");return -1;}else if(pid == 0){printf("%d %d\n",getpid(),pid);//while(1);sleep(5);}else{//int pid1 = wait(NULL);//int pid1 = waitpid(-1,NULL,0); //父進程阻塞,和wait(NULL)作用一樣 //int pid1 = waitpid(pid,NULL,WNOHANG); //父進程不阻塞,這里就不等待子進程結束 int pid1 = 0;while(1){ //當使用循環的時候,作用就和阻塞效果一樣,直到子進程結束才回退出循環pid1 = waitpid(pid,NULL,WNOHANG);if(pid1 > 0){ //只有當進程pid結束才會返回大于零的數break;}}printf("父進程:%d 子進程:%d 回收的子進程:%d\n",getpid(),pid,pid1);while(1);}return 0;
}
3 結束進程
void exit(int status);
頭文件:#include <stdlib.h>
??????????????#include <unistd.h>
功能:結束進程,刷新緩存
exit(0)
?表示程序正常退出。exit(-1)
?或其他非零值表示程序異常退出。具體數值可根據需求定義,但非零值通常表示錯誤。void _exit(int status);
頭文件:#include <stdlib.h>
??????????????#include <unistd.h>
功能:結束進程,不刷新緩存
參數:status是一個整型的參數,可以利用這個參數傳遞進程結束時的狀態。
? ? ? ? ? ?通常0表示正常結束;
? ? ? ? ? ?其他的數值表示出現了錯誤,進程非正常結束
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main(int argc, const char *argv[])
{int pid = fork(); //創建子進程if(pid < 0){ //容錯判斷perror("fork err");exit(-1); //程序異常退出}if(pid == 0){ //子進程進入printf("%d %d\n",getpid(),pid);//exit(0); //子進程終止,刷新緩存_exit(0); //子進程終止,但是不刷新緩存while(1);}if(pid > 0){waitpid(pid,NULL,0);printf("%d %d\n",getpid(),pid);}return 0;
}
4 獲取進程號
pid_t getpid(void);
頭文件:#include <sys/types.h>
? ? ? ? ? ? ? #include <unistd.h>功能:獲取當前進程的進程號
pid_t getppid(void);
頭文件:#include <sys/types.h>
? ? ? ? ? ? ? #include <unistd.h>功能:獲取當前進程的父進程號
之前已經用過了就不做練習了。一定要多敲代碼多練,紙上得來終覺淺。