fork和殭屍程式

一盞淡酒、醉了夕陽發表於2019-06-29

1. 關於fork

fork()函式:

   用於建立一個程式,所建立的程式複製父程式的程式碼段/資料段/BSS段/堆/棧等所有使用者空間資訊;在核心中作業系統重新為其申請了一個PCB,並使用父程式的PCB進行初始化;

#include <iostream>
#include <unistd.h>
using namespace std;
int val = 10;
int main(int argc, char *argv[])
{
    pid_t pid;
    int lval = 20;
​
    pid = fork();
​
    if(pid == 0){
        val += 2;
        lval += 5;
    }else{
        val -= 2;
        lval += 5;
    }
​
    if(pid == 0){
        cout << "val:" << val << ", lval = " << lval << endl;
    }else{
        cout << "val:" << val << ", lval = " << lval << endl;
    }
    return 0;
}

對於父程式而言,fork()函式返回子程式的ID(子程式的PID);而對於子程式而言,fork函式返回0。

殭屍程式

  父程式建立子程式後,子程式執行到終止時刻(例如,呼叫exit()函式,或者執行到main中的return語句時,都會將返回的值傳遞給 作業系統),此時如果父程式還在執行,子程式並不會立即被銷燬,直到這些值傳到了產生該子程式的父程式。也就是說,如果父程式沒有主動要求獲得子程式的結束狀態值,作業系統就會一直儲存該程式的相關資訊,並讓子程式長時間處於殭屍狀態,例如下面程式:

int main(){
    pid_t pid = fork();
    if(pid == 0){
        cout << "I am a Child Process." <<endl;
    }else{
        cout << "I am a Father Process and Child Process is " << pid << endl;
        sleep(30);  //讓父程式休眠30秒,此時便於觀察子程式的狀態
    }
    if(pid == 0){
        cout << " Child Process exits " << endl;
    }else{
        cout << "Father Process exits " << endl;
    }
    return 0;
}

此時,執行該程式,檢視後臺程式可知(test16是該測試程式的名稱,defunct表示殭屍程式):

gqx@gqx-Lenovo-Product:~$ ps -au
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root       923  0.6  0.9 480840 159824 tty7    Ssl+ 4月09  36:07 /usr/lib/xorg/
root      1351  0.0  0.0  17676  1768 tty1     Ss+  4月09   0:00 /sbin/agetty -
...
gqx      24856  0.0  0.0      0     0 pts/11   Z+   11:03   0:00 [tes16] <defunct>
gqx      24859  0.0  0.0  39104  3300 pts/3    R+   11:03   0:00 ps -au

殭屍程式的消除

方法一:呼叫wait()函式:

/* Wait for a child to die.  When one does, put its status in *STAT_LOC
   and return its process ID.  For errors, return (pid_t) -1.
​
   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern __pid_t wait (__WAIT_STATUS __stat_loc);

成功返回終止的程式ID,失敗返回-1;子程式的最終返回值將指向該函式引數所指向的記憶體空間,但函式所指向的記憶體單元總還含有其他的資訊,需要使用巨集進行分離。

# define WIFEXITED(status)  __WIFEXITED (__WAIT_INT (status))  //子程式正常終止返回"true"
# define WEXITSTATUS(status)    __WEXITSTATUS (__WAIT_INT (status)) //返回子程式的返回值

要注意的是:如果沒有已終止的子程式,那麼程式將被阻塞,直到有子程式終止。

方法二:呼叫waitpid()函式

/* Wait for a child matching PID to die.
   If PID is greater than 0, match any process whose process ID is PID.
   If PID is (pid_t) -1, match any process.
   If PID is (pid_t) 0, match any process with the
   same process group as the current process.
   If PID is less than -1, match any process whose
   process group is the absolute value of PID.
   If the WNOHANG bit is set in OPTIONS, and that child
   is not already dead, return (pid_t) 0.  If successful,
   return PID and store the dead child's status in STAT_LOC.
   Return (pid_t) -1 for errors.  If the WUNTRACED bit is
   set in OPTIONS, return status for stopped children; otherwise don't.
​
   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern __pid_t waitpid (__pid_t __pid, int *__stat_loc, int __options);

第一個引數:如果__pid的值是-1,則與wait()函式相同,可以等待任意的子程式終止;如果是0,則等待程式組識別碼與目前程式相同的任何子程式;如果pid>0,則等待任何子程式識別碼為 pid 的子程式。

第二個引數:與前一個函式wait()的引數意義相同。

第三個引數:常用WNOHANG——若pid指定的子程式沒有結束,則waitpid()函式返回0,不予以等待。若結束,則返回該子程式的ID。

示例程式如下:

#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
using namespace std;
​
void read_childproc(int sig){
    int status;
    pid_t id = waitpid(-1, &status, WNOHANG);
    if(WIFEXITED(status)){
        printf("Remove proc id: %d \n", id);
        printf("Child send: %d \n", WEXITSTATUS(status));
    }
}
​
int main(){
    pid_t pid;
    struct sigaction act;
    act.sa_handler = read_childproc;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGCHLD, &act, 0);
​
    pid = fork();
​
    if(pid == 0){
        puts("Hi, I am a child process!");
        sleep(6);
        return 12;
    }else{
        printf("Child proc id: %d \n", pid);
        pid = fork();
        if(pid == 0){
            puts("Hi, I am a child process!");
            sleep(13);
            exit(24);
        }else{
            int i;
            printf("Child proc id: %d \n", pid);
            for(i  = 0; i < 4; i++){
                puts("wait...");
                sleep(5);
            }
        }
    }
    return 0;
}

  

 

相關文章