1. 程式人生 > >SIGCHID訊號 詳解

SIGCHID訊號 詳解

    1.父程序可以監測子程序的以下三種事件; 每次狀態改變,子程序會發SIGCHID給父程序
        · 子程序終止(即子程序死亡)
        · 子程序停止(即子程序暫停)
        · 子程序恢復(即子程序從暫停中恢復執行)


    2.若sigaction---sigqueue中註冊了SIGCHLD訊號(訊號的傳送和安裝)
        1.sigaction使用了巨集SA_NOCLDSTOP:
            一旦父程序為SIGCHLD訊號設定了這個標誌位,那麼子程序停止和子程序恢復這兩件事情,就不會向父程序傳送SIGCHLD訊號了。 但是子程序切換為SIGCONT時還是會給父程序傳送SIGCHLD訊號。


        2.sigaction使用了巨集SA_NOCLDWAIT:
            如果父程序為SIGCHLD設定了SA_NOCLDWAIT 標誌位,那麼子程序退出時,就不會進入殭屍狀態,而是直接自行了斷。
            對於Linux而言,子程序轉換切換為SIGSTOP.SIGCONT.SIGKILL時都會給父程序傳送SIGCHLD訊號。這點和上面的 SA_NOCLDSTOP 略有不同。


3. 示例程式碼

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>

#define ERR printf("ERR: %d\n", __LINE__)
#define CUR printf("CUR: %d\n", __LINE__)

// signal函式安裝的訊號處理函式 
void sig_child(int signum)
{
	printf("sig: sig_child\n");
}

// 無SA_SIGINFO標誌時,安裝的訊號處理函式
//void sa_handler(int signum)
//{	
	//printf("sig: sa_handler, no SA_SIGINFO\n");
//}
// 有SA_SIGINFO標誌時,安裝的訊號處理函式
static void _handle(int signum, siginfo_t *info, void *ucontext)
{
	printf("sig: sa_handler, have SA_SIGINFO\n");
}

