sigaction函数 和 sigqueue函数(信号的发送和安装)

    [1]sigqueue(发送信号)
        

int sigqueue(pid_t pid, int sig, const union sigval value);
参数: pid:     要发送信号的进程ID
                 sig:      要发送的信号
              value: 发送的伴随数据,该参数的数据类型是联合体
                union sigval {
                    int sival_int;
                    void *sival_ptr;    // 几乎不用(每个进程都有独立的地址空间)
                };

       
            //考虑到不同的进程有各自独立的地址空间,传递指针到另一个进程几乎没有任何意义。因此 sigqueue 函数很少传递指 针( sival_ptr ),大多是传递整型( sival_int )。 

        1.传统的信号多用 signal/kill 这两个函数搭配
        2.signal函数的表达力有限,控制不够精准;所以引入了sigqueue函数来完成实时信号的发送
        3.sigqueue函数也可以发送空信号(信号0)来检查进程是否存在。
        4.和 kill 函数不同的地方在于,它不能通过将pid指定为负值而向整个进程组发送信号。

    [2]    sigaction(安装信号)
      

 int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

        1.参数: signum: 信号编号
          

 1.struct sigaction {    // 第二个参数
                union {
                    void (*sa_handler)(int);//sa_flags里没有设置SA_SIGINFO标记的信号处理函数
                        //sa_flags里设置了SA_SIGINFO标志, 的信号处理函数
                    void handle(int, siginfo_t *info, void *ucontext);
                        // 收到的额外数据保存在siginfo_t->si_value中的si_int和si_ptr中
                }
                sigset_t sa_mask;            // 阻塞信号集
                int sa_flags;                // 标志
                void (*sa_restorer)(void);    // 恢复处理程序
            };      

            2.

siginfo_t {     // handle信号处理函数的第二个参数    
                int si_signo; // 信号的值
                int si_code;  // 信号来源:SI_USER.SI_TKILL.SI_QUEUE.. 
                pid_t si_pid;    // 信号发送进程的进程 ID 。
                uid_t si_uid;   //信号发送进程的真实用户 ID 。
                union sigval si_value; //sigqueue 函数发送信号时所带的伴随数据。
                ...
            }

            3.ucontext是 void* 类型的,其实它是一个 ucontext_t 类型的变量。
                这个结构体提供了进程上下文的信息,用于描述进程执行信号处理函数之前进程所处的状态。通常情况下信号处理函数很少会用到这个变量

            4. sa_flags的含义
                1.SA_NOCLDSTOP
                    一旦父进程为SIGCHLD信号设置了这个标志位,那么子进程停止和子进程恢复这两件事情,就不会向父进程发送 SIGCHLD信号了但是子进程切换为SIGCONT时还是会给父进程发送SIGCHLD信号。

                2.SA_NOCLDWAIT
                    如果父进程为SIGCHLD设置了SA_NOCLDWAIT 标志位,那么子进程退出时,就不会进入僵尸状态,而是直接自行 了断。 对于Linux而言,子进程转换切换为SIGSTOP.SIGCONT.SIGKILL时都会给父进程发送SIGCHLD信号。这点 和上面的 SA_NOCLDSTOP 略有不同。

                3.SA_ONESHOT 和 SA_RESETHAND
                    这两个标志位的本质是一样的,表示信号处理函数是一次性的,信号递送出去之后,信号处理函数便恢复成默认值                       SIG_DFL 。

                4.SA_NODEFER 和 SA_NOMASK
                    这两个标志位的作用是一样的,在信号处理函数执行期间,不阻塞当前信号。

                5.SA_RESTART
                    这个标志位表示,如果系统调用被信号中断,则不返回错误,而是自动重启系统调用

                6.SA_SIGINFO
                    没有设置SA_SIGINFO:
                        跟signal使用方法相同, 使用一个参数的信号处理函数
                        void (*sa_handler)(int);
                    设置了SA_SIGINFO:
                        1.这个标志位表示信号发送者会提供伴随数据。这时使用带3个参数的信号处理函数
                            void handle(int, siginfo_t *info, void *ucontext);    
                        2.能获取到发送进程的PID、UID.信号来源.及发送的额外信息...


        2.注意
            1.对SIGKILL 和 SIGSTOP,不可以为它们安装信号处理函数,也不能屏蔽掉这些信号。
                若通过 sigaction 强行给 SIGKILL 或 SIGSTOP 注册信号处理函数,则会返回-1,并置errno为EINVAL。    


   [3].使用例子

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define ERR printf("ERR: %d\n", __LINE__)
#define CUR printf("CUR: %d\n", __LINE__)
#define SIG_PHYSICAL 	SIGRTMIN+1		// 运动信号
enum physical {
	BASKETBALL = 0x1,	//	篮球
	TENNIS 	   = 0x2,	// 网球
	PINGPONG   = 0x3,	// 乒乓球
};

