fork和殭屍程序
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; }