一、sigqueue函数
功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。
原型:int sigqueue(pid_t pid, int sig, const union sigval value);
参数:
sigqueue的第一个参数是指定接收信号的进程id,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。
返回值:成功返回0,失败返回-1
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;
sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。
写两个小程序测试一下:
首先是接收信号:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | /************************************************************************* > File Name: process_.c > Author: Simba > Mail: dameng34&#64;163.com > Created Time: Sat 23 Feb 2013 02:34:02 PM CST ************************************************************************/ #include #include #include #include #include #include #include #include #include #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) void handler(int, siginfo_t *, void *); int main(int argc, char *argv[]) { struct sigaction act; act.sa_sigaction &#61; handler; //sa_sigaction与sa_handler只能取其一 //sa_sigaction多用于实时信号&#xff0c;可以保存信息 sigemptyset(&act.sa_mask); act.sa_flags &#61; SA_SIGINFO; // 设置标志位后可以接收其他进程 // 发送的数据&#xff0c;保存在siginfo_t结构体中 if (sigaction(SIGINT, &act, NULL) < 0) ERR_EXIT("sigaction error"); for (; ;) pause(); return 0; } void handler(int sig, siginfo_t *info, void *ctx) { printf("recv a sig&#61;%d data&#61;%d data&#61;%d\n", sig, info->si_value.sival_int, info->si_int); } |
在前面的《信号捕捉与sigaction函数》中说过&#xff0c;sa_sigaction与SA_SIGINFO要配合使用&#xff0c;如上所示&#xff0c;siginfo_t 结构体也可以参见这篇文章。
然后是信号发送&#xff1a;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /************************************************************************* > File Name: process_.c > Author: Simba > Mail: dameng34&#64;163.com > Created Time: Sat 23 Feb 2013 02:34:02 PM CST ************************************************************************/ #include #include #include #include #include #include #include #include #include #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { if (argc !&#61; 2) { fprintf(stderr, "Usage %s pid\n", argv[0]); exit(EXIT_FAILURE); } pid_t pid &#61; atoi(argv[1]); //字符串转换为整数 union sigval val; val.sival_int &#61; 100; sigqueue(pid, SIGINT, val); // 只可以发信号给某个进程&#xff0c;而不能是进程组 return 0; } |
测试如下&#xff1a;
先运行recv程序&#xff1a;
simba&#64;ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigqueue_recv
再ps出recv进程的pid&#xff0c;然后运行send程序&#xff1a;
simba&#64;ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigqueue_send 3323
则recv进程会输出一条recv语句&#xff0c;当然我们也可以ctrl&#43;c 给自己发送信号&#xff0c;如下所示&#xff0c;结果是一样的。
recv a sig&#61;2 data&#61;100 data&#61;100
^Crecv a sig&#61;2 data&#61;100 data&#61;100
^Crecv a sig&#61;2 data&#61;100 data&#61;100
......................................................
需要提醒一下的是siginfo_t 结构体的两个参数&#xff08;int si_int; /* POSIX.1b signal */ void *si_ptr; /* POSIX.1b signal */&#xff09;的值也会与si_value 一致&#xff0c;取决于发送的是sival_int 还是 sival_ptr。
二、实时信号与不可靠信号的区别
下面通过程序来说明区别&#xff0c;主要就是实时信号支持排队不会丢失。&#xff08;实时信号还有一个特点&#xff0c;即到达的顺序是可以保证的&#xff09;
先是recv程序&#xff1a;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include #include #include #include #include #include #include #include #include #include #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) void handler(int); int main(int argc, char *argv[]) { struct sigaction act; act.sa_handler &#61; handler; sigemptyset(&act.sa_mask); act.sa_flags &#61; 0; sigset_t s; sigemptyset(&s); sigaddset(&s, SIGINT); sigaddset(&s, SIGRTMIN); sigprocmask(SIG_BLOCK, &s, NULL); if (sigaction(SIGINT, &act, NULL) < 0) ERR_EXIT("sigaction error"); if (sigaction(SIGRTMIN, &act, NULL) < 0) ERR_EXIT("sigaction error"); if (sigaction(SIGUSR1, &act, NULL) < 0) ERR_EXIT("sigaction error"); for (;;) pause(); return 0; } void handler(int sig) { if (sig &#61;&#61; SIGINT || sig &#61;&#61; SIGRTMIN) printf("recv a sig&#61;%d\n", sig); else if (sig &#61;&#61; SIGUSR1) { sigset_t s; sigemptyset(&s); sigaddset(&s, SIGINT); sigaddset(&s, SIGRTMIN); sigprocmask(SIG_UNBLOCK, &s, NULL); } } |
在主函数中将SIGINT和SIGRTMIN信号加入信号屏蔽字&#xff0c;只有当接收到SIGUSR1信号时才对前面两个信号unblock。需要注意的是如《信号的未决与阻塞》中说的一样&#xff1a;如果在信号处理函数中对某个信号进行解除阻塞时&#xff0c;则只是将pending位清0&#xff0c;让此信号递达一次&#xff08;同个实时信号产生多次进行排队都会抵达&#xff09;&#xff0c;但不会将block位清0&#xff0c;即再次产生此信号时还是会被阻塞&#xff0c;处于未决状态。
接着是send程序&#xff1a;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | /************************************************************************* > File Name: sigrtime_send.c > Author: Simba > Mail: dameng34&#64;163.com > Created Time: Sat 23 Feb 2013 02:34:02 PM CST ************************************************************************/ #include #include #include #include #include #include #include #include #include #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { if (argc !&#61; 2) { fprintf(stderr, "Usage %s pid\n", argv[0]); exit(EXIT_FAILURE); } pid_t pid &#61; atoi(argv[1]); //字符串转换为整数 union sigval val; val.sival_int &#61; 100; sigqueue(pid, SIGINT, val); // 不可靠信号不会排队&#xff0c;即会丢失 sigqueue(pid, SIGINT, val); sigqueue(pid, SIGINT, val); sigqueue(pid, SIGRTMIN, val); //实时信号会排队&#xff0c;即不会丢失 sigqueue(pid, SIGRTMIN, val); sigqueue(pid, SIGRTMIN, val); sleep(3); kill(pid, SIGUSR1); return 0; } |
先是运行recv程序&#xff1a;
simba&#64;ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigrtime_recv2
接着ps出recv进程的pid&#xff0c;运行send程序&#xff1a;
simba&#64;ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigrtime_send 4076
在send程序中连续各发送了SIGINT和SIGRTMIN信号3次&#xff0c;接着睡眠3s后使用kill函数发送SIGUSR1信号给recv进程&#xff0c;此时recv进程会输出如下&#xff1a;
recv a sig&#61;34
recv a sig&#61;34
recv a sig&#61;34
recv a sig&#61;2
即实时信号支持排队&#xff0c;3个信号都接收到了&#xff0c;而不可靠信号不支持排队&#xff0c;只保留一个信号。
参考&#xff1a;《APUE》