// 无SA_SIGINFO标志时,安装的信号处理函数
static void handle_(int signum)
{	
	printf("sig: sa_handler, no SA_SIGINFO\n");
}

// 有SA_SIGINFO标志时,安装的信号处理函数
static void _handle(int signum, siginfo_t *info, void *ucontext)
{		CUR;

	switch(info->si_value.sival_int)	{
		case BASKETBALL:
			printf("I will go basketball\n"); break;
		case TENNIS:
			printf("I will go tennis\n");	  break;	
		case PINGPONG:
			printf("I will go pingpong\n");	  break;	
		default:
			return;							  break;
	}
	printf("send pid = %d\n", info->si_pid);
	printf("send si_uid = %d\n", info->si_uid);
	printf("------------------------------\n");
}

void sig_eat(int signum)
{	
	printf("I will go eat\n");
}
int main(int argc, char **argv)
{
	int ret, cldpid;
	struct sigaction act;
	sigset_t block_mask;	// 阻塞信号集
  	union sigval sigval1, sigval2,sigval3;
	
	sigval1.sival_int = BASKETBALL;
	sigval2.sival_int = TENNIS;
	sigval3.sival_int = PINGPONG;

	printf("Usage:\n");
	printf("%s [SA_SIGINFO]\n", argv[0]);
	
	// 注册信号处理函数
	if ((argc > 1) && (!strcmp(argv[1], "SA_SIGINFO"))){
		
		memset(&act, 0, sizeof(act));
		act.sa_sigaction = (void (*)(int, siginfo_t *, void *))_handle;	// 有SA_SIGINFO标志时,安装的信号处理函数
		act.sa_flags = SA_SIGINFO | SA_RESTART;	
		sigaction(SIG_PHYSICAL, &act, NULL);	//安装一个可靠信号
	}
	else {
		memset(&act, 0, sizeof(act));
		act.sa_handler = handle_;	// 无SA_SIGINFO标志时,安装的信号处理函数
		act.sa_flags =  0;	
		sigaction(SIG_PHYSICAL, &act, NULL);	//安装一个可靠信号
	}
	ret = fork();
	if (ret < 0) {
		ERR;
		return -1;
	}
	else if(!ret)	{
		while(1){
			sleep(2);
			cldpid = getpid();
			sigqueue(cldpid, SIG_PHYSICAL, sigval1);		
			sleep(1);	
			sigqueue(cldpid, SIG_PHYSICAL, sigval2);
			sleep(1);	
			sigqueue(cldpid, SIG_PHYSICAL, sigval3);		
			sleep(3);
		}
	}
	while(1)
			pause();
	return 0;
}

 

执行结果:

book@gui_hua_shu:/work/nfs_root/qt_fs_new/2system_pro/sig$ ./a.out
Usage:
./a.out [SA_SIGINFO]
sig: sa_handler, no SA_SIGINFO
sig: sa_handler, no SA_SIGINFO
sig: sa_handler, no SA_SIGINFO
^C

book@gui_hua_shu$ ./a.out SA_SIGINFO
Usage:
./a.out [SA_SIGINFO]
CUR: 36
I will go basketball
send pid = 11627
send si_uid = 1000
------------------------------
CUR: 36
I will go tennis
send pid = 11627
send si_uid = 1000
------------------------------
CUR: 36
I will go pingpong
send pid = 11627
send si_uid = 1000
------------------------------
^C
book@gui_hua_shu$
 

你可能感兴趣的:(第6章,信号)