信号处理三大特性
问题:三种注册信号与处理函数的方法又什么区别?
信号的 OneShot 特性
System V 风格的 signal 函数,注册的信号处理是一次性的
- 进程收到信号后,调用由 signal 注册的处理函数
- 处理函数一旦执行,之后进程通过默认的方式处理后续相同信号
- 如果想要重复触发,那么必须再次调用 signal 注册处理函数
BSD 风格的 siganl 函数不存在 OnetShot,能够自动反复触发处理函数的调用。
大多数 linux 发行版默认风格的 siganl 函数,行为与 BSD 风格一致
信号的自身屏蔽特性
在信号处理函数执行期间,很可能再次收到信号
- 即:处理 A 信号的时候,再次收到 A 信号
对于 System V 风格的 signal 函数,会引起信号处理函数的重入【对于共享资源存在缺陷】
- 即:调用处理函数的过程中,再次触发处理函数的调用(打断执行)
System V 案例分析:
1. 进程收到 A 信号,调用信号处理函数
2. 在第一次的信号处理函数执行过程中(未结束),此时收到第二次 A 信号
3. 第一次 A 信号的信号处理函数停止运行 ,执行第二次 A 信号的处理函数
4. 当第二次 A 信号处理函数执行完成,继续第一次信息处理函数运行
在注册信号处理函数时:
- System V 风格的 signal 不屏蔽任何信号
- BSD 风格的 signal 会屏蔽(排队,不出现打断)当前注册的信号
BSD 案例分析:
1. 进程收到 A 信号,调用信号处理函数
2. 在第一次的信号处理函数执行过程中(未结束),此时收到第二次 A 信号
3. 第二次 A 信号等待
4. 第一次的信号处理函数执行结束,开始执行第二次 A 的信号处理函数
问:BSD 风格的 signal 函数,处理 A 信号期间,如果收到 B 信号会发生什么?
系统调用的重启特性
- 系统调用期间,可能收到信号,此使进程必须从系统调用中返回
- 对于执行较长的系统调用 (write / read), 被信号中断的可能性很大
如果希望信号处理之后,被中断的系统调用能够重启,则可用通过条件 errno == EINTR 判断重启系统调用
信号处理深度实验 - 上
系统调用重入示例代码
pid_t r_wait(int *status)
{
int ret = -1;
// 手工重启系统调用的方案
while ( status &&
((ret = wait(status)) == -1) && // wait 等待子进程结束,当在等待过程中收到信息,信号处理函数被调用,处理返程,主执行流返回,wait 返回 -1
(errno == EINTR)); // 信号处理函数执行完成返回,errno 被赋值为 EINTR
return ret;
}
System V 风格的 signal 函数
- 系统调用被信号中断后,直接返回 -1, 并且 errno == EINTR (需要手动重启)
BSD 风格的 signal 函数
- 系统调用被中断,内核在信号处理函数结束后,自动重启系统调用
大多数 linux 发行版默认风格的 siganl 函数,行为与 BSD 风格一致
编程实验
OneShot 特性
system_v.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig)
{
printf("handler : sig = %d\n", sig);
}
int main(void)
{
sysv_signal(SIGINT, signal_handler);
while (1) {
sleep(1);
}
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
^Chandler : sig = 2 // 键盘 ctrl + c, 信号被捕获
^C // 信号未被捕获,进程结束
bsd.c
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200800L
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig)
{
printf("handler : sig = %d\n", sig);
}
int main(void)
{
bsd_signal(SIGINT, signal_handler);
while (1) {
sleep(1);
}
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
^Chandler : sig = 2 // 信号被持续捕获
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
linux_default.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig)
{
printf("handler : sig = %d\n", sig);
}
int main(void)
{
signal(SIGINT, signal_handler);
while (1) {
sleep(1);
}
return 0;
}
wu_tiansong@ubuntu-server:~/test$ ./a.out
^Chandler : sig = 2 // 信号被持续捕获
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
信号重入实验
system_v.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void delay_handler(int sig)
{
int i = 0;
sysv_signal(SIGINT, delay_handler); // 手工注册信号处理函数,规避 oneshot 特性
printf("begin delay handler ...\n");
for (i=0; i<5; ++i) {
printf("sleep %d\n", i);
sleep(1);
}
printf("end delay handler ...\n");
}
int main(void)
{
sysv_signal(SIGINT, delay_handler);
while (1) {
sleep(1);
}
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
^Cbegin delay handler ...
sleep 0
sleep 1
^Cbegin delay handler ... // 信号处理函数重入
sleep 0
sleep 1
sleep 2
sleep 3
sleep 4
end delay handler ...
sleep 2
sleep 3
sleep 4
end delay handler ...
bsd.c
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200800L
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void delay_handler(int sig)
{
int i = 0;
printf("begin delay handler ...\n");
for (i=0; i<5; ++i) {
printf("sleep %d\n", i);
sleep(1);
}
printf("end delay handler ...\n");
}
int main(void)
{
bsd_signal(SIGINT, delay_handler);
while (1) {
sleep(1);
}
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
^Cbegin delay handler ...
sleep 0
sleep 1
sleep 2
sleep 3
^Csleep 4 // ctrl + c 再次触发信号
end delay handler ...
begin delay handler ...
sleep 0 // 排队,等待当前信号处理完成之后才开始执行
sleep 1
sleep 2
sleep 3
sleep 4
end delay handler ...
linux_default.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void delay_handler(int sig)
{
int i = 0;
printf("begin delay handler ...\n");
for (i=0; i<5; ++i) {
printf("sleep %d\n", i);
sleep(1);
}
printf("end delay handler ...\n");
}
int main(void)
{
signal(SIGINT, delay_handler);
while (1) {
sleep(1);
}
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
^Cbegin delay handler ...
sleep 0
sleep 1
sleep 2
sleep 3
^Csleep 4 // ctrl + c 再次触发信号
end delay handler ...
begin delay handler ...
sleep 0 // 排队,等待当前信号处理完成之后才开始执行
sleep 1
sleep 2
sleep 3
sleep 4
end delay handler ...
信号处理深度实验 - 下 (系统调用重启)
system_v.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
void signal_handler(int sig)
{
printf("handler : sig = %d\n", sig);
}
int r_read(char *data, int len)
{
int ret = -1;
while (data &&
((ret = read(STDIN_FILENO, data, len)) == -1) &&
(errno == EINTR)) {
printf("restart syscall mannually ...\n");
}
if (ret != -1) {
data[ret] = 0;
}
return ret;
}
int main(void)
{
char buf[32] = {0};
sysv_signal(SIGINT, signal_handler);
r_read(buf, 32);
printf("input: %s\n", buf);
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
1234^Chandler : sig = 2
restart syscall mannually ... // 系统调用重启
bsd.c
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200800L
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
void signal_handler(int sig)
{
printf("handler : sig = %d\n", sig);
}
int r_read(char *data, int len)
{
int ret = -1;
while (data &&
((ret = read(STDIN_FILENO, data, len)) == -1)) {
printf("restart syscall mannually ...\n");
}
if (ret != -1) {
data[ret] = 0;
}
return ret;
}
int main(void)
{
char buf[32] = {0};
bsd_signal(SIGINT, signal_handler);
r_read(buf, 32);
printf("input: %s\n", buf);
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
1234^Chandler : sig = 2
1234^Chandler : sig = 2
restart syscall mannually ... 未被打印,证明 bsd 风格会自动重启系统调用
linux_default.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
void signal_handler(int sig)
{
printf("handler : sig = %d\n", sig);
}
int r_read(char *data, int len)
{
int ret = -1;
while (data &&
((ret = read(STDIN_FILENO, data, len)) == -1)) {
printf("restart syscall mannually ...\n");
}
if (ret != -1) {
data[ret] = 0;
}
return ret;
}
int main(void)
{
char buf[32] = {0};
signal(SIGINT, signal_handler);
r_read(buf, 32);
printf("input: %s\n", buf);
return 0;
}
wu_tiansong@ubuntu-server:~/test$ ./a.out
1234^Chandler : sig = 2
1234^Chandler : sig = 2
表现与 bsd 风格一致
restart syscall mannually ... 未被打印,证明 bsd 风格会自动重启系统调用
问题:BSD 风格的 signal 函数,处理 A 信号期间,如果收到 B 信号会发生什么?
答: bsp 风格的信号处理函数,可以防止同一个信号的重入。但是在处理不同信号时,会有重入问题的发生。
main.c
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200800L
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig)
{
printf("handler : sig = %d\n", sig);
}
void delay_handler(int sig)
{
int i = 0;
printf("begin delay handler ...\n");
for (i=0; i<5; ++i) {
printf("sleep %d\n", i);
sleep(1);
}
printf("end delay handler ...\n");
}
int main(void)
{
bsd_signal(40, signal_handler);
bsd_signal(SIGINT, delay_handler);
while (1) {
sleep(1);
}
return 0;
}
test.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char *argv[])
{
int pid = atoi(argv[1]);
int sig = atoi(argv[2]);
printf("send sig(%d) to process (%d)...\n", sig, pid);
kill(pid, sig);
raise(SIGINT);
while (1) {
printf("while...\n");
sleep(1);
}
return 0;
}
终端1,输出:
u_tiansong@ubuntu-server:~/test$ ./a.out
^Cbegin delay handler ...
sleep 0
sleep 1
handler : sig = 40 // 终端 2 此时发送信号,可以看到,信号处理函数立即执行
sleep 2
sleep 3
sleep 4
end delay handler ...
终端2, 输出:
wu_tiansong@ubuntu-server:~/test$ ps -ajx | grep "a.out"
4044972 4045207 4045207 4044972 pts/12 4045207 S+ 1007 0:00 ./a.out
4039252 4045263 4045262 4039252 pts/13 4045262 S+ 1007 0:00 grep --color=auto a.out
wu_tiansong@ubuntu-server:~/test$ ./test.out 4045207 40
send sig(40) to process (4045207)...
注意事项
并非所有的系统调用对信号中断都表现相同行为
一些系统调用支持信号中断后自动重启
read(), write(), wait(), waitpid(), ioctl(), ...
一些系统调用完全不支持中断后自动重启
poll(), select(), usleep(), ...
可通过下面命令查询:man 7 signal
函数 | OneShot | 屏蔽自身 A A | 屏蔽其它 A B | 重启系统调用 |
---|---|---|---|---|
signal(...) | false | true | false | true |
sysv_signal(...) | true | false | false | false |
bsd_signal(...) | false | true | false | true |
在信号处理上,Linux 系统通常更接近 BSD 风格的操作;
默认的 signal 函数在不同的 Linux 发行版上语义可能不同【即不同 Linux 发行版的 signa 可能会有不同表现】,从代码以执行角度,避免直接使用 signal(...) 函数
。
初探现代信号处理
现代信号处理注册函数
#include <signal.h>
int sigaction(int signum, // 信号值
const struct sigaction *act, // 信号处理方式
struct sigaction *oldact); // 原有的处理信号的方式,通常指定为 NULL,不使用
struct sigaction {
void (*sa_handler)(int); // 信号处理函数,简易版本
void (*sa_sigaction)(int, siginfo_t *, void*); // 信号处理函数,复杂版本,可携带更多信息
sigset_t sa_mask; // 描述信号处理期间,屏蔽的信号集合
int sa_flags; // 信号处理特性标志位,例如,OneShot、Restart
void (*sa_restorer)(void); // 废弃不再使用
};
信号屏蔽与标记
sigset_t sa_mask
- 信号屏蔽,
sa_mask = SIGHUP | SIGINT | SIGUSER1;
(说明,在处理信号时,会屏蔽右侧三个信号,直到当前信号处理完成) - 注意:并不是所有信号都可以被屏蔽,如:SIGKILL、SIGSTOP
- 信号屏蔽,
int sa_flags
- 信号特性:
sa_flags = SA_ONESHOT | SA_RESTART;
特殊特性(SA_SIGINFO), 信号处理时能够收到额外的附加信息
- 信号发送也是一种进程间的通讯方式,可通过设置 SA_SIGINFO 完成
- 信号特性:
信号状态小知识
信号产生
- 信号来源,如: SI_KERNEL, SI_USER, SI_TIMER, ...
信号未决
- 从信号产生到信号被接收的状态 (处于未决状态的信号必然是存在的)
信号传送达
- 信号送达进程,被进程接收(忽略,默认处理,自定义处理)
信号屏蔽 VS 信号阻塞
信号屏蔽 (信号未决)
- 信号处理函数执行期间,被屏蔽的信号不会被递送给进程(针对多个信号)
sa_mask = SIGHUP | SIGINT | SIGUSER1;
信号阻塞 (信号未决)
- 信号处理函数执行期间,当前信号不会传递给进程(仅指 ”当前信号“)
sa_flags = SA_RESTART | SA_NODEFER;
[SA_RESTART 支持系统调用自动重启 ; SA_NODEFER 不阻塞当前信号]
sigset_t sa_mask 是一个信号集,在调用该信号捕捉函数之前,将需要block的信号加入这个sa_mask,【仅当信号捕捉函数正在执行时,才阻塞sa_mask中的信号】,当从信号捕捉函数返回时进程的信号屏蔽字复位为原先值。
Q1:这个复位动作是sigaction函数内部处理,还是由调用者自己处理呢?
由sigaction函数自动复位,不用我自己再去处理。
Q2:设置sa_mask的目的?
在调用信号处理程序时就能阻塞某些信号。注意仅仅是在信号处理程序正在执行时才能阻塞某些信号,如果信号处理程序执行完了,那么依然能接收到这些信号。
在信号处理程序被调用时,操作系统建立的新信号屏蔽字包括正被递送的信号,也就是说自己也被阻塞,除非设置了SA_NODEFER。
因此保证了在处理一个给定的信号时,如果这种信号再次发生,通常并不将它们排队,所以【如果在某种信号被阻塞时它发生了5次,那么对这种信号解除阻塞后,其号处理函数通常只会被调用一次】。
Q3:对于不同信号,当信号A被捕捉到并信号A的handler正被调用时,信号B产生了,
3.1如果信号B没有被设置阻塞,那么正常接收信号B并调用自己的信号处理程序。另外,如果信号A的信号处理程序中有sleep函数,那么当进程接收到信号B并处理完后,sleep函数立即返回(如果睡眠时间足够长的话)
3.2如果信号B有被设置成阻塞,那么信号B被阻塞,直到信号A的信号处理程序结束,信号B才被接收并执行信号B的信号处理程序。
如果在信号A的信号处理程序正在执行时,信号B连续发生了多次,那么当信号B的阻塞解除后,信号B的信号处理程序只执行一次。
如果信号A的信号处理程序没有执行或已经执行完,信号B不会被阻塞,正常接收并执行信号B的信号处理程序。
Q4:对于相同信号,当一个信号A被捕捉到并信号A的handler正被调用时,
4.1 又产生了一个信号A,第二次产生的信号被阻塞,直到第一次产生的信号A处理完后才被递送;
4.2 如果连续产生了多次信号,当信号解除阻塞后,信号处理函数只执行一次。
现代信号处理实验
现代信号处理注册示例
int main(void)
{
struct sigaction act = {0};
act.sa_handler = delay_handler;
act.sa_flags = SA_RESTART | SA_NODEFER;
sigaddset(&act.sa_mask, 40);
sigaddset(&act.sa_mask, SIGINT);
sigaction(40, &act, NULL);
sigaction(SIGINT, &act, NULL);
return 0;
}
main.c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig)
{
printf("handler : sig = %d\n", sig);
}
int r_read(char *data, int len)
{
int ret = -1;
while (data &&
((ret = read(STDIN_FILENO, data, len)) == -1)) {
printf("restart syscall mannually ...\n");
}
if (ret != -1) {
data[ret] = 0;
}
return ret;
}
int main(void)
{
char buf[32] = {0};
struct sigaction act = {0};
act.sa_handler = signal_handler;
act.sa_flags = SA_RESTART | SA_ONESHOT;
sigaddset(&act.sa_mask, 40);
sigaddset(&act.sa_mask, SIGINT);
sigaction(40, &act, NULL);
sigaction(SIGINT, &act, NULL);
r_read(buf, 32);
printf("input: %s\n", buf);
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
^Chandler : sig = 2
^C
传统的 sys_v, bsd, 以及默认风格的 signal 函数,都是由 sigaction 实现的
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig)
{
}
int main(void)
{
signal(SIGINT, signal_handler);
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7fff6cce8e00 /* 34 vars */) = 0
brk(NULL) = 0x55ac932af000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffce2952210) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=60964, ...}) = 0
mmap(NULL, 60964, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f038e7c0000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300A\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\207\2631\3004\246E\214d\316\t\30099\351G"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029592, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f038e7be000
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\207\2631\3004\246E\214d\316\t\30099\351G"..., 68, 880) = 68
mmap(NULL, 2037344, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f038e5cc000
mmap(0x7f038e5ee000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f038e5ee000
mmap(0x7f038e766000, 319488, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19a000) = 0x7f038e766000
mmap(0x7f038e7b4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f038e7b4000
mmap(0x7f038e7ba000, 13920, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f038e7ba000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f038e7bf540) = 0
mprotect(0x7f038e7b4000, 16384, PROT_READ) = 0
mprotect(0x55ac9251f000, 4096, PROT_READ) = 0
mprotect(0x7f038e7fc000, 4096, PROT_READ) = 0
munmap(0x7f038e7c0000, 60964) = 0
// 注意这里, sa_mask 屏蔽 SIGINT 信号;sa_flags (SA_RESTORER等同于SA_ONESHOT)自定义信号处理函数只生效一次,系统调用自动重启
rt_sigaction(SIGINT, {sa_handler=0x55ac9251d149, sa_mask=[INT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f038e60f090}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
exit_group(0) = ?
+++ exited with 0 +++
现代信号发送与处理
现代信号发送
#include <signal.h>
int sigqueue(pid_t pid, // 给指定进程发送信号
int sig, // 信号值
const union sigval value); // 信号附带的数据(4字节) [进程间通讯的一种方式]
union sigval {
int sival_int;
void *sival_ptr; // 对于 linux 进程,指针成员无意义 (因为 linux 进程间是相互隔离的)
};
sigqueue(...)
的黄金搭档是sigaction(...)
sa_flags
设置SA_SIGINFO
标志位,可使用三参数信号处理函数
void handler(int sig,
siginfo_t *info, // 信号携带的数据
void *ucontext) // 类型为 ucontext_t * 指针,用于描述执行信号处理函数之前的进程上下文信息;极少数情况会使用该参数
{ }
现代信号处理函数的关键参数
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
union sigval si_value; /* Signal value */ =====>>>>>>>>> 四字节的附带数据
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count;
POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
long si_band; /* Band event (was int in
glibc 2.3.2 and earlier) */
int si_fd; /* File descriptor */
short si_addr_lsb; /* Least significant bit of address
(since Linux 2.6.32) */
void *si_lower; /* Lower bound when address violation
occurred (since Linux 3.19) */
void *si_upper; /* Upper bound when address violation
occurred (since Linux 3.19) */
int si_pkey; /* Protection key on PTE that caused
fault (since Linux 4.6) */
void *si_call_addr; /* Address of system call instruction
(since Linux 3.5) */
int si_syscall; /* Number of attempted system call
(since Linux 3.5) */
unsigned int si_arch; /* Architecture of attempted system call
(since Linux 3.5) */
}
现代信号发送处理示例
main.c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int sig, siginfo_t *info, void *ucontext)
{
printf("handler : sig = %d\n", sig);
printf("handler : info->si_signo = %d\n", info->si_signo);
printf("handler : info->si_code = %d\n", info->si_code);
printf("handler : info->si_pid = %d\n", info->si_pid);
printf("handler : info->si_value = %d\n", info->si_value.sival_int);
}
int main(void)
{
char buf[32] = {0};
struct sigaction act = {0};
act.sa_sigaction = signal_handler;
act.sa_flags = SA_SIGINFO;
sigaction(40, &act, NULL);
sigaction(SIGINT, &act, NULL);
while (1) {
sleep(1);
}
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./a.out
handler : sig = 40
handler : info->si_signo = 40
handler : info->si_code = -1
handler : info->si_pid = 91743
handler : info->si_value = 123456
test.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char *argv[])
{
int pid = atoi(argv[1]);
int sig = atoi(argv[2]);
union sigval sv = {123456};
printf("current pid(%d) ..\n", getpid());
printf("send sig(%d) to process (%d)...\n", sig, pid);
sigqueue(pid, sig, sv);
raise(SIGINT);
while (1) {
printf("while...\n");
sleep(1);
}
return 0;
}
输出:
wu_tiansong@ubuntu-server:~/test$ ./test.out 91483 40
current pid(91743) ..
send sig(40) to process (91483)...
问题:利用信号搞进程间通讯靠谱吗?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。