進程狀態,殭屍進程以及孤兒進程

進程狀態,殭屍進程以及孤兒進程

進程狀態
  • 課本上的進程狀態分爲三種:就緒,阻塞,運行
  • 進程狀態:一個進程的生命週期可以劃分爲一組狀態,這些狀態刻畫了整個進程。進程狀態即體現一個進程的生命狀態。
  • 運行(running)態:進程佔有處理器正在運行。
  • 就緒(ready)態:進程具備運行條件,等待系統分配處理器以便運行。
  • 等待(wait)態:又稱爲阻塞(blocked)態或睡眠(sleep)態,指進程不具備運行條件,正在等待某個事件的完成。
    在這裏插入圖片描述
在Linux下的進程狀態分爲
  • 運行狀態–R:正在運行或者只要拿到時間片就能運行(這種進程會調度到CPU上進行處理);R運行狀態(running): 並不意味着進程一定在運行中,它表明進程要麼是在運行中要麼在運行隊列裏
  • 可中斷休眠狀態(S):當前處於休眠狀態,但是這種休眠可以被打斷;S睡眠狀態(sleeping):意味着進程在等待事件完成(這裏的睡眠有時候也叫做可中斷睡眠
  • 不可中斷休眠狀態(D):當前處於休眠狀態,但是這種休眠不會被打斷;D磁盤休眠狀態(Disk sleep)有時候也叫不可中斷睡眠狀態,在這個狀態的進程通常會等待IO的結束。
  • 可中斷休眠狀態和不可中斷休眠狀態是在幹事情的,他們的事情就是休眠,休眠就是他們的工作,而停止狀態就是真的什麼都不幹
  • 停止狀態(T):什麼都不幹,但是kill是不能把他殺死的,得用kill-9,強制殺死停止狀態的進程;T停止狀態(stopped):可以通過發送SIGSTOP信號給進程來停止(T)進程。這個被暫停的進程可以通過發送 SIGCONT 信號讓進程繼續運行。
  • 僵死狀態(Z):進程已經退出,但是資源沒有完全被釋放時處於的一種狀態,一種等待後續被處理的一種狀態
  • X死亡狀態(dead):這個狀態只是一個返回狀態,你不會在任務列表裏看到這個狀態。
  • kill -9 進程id,作用是強制殺死指定的進程,針對於停止狀態的進程,如果說現在有一個程序,它處於運行的狀態,直接使用kill pid就可以將其殺死了,不用使用kill -9 對其進行強制的殺死
    在這裏插入圖片描述
Z-殭屍進程—就是處於僵死狀態的進程
  • 僵死狀態是一個比較特殊的狀態。當進程退出並且父進程沒有讀取到子進程退出的返回代碼時,就會產生僵死(屍)進程
  • 僵死進程會以終止狀態保持在進程表中,並且會一直在等待父進程讀取退出狀態代碼。
  • 所以,只要子進程退出,父進程還在運行,但父進程沒有讀取子進程狀態,子進程進入Z狀態
  • 殭屍進程是當子進程比父進程先結束,而父進程又沒有回收子進程,釋放子進程佔用的資源,此時子進程將成爲一個殭屍進程。如果父進程先退出 ,子進程被init接管,子進程退出後init會回收其佔用的相關資源
  • 我們都知道進程的工作原理。我們啓動一個程序,開始我們的任務,然後等任務結束了,我們就停止這個進程。 進程停止後, 該進程就會從進程表中移除。你可以通過 System-Monitor 查看當前進程。
  • 在UNIX 系統中,一個進程結束了,但是他的父進程沒有等待(調用wait / waitpid)他, 那麼他將變成一個殭屍進程。 但是如果該進程的父進程已經先結束了,那麼該進程就不會變成殭屍進程, 因爲每個進程結束的時候,系統都會掃描當前系統中所運行的所有進程, 看有沒有哪個進程是剛剛結束的這個進程的子進程,如果是的話,就由Init 來接管他,成爲他的父進程
殭屍進程危害
  • 進程的退出狀態必須被維持下去,因爲他要告訴關心它的進程(父進程),你交給我的任務,我辦的怎麼樣了。可父進程如果一直不讀取,那子進程就一直處於Z狀態
  • 維護退出狀態本身就是要用數據維護,也屬於進程基本信息,所以保存在task_struct(PCB)中,換句話說,Z狀態一直不退出,PCB一直都要維護
  • 那一個父進程創建了很多子進程,就是不回收,是不是就會造成內存資源的浪費?是的!因爲數據結構對象本身就要佔用內存,想想C中定義一個結構體變量(對象),是要在內存的某個位置行開闢空間!
  • 如何避免?—進程等待—父進程一直等在子進程的退出在這裏插入圖片描述
  • 一個用戶可以創建的進程的個數是有限的,如果殭屍進程過多的話,就會導致用戶無法創建新的進程了。在這裏插入圖片描述
    在這裏插入圖片描述
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
   pid_t pid;
   pid = fork();
   if (pid < 0)
   {
       perror("fork error:");
       exit(1);
   }
   else if (pid == 0)
   {
       printf("I am child process.I am exiting.\n");
       exit(0);
   }
   printf("I am father process.I will sleep two seconds\n");
   //等待子進程先退出
   sleep(2);
   //輸出進程信息
   system("ps -o pid,ppid,state,tty,command");
   printf("father process is exiting.\n");
   return 0;
}

