目錄
進程狀態
Z(zombie)-殭屍進程
殭屍進程的核心危害
解決方法(按實用優先級排序)
孤兒進程
進程優先級
基本概念
查看系統進程
PRI and NI
查看進程優先級的命令
其他概念
進程狀態
在Linux內核裏,進程有時候也叫做任務
- R運行狀態(running): 並不意味着進程一定在運行中,它表明進程要麼是在運行中要麼在運行隊列裏
- S睡眠狀態(sleeping): 意味着進程在等待事件完成(這裏的睡眠有時候也叫做可中斷睡眠(interruptible sleep))
- D磁盤休眠狀態(Disk sleep)有時候也叫不可中斷睡眠狀態(uninterruptible sleep),在這個狀態的進程通常會等待IO的結束。
- T停止狀態(stopped): 可以通過發送 SIGSTOP 信號給進程來停止(T)進程。這個被暫停的進程可以通過發送 SIGCONT 信號讓進程繼續運行。
- X死亡狀態(dead):這個狀態只是一個返回狀態,你不會在任務列表裏看到這個狀態
Z(zombie)-殭屍進程
僵死狀態(Zombies)是一個比較特殊的狀態。
- 當進程退出並且父進程(使用wait()系統調用)沒有讀取到子進程退出的返回代碼時就會產生僵死(屍)進程
- 僵死進程會以終止狀態保持在進程表中,並且會一直在等待父進程讀取退出狀態代碼。
- 所以,只要子進程退出,父進程還在運行,但父進程沒有讀取子進程狀態,子進程進入Z狀態
#include <stdio.h>
#include <stdlib.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id > 0){ //parent
printf("parent[%d] is sleeping...\n", getpid());
sleep(30);
}else{
printf("child[%d] is begin Z...\n", getpid());
sleep(5);
exit(EXIT_SUCCESS);
}
return 0;
}
殭屍進程的核心危害
- 佔用PID資源:系統PID數量有限(默認通常32768個),殭屍進程累積會耗盡PID,導致無法創建新進程。
- 浪費系統資源:雖不佔用CPU/內存,但會留存進程表項(包含PID、退出狀態等信息),消耗內核資源。
- 阻塞父進程等待:若父進程未處理子進程退出狀態,可能因持續等待導致自身阻塞
解決方法(按實用優先級排序)
- 父進程主動等待:用 wait() 或 waitpid() 獲取子進程退出狀態
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
printf("子進程 ID: %d,父進程 ID: %d\n", getpid(), getppid());
exit(EXIT_SUCCESS);
} else {
printf("父進程 ID: %d,創建的子進程 PID: %d\n", getpid(), pid);
wait(NULL);
printf("子進程已退出\n");
}
return 0;
}
- 註冊SIGCHLD信號處理函數:子進程退出時內核發SIGCHLD,父進程捕獲後調用 wait() ,適合多子進程場景
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
void sigchld_handler(int signum) {
pid_t pid;
while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
printf("子進程 %d 已退出\n", pid);
}
}
int main() {
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGCHLD, &sa, NULL);
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
printf("子進程 ID: %d,父進程 ID: %d\n", getpid(), getppid());
sleep(1);
exit(EXIT_SUCCESS);
} else {
printf("父進程 ID: %d,創建的子進程 PID: %d\n", getpid(), pid);
while (1) {
// 模擬父進程繼續執行其他任務
sleep(1);
}
}
return 0;
}
- 父進程先於子進程退出:父進程退出後,子進程會被init(PID=1)接管,init會自動回收子進程資源。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
printf("子進程 ID: %d,父進程 ID: %d\n", getpid(), getppid());
sleep(3);
printf("此時父進程已退出,我的新父進程 ID: %d\n", getppid());
exit(EXIT_SUCCESS);
} else {
printf("父進程 ID: %d,創建的子進程 PID: %d\n", getpid(), pid);
exit(EXIT_SUCCESS);
}
return 0;
}
- 使用 fork() 兩次:子進程創建孫進程後立即退出,孫進程成為孤兒進程被init接管,避免子進程變成殭屍。
孤兒進程
- 父進程先退出,子進程就稱之為“孤兒進程”
- 孤兒進程被1號init進程領養,當然要有init進程回收
進程優先級
基本概念
- cpu資源分配的先後順序,就是指進程的優先權(priority)。
- 優先權高的進程有優先執行權利。
- 配置進程優先權對多任務環境的linux很有用,可以改善系統性能。還可以把進程運行到指定的CPU上,這樣一來,把不重要的進程安排到某個CPU,可以大大改善系統整體性能。
查看系統進程
在linux或者unix系統中,用ps –l命令則會類似輸出以下幾個內容
- UID : 代表執行者的身份
- PID : 代表這個進程的代號
- PPID :代表這個進程是由哪個進程發展衍生而來的,亦即父進程的代號
- PRI :代表這個進程可被執行的優先級,其值越小越早被執行
- NI :代表這個進程的nice值
PRI and NI
- PRI即進程的優先級,或者説就是程序被CPU執行的先後順序,此值越小進程的優先級別越高
- NI是nice值,其表示進程可被執行的優先級的修正數值
- PRI值越小越快被執行,那麼加入nice值後,將會使得PRI變為:PRI(new)=PRI(old)+nice
- 這樣,當nice值為負值的時候,那麼該程序將會優先級值將變小,即其優先級會變高,則其越快被執行所以,調整進程優先級,在Linux下,就是調整進程nice值
- nice其取值範圍是-20至19,一共40個級別。
查看進程優先級的命令
用top命令更改已存在進程的nice:
top
進入top後按“r”–>輸入進程PID–>輸入nice值
nice 命令(查看/設置新進程的默認優先級)
查看默認nice值(未指定時為0)
nice
renice 命令(調整已運行進程的優先級)
將PID為1234的進程nice值改為5(降低優先級)
renice 5 -p 1234
其他概念
- 競爭性: 系統進程數目眾多,而CPU資源只有少量,甚至1個,所以進程之間是具有競爭屬性的。為了高效完成任務,更合理競爭相關資源,便具有了優先級
- 獨立性: 多進程運行,需要獨享各種資源,多進程運行期間互不干擾
- 並行: 多個進程在多個CPU下分別,同時進行運行,這稱之為並行
- 併發: 多個進程在一個CPU下采用進程切換的方式,在一段時間之內,讓多個進程都得以推進,稱之為併發