42-带参数的信号
来源:互联网 发布:天天卡牌淘宝杂货铺 编辑:程序博客网 时间:2024/05/17 01:48
前面不管我们是使用 signal 信号注册函数还是 sigaction 信号注册函数,我们都只注册了带一个参数的信号处理函数 void handler(int sig)
。
实际上,我们也可以使用带参数的的信号处理函数。signal 函数没办法注册一个带附加参数的信号处理函数,但是 sigaction 可以。具体是通过 sigaction 的第二个参数 struct sigaction 结构体来指定带附加参数的信号处理函数。
1. 回忆 struct sigaction 结构体
1.1 结构体
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);};
实际上,在某些系统的实现中, sa_handler 与 sa_sigaction 是以联合体的方式实现的,类似下面这样:
struct sigaction { union { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); }_u; sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);};
所以我们在使用的时候,只要给其中某一个成员赋值就行了,如果你给两个成员都赋值,会导致另一个被覆盖。
1.2 如何给信号处理函数传递参数
在成员 sa_flags 上加上选项——SA_SIGINFO.
需要注意的是,sa_flags 加上选项 SA_SIGINFO 的含义仅仅是表明:在处理信号的时候,会附带一个 siginfo_t* 类型的参数。它并不表明使用该选项了就必须得 sa_sigaction 成员赋值,换句话说,即使你使用不带参数的信号处理函数,你也可以给 sa_flags 加上 SA_SIGINFO 选项。只不过这样做没什么用。
同样的,就算你不指定 SA_SIGINFO 选项,你也一样可以使用带附加参数的 sa_sigaction。
总之一句话,SA_SIGINFO 仅仅表示在处理信号的时候会附加一个 siginfo_t* 类型的参数(至于你用哪种信号处理函数,无所谓)。
1.3 sa_sigaction 成员
sa_sigaction 成员是一个函数指针。它指示的函数原型必须是下面这样:
void fun(int sig, siginfo_t *siginfo, void *context); // 函数是什么名字无所谓
该函数的第一个参数表示处理的是哪个信号,第二个参数是一个结构体,第三个参数实际上类型为 ucontext_t 类型的指针,使用的时候应该把 void* 转换为 ucontext_t*,它表示发送进程在发送信号时的上下文,这个参数目前来说没什么用,我们不用理会。这样一来,重心就在 siginfo_t 这个结构体上了。
2 struct siginfo_t 结体体
这个结构体成员众多,不过我们仅仅关心其中的三个值。如果你想看完整版本的,请看文末附录。
- 简化版
struct siginfo_t { pid_t si_pid; /* 发送信号的进程 ID */ uid_t si_uid; /* 发送信号的进程实际用户 ID */ sigval_t si_value; /* 附加参数(联合体) */ int si_int; /* 实际上这个参数的值就是 si_value,他们相等 */ void *si_ptr; /* 同上 */}union sigval_t { int sival_int; void *sival_ptr;};
如果一来,我们需要关心的成员数量就减少很多了,便于学习。值得注意的是 si_value 成员、si_int 和 si_ptr 这几个成员,这些成员实际上由用户在发送信号的时候传递的。而且,si_value 的值,和 si_int,si_ptr 的值是完全一致的,后面的实验可以验证。
2.1 新的信号发送函数 sigqueue
可能你比较关心的是用户如何发送信号的时候传递参数到 siginfo_t 结构体中的 si_value?我们学习的 kill 函数并不支持这个功能啊?没关系,linux 系统提供了另一个信号发送函数 sigqueue 帮助我们解决这个问题,它的原型如下:
int sigqueue(pid_t pid, int sig, const union sigval value);
它的用法和 kill 函数一样,只不过多一个参数,上面这个参数类型 sigval 其实就是 sigval_t,没有任何区别。
union sigval { int sival_int; void *sival_ptr;};
3. 实例
下面一共有两个程序,分别是 a 和 b。
程序 a 的功能就是使用带附加参数的信号处理函数,然后打印所有附加参数的值。
程序 b 的功能就是给程序 a 发信号,同时附带一个整数。
3.1 程序 a
// a.c#include <unistd.h>#include <signal.h>#include <stdio.h>void handler(int sig, siginfo_t* siginfo, void* context) { if (sig == SIGQUIT) printf("hello SIGQUIT\n"); if (siginfo) { printf("si_signo = %d\n", siginfo->si_signo); printf("si_errno = %d\n", siginfo->si_errno); printf("si_code = %d\n", siginfo->si_code); // printf("si_trapno = %d\n", siginfo->si_trapno); 这个成员依赖于架构 printf("si_pid = %d\n", siginfo->si_pid); printf("si_uid = %d\n", siginfo->si_uid); printf("si_status = %d\n", siginfo->si_status); printf("si_utime = %ld\n", siginfo->si_utime); printf("si_stime = %ld\n", siginfo->si_stime); printf("si_value{\n"); printf("\tsival_int = %08x(%d)\n", siginfo->si_value.sival_int, siginfo->si_value.sival_int); printf("\tsival_ptr = %p\n", siginfo->si_value.sival_ptr); printf("}\n"); printf("si_int = %08x(%d)\n", siginfo->si_int, siginfo->si_value.sival_int); printf("si_ptr = %p\n", siginfo->si_ptr); printf("si_overrun= %d\n", siginfo->si_overrun); printf("si_timerid= %d\n", siginfo->si_timerid); printf("si_addr = %p\n", siginfo->si_addr); printf("si_band = %ld\n", siginfo->si_band); printf("si_fd = %d\n", siginfo->si_fd); } printf("---------------------------------------------\n");}int main() { printf("I'm %d\n", getpid()); struct sigaction act; act.sa_sigaction = handler; // 使用带附加参数的信号处理函数 sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; // 发送的信号带参数 sigaction(SIGQUIT, &act, NULL); while(1) { write(STDOUT_FILENO, ".", 1); pause(); } return 0;}
3.2 程序 b
// b.c#include <unistd.h>#include <signal.h>#include <stdlib.h>#include <stdio.h>int main(int argc, char* argv[]) { if (argc < 2) printf("usage: %s <pid>\n", argv[0]); pid_t pid = atoi(argv[1]); union sigval val; while(1) { scanf("%d", &val.sival_int); if (sigqueue(pid, SIGQUIT, val) < 0) { // 发送带附加值的信号 perror("sigqueue"); } }}
3.3 编译与运行
$ gcc a.c -o a$ gcc b.c -o b
- 先运行程序 a
$ ./a
屏幕打印:
I'm 3959.
- 运行程序 b
再开启一个终端,键入:
$ ./b 3959
这时候,再键入任意数字
1234
- 结果
程序 a 会打印:
I'm 3959.hello SIGQUITsi_signo = 3 // 注册的是 SIGQUIT 信号si_errno = 0si_code = -1si_pid = 3966 // 这是程序 b 的进程 id 号si_uid = 1000 // 程序 b 的实际用户 id 号si_status = 1234 // 发现这个值也是 1234si_utime = 0si_stime = 0si_value{ sival_int = 000004d2(1234) // 这个值是我们通过 sigqueue 传递过来的,后面的都是 sival_ptr = 0x4d2}si_int = 000004d2(1234)si_ptr = 0x4d2si_overrun= 1000si_timerid= 3966si_addr = 0xf7esi_band = 3966si_fd = 1000---------------------------------------------.
4. 总结
- 掌握 sa_flags 选项 SA_SIGINFO,知道它的含义
- 掌握 siginfo_t 结构体,知道常用的成员
- 掌握 sigval_t 和 sigval 结构体,知道这个值是从何处而来
- 掌握 sigqueue 信号发送函数
本篇的量有点大,不想写代码的同志,请复制粘贴到你的环境里编译。无论如何,动手练一练。
附录
完整版的 siginfo_t(实际上有些系统实现里,成员会比这个更多)
struct 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 */ sigval_t 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 */ int si_band; /* Band event */ int si_fd; /* File descriptor */}
- 42-带参数的信号
- PyQt4--发送带参数的自定义信号
- 信号槽如何传递参数(或带参数的信号槽)
- 信号槽如何传递参数(或带参数的信号槽)
- QT带参数信号槽
- QT信号与槽“带参数”
- 初学Qt之--带参数的信号和槽的实现(入门级)
- 初学Qt之--带参数的信号和槽的实现(入门级)
- 带参数的超级连接
- 带参数的多线程
- 带参数的exit
- 带参数的批处理
- 带参数的命令
- 带参数的游标
- 带参数的响应函数
- 带参数的宏
- 带参数的main
- 带参数的游标
- Android非常好用的相册开源项目-TelegramGallery
- 修改apt-get和yum为阿里源
- 17. Letter Combinations of a Phone Number
- webstorm搭配Monokai-Sublime主题颜色方法
- Android/Socket使用
- 42-带参数的信号
- 实现vector的代码
- 【中文分词系列】 5. 基于语言模型的无监督分词
- 弹出框
- python--删除文本中的空行
- NGINX基本操作
- centos7中启动tomcat提示bash: tomcat8.0.39/bin/startup.sh: 权限不够
- UVa - 11729 - Commando War ( 排序+贪心 )
- addEventListener attachEvent和解决IE 6 7 8 this指向错误