return 和exit(0)的區別

在這裏插入圖片描述

殭屍進程的避免

  • 父進程通過wait和waitpid等函數等待子進程結束,這會導致父進程掛起。
  • 如果父進程很忙,那麼可以用signal函數爲SIGCHLD安裝handler,因爲子進程結束後, 父進程會收到該信號,可以在handler中調用wait回收。
  • 如果父進程不關心子進程什麼時候結束,那麼可以用signal(SIGCHLD,SIG_IGN) 通知內核,自己對子進程的結束不感興趣,那麼子進程結束後,內核會回收, 並不再給父進程發送信號。
  • 還有一些技巧,就是fork兩次,父進程fork一個子進程,然後繼續工作,子進程fork一 個孫進程後退出,那麼孫進程被init接管,孫進程結束後,init會回收。不過子進程的回收 還要自己做。
怎樣來清除殭屍進程
  • 改寫父進程,在子進程死後要爲它收屍。具體做法是接管SIGCHLD信號。子進程死後, 會發送SIGCHLD信號給父進程,父進程收到此信號後,執行 waitpid()函數爲子進程收屍。這是基於這樣的原理:就算父進程沒有調用wait,內核也會向它發送SIGCHLD消息,儘管對的默認處理是忽略, 如果想響應這個消息,可以設置一個處理函數。
  • 把父進程殺掉。父進程死後,殭屍進程成爲"孤兒進程",過繼給1號進程init,init始終會負責清理殭屍進程,關機或重啓後所有殭屍進程都會消失。
孤兒進程
  • 父進程先退出,子進程就稱之爲“孤兒進程”
  • 孤兒進程被1號init進程領養,當然要有init進程回收。
  • 在操作系統領域中,孤兒進程指的是在其父進程執行完成或被終止後仍繼續運行的一類進程。這些孤兒進程將被init進程(進程號爲1)所收養,並由init進程對它們完成狀態收集工作。
  • 孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那麼那些子進程將成爲孤兒進程。孤兒進程將被init進程(進程號爲1)所收養,並由init進程對它們完成狀態收集工作。孤兒進程是沒有父進程的進程,孤兒進程這個重任就落到了init進程身上,init進程就好像是一個民政局,專門負責處理孤兒進程的善後工作。每當出現一個孤兒進程的時候,內核就把孤 兒進程的父進程設置爲init,而init進程會循環地wait()它的已經退出的子進程。這樣,當一個孤兒進程淒涼地結束了其生命週期的時候,init進程就會代表黨和政府出面處理它的一切善後工作。因此孤兒進程並不會有什麼危害
  • 但孤兒進程與殭屍進程不同的是,由於父進程已經死亡,系統會幫助父進程回收處理孤兒進程。所以孤兒進程實際上是不佔用資源的,因爲它終究是被系統回收了。不會像殭屍進程那樣佔用ID,損害運行系統。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return 1;
    }
    else if(id == 0)
    {//child
        printf("I am child, pid : %d\n", getpid());
        sleep(10);
    }else
    {//parent
        printf("I am parent, pid: %d\n", getpid());
        sleep(3);
        exit(0);
    }
    return 0;
}
  • 這次讓父進程先退出
    在這裏插入圖片描述
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章