三种注册信号与处理函数的方法有什么区别???
System V 风格的 signal 函数,注册的信号处理是一次性的
进程收到信号后,调用由 signal 注册的处理函数
处理函数一旦执行,进程通过默认的方式处理后续相同信号
如果想要重复触发,那么必须再次调用 signal 注册处理函数
在信号处理函数执行期间,很可能再次收到当前信号
对于 System V 风格的 signal 函数,会引起信号处理函数的重入
在注册信号处理函数时:
会打断当前 A 信号的信号处理函数,去执行 B 信号的信号处理函数,等 B 信号的信号处理函数执行完成返回后,再继续去执行 A 信号的信号处理函数
系统调用期间,可能收到信号,此时进程必须从系统调用中返回
对于执行时间较长的系统调用 (write / read),被信号中断的可能性很大
如果希望信号处理之后,被中断的系统调用能够重启,则:可以通过 errno == EINTR 判断重启系统调用
System V 风格的 signal 函数
BSD 风格的 signal 函数
并非所有的系统调用对信号中断都表现同样的行为
sigaction() 函数比 signal() 函数更加灵活,sigaction() 函数可以指定信号的一些特性,比如:ONTSHOT、屏蔽自身和其他信号、重启系统调用等
由于 signal() 函数在不同的 Linux 发行版中可能存在不同的特性,所以不推荐使用 signal() 来处理信号,推荐使用 sigaction() 函数来处理信号
sigset_t sa_mask
int sa_flags
sa_flags 默认参数是不具备 ONESHOT 并且屏蔽自身、不会重启系统调用
信号产生
信号未决
信号递达
信号屏蔽
信号阻塞
main.c
// #define _GNU_SOURCE
// #define _XOPEN_SOURCE 600
// #define _POSIX_C_SOURCE 200800L
#include
#include
#include
#include
#include
#include
void delay_handler(int sig)
{
int i = 0;
// sysv_signal(SIGINT, delay_handler);
printf("begin delay handler...\n");
for(i=0; i<5; i++)
{
printf("sleep %d ...\n", i);
sleep(1);
}
printf("end delay handler...\n");
}
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)) == -1) &&
(errno == EINTR) )
{
printf("restart syscall mannually...\n");
}
if( ret != -1 )
{
data[ret] = 0;
}
return ret;
}
int main(void)
{
char buf[32] = {0};
signal(SIGINT, signal_handler);
// bsd_signal(40, signal_handler);
// sysv_signal(SIGINT, signal_handler);
// bsd_signal(SIGINT, delay_handler);
r_read(buf, 32);
printf("input: %s\n", buf);
return 0;
}
第 57 行,我们为用 signal() 函数为 SIGINT 信号注册了信号处理函数
我们使用 strace 来跟踪这个进程的系统调用
可以看出 signal() 函数就是调用 sigaction() 函数来实现的
sigqueue(...) 的黄金搭档是 sigaction(...)
sa_flags 设置 SA_SIGINFO 标志位,可使用三参数信号处理函数
其中有几个常用的参数,si_signo: 信号值;si_code: 信号是怎么产生的;si_pid: 信号是由哪个进程发送过来的;si_value: 发送方发送信号传递过来的参数
test2.c
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
int pid = atoi(argv[1]);
int sig = atoi(argv[2]);
union sigval sv = {1234567};
printf("current pid(%d) ...\n", getpid());
printf("send sig(%d) to process(%d)...\n", sig, pid);
// kill(pid, sig);
sigqueue(pid, sig, sv);
raise(SIGINT);
while( 1 )
{
printf("while...\n");
sleep(1);
}
return 0;
}
第 18 行,使用 sigqueue() 函数给指定进程发送指定信号,并且携带了一个 int 类型参数
main2.c
// #define _GNU_SOURCE
//#define _XOPEN_SOURCE 600
//#define _POSIX_C_SOURCE 200800L
#include
#include
#include
#include
#include
#include
void delay_handler(int sig)
{
int i = 0;
printf("begin delay handler...\n");
for(i=0; i<5; i++)
{
printf("sig(%d) - sleep %d ...\n", sig, i);
sleep(1);
}
printf("end delay handler...\n");
}
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 r_read(char* data, int len)
{
int ret = -1;
while( data &&
((ret = read(STDIN_FILENO, data, len-1)) == -1) &&
(errno == EINTR) )
{
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_sigaction = signal_handler;
act.sa_flags = SA_RESTART | SA_SIGINFO;
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;
}
我们想要收到发送方发送信号携带的 4 个字节的数据时,必须使用 sigaction() 函数来注册信号,并且 sa_flags 要指定 SA_SIGINFO 参数
程序运行结果如下图所示:
main.out 进程在接收到信号值为 40 的信号后,成功获取到信号所携带的数据
利用信号搞进程间通信靠谱吗???