int main(int argc, char **argv)
{
	int ret;
	int signum, cldpid, cnt;
	struct sigaction act;
  	union sigval sigval;

	// 父程序註冊sigchild訊號

	signal(SIGCHLD, sig_child);
	ret = fork();
	if (ret < 0) {
		ERR;
		return -1;
	}
	else if(!ret)	{	
		cldpid = getpid();
		printf("cldpid = %d\n", cldpid);
		ret = fork();
		if (ret < 0) {
			ERR;
			return -1;
		}
		else if(!ret)	{	
			//grandson
			CUR;
			kill(cldpid, SIGSTOP);
			sleep(1);	CUR;
			kill(cldpid, SIGCONT);
			sleep(1);	CUR;
			kill(cldpid, SIGKILL);		// 子程序在接收到這個訊號後終止
			sleep(1);					
			exit(0);		
		}	
		// child
		cnt = 3;
		while(cnt--)		// 捕捉孫程序傳送的3個SIGSTOP.SIGCONT.SIGKILL訊號
			pause();
		exit(0);		
	}
	// parent 
	cnt = 3;
	while(cnt--)		// 監測子程序狀態改變後傳送的SIGCHLD訊號
		pause();
	
	// SA_NOCLDSTOP標誌時, 子程序狀態變為停止和子程序恢復時,不會向父程序傳送SIGCHLD 訊號
	printf("------------------------------------------------\n");
	signal(SIGCHLD, SIG_DFL);
	memset(&act, 0, sizeof(act));
	act.sa_sigaction = (void (*)(int, siginfo_t *, void *))_handle;	// 有SA_SIGINFO標誌時,安裝的訊號處理函式
	act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO | SA_RESTART;	
	sigaction(SIGCHLD, &act, NULL);
	ret = fork();
	if (ret < 0) {
		ERR;
		return -1;
	}
	else if(!ret)	{	
		cldpid = getpid();
		printf("cldpid = %d\n", cldpid);
		ret = fork();
		if (ret < 0) {
			ERR;
			return -1;
		}
		else if(!ret)	{	
			//grandson
			CUR;
			sigval.sival_int = getpid();
			sigqueue(cldpid, SIGSTOP, sigval);		
			sleep(1);	CUR;
			sigqueue(cldpid, SIGCONT, sigval);
			sleep(1);	CUR;
			sigqueue(cldpid, SIGKILL, sigval);		// 子程序在接收到這個訊號後終止
			sleep(1);					
			exit(0);		
		}
		// child
		cnt = 3;
		while(cnt--)		// 捕捉孫程序傳送的3個SIGSTOP.SIGCONT.SIGKILL訊號
			pause();
		exit(0);		
	}
	CUR;
	cnt = 1;
	//cnt = 2;				// 這個會使pause();阻塞, 因為設定了SA_NOCLDSTOP標記, 子程序切換到停止和終止狀態時不會給父程序傳送訊號
	while(cnt--)			// 再次監測子程序狀態改變後傳送的SIGCHLD訊號
		pause();
	CUR;
	
	// SA_NOCLDWAIT標誌時, 子程序狀態變為停止會向父程序傳送SIGCHLD訊號
	printf("------------------------------------------------\n");
	signal(SIGCHLD, SIG_DFL);
	memset(&act, 0, sizeof(act));
	act.sa_sigaction = (void (*)(int, siginfo_t *, void *))_handle;	// 有SA_SIGINFO標誌時,安裝的訊號處理函式
	act.sa_flags = SA_NOCLDWAIT | SA_SIGINFO | SA_RESTART;	
	sigaction(SIGCHLD, &act, NULL);
	ret = fork();
	if (ret < 0) {
		ERR;
		return -1;
	}
	else if(!ret)	{	
		cldpid = getpid();
		printf("cldpid = %d\n", cldpid);
		ret = fork();
		if (ret < 0) {
			ERR;
			return -1;
		}
		else if(!ret)	{	
			//grandson
			CUR;
			sigval.sival_int = getpid();
			sigqueue(cldpid, SIGSTOP, sigval);		
			sleep(1);	CUR;
			sigqueue(cldpid, SIGCONT, sigval);
			sleep(1);	CUR;
			sigqueue(cldpid, SIGKILL, sigval);		// 子程序在接收到這個訊號後終止
			sleep(1);					
			exit(0);		
		}
		// child
		cnt = 3;
		while(cnt--)		// 捕捉孫程序傳送的3個SIGSTOP.SIGCONT.SIGKILL訊號
			pause();
		exit(0);		
	}
	cnt = 3;
	while(cnt--)			// 再次監測子程序狀態改變後傳送的SIGCHLD訊號
		pause();
	while(1);				// 第3個子程序退出後, 自行了斷, 不會變為殭屍程序
	return 0;
}

 

4. 執行結果

[email protected]_hua_shu:/work/nfs_root/qt_fs_new/2system_pro/sig$ cldpid = 11319
CUR: 64
sig: sig_child
CUR: 66
sig: sig_child
CUR: 68
sig: sig_child
------------------------------------------------
CUR: 122
cldpid = 11321
CUR: 106
CUR: 109
CUR: 111
sig: sa_handler, have SA_SIGINFO
CUR: 127
------------------------------------------------
cldpid = 11323
CUR: 151
sig: sa_handler, have SA_SIGINFO
CUR: 154
sig: sa_handler, have SA_SIGINFO
CUR: 156
sig: sa_handler, have SA_SIGINFO

[email protected]_hua_shu:/work/nfs_root/qt_fs_new/2system_pro/sig$
[email protected]_hua_shu:/work/nfs_root/qt_fs_new/2system_pro/sig$ ps
  PID TTY          TIME CMD
10775 pts/12   00:00:00 bash
11318 pts/12   00:00:03 a.out
11319 pts/12   00:00:00 a.out <defunct>
11321 pts/12   00:00:00 a.out <defunct>
11325 pts/12   00:00:00 ps                          // 說明最後一個程序並沒有變為殭屍程序