Linux自定义信号


信号同样是用于进程通信的,他是一种异步通信方式。我们之前的管道不是,明显,读进程要等管道里面有数据才能运行,否则他要等待。信号处理则不同,进程不知道什么时候信号会到来,先看一小段代码,看看信号的程序:

[root@liumengli signal]# cat ./signal_recive1.c 

#include "stdio.h"
#include "signal.h"
#include "sys/types.h"
#include "unistd.h"

void new_op(int, siginfo_t *, void *);

int main(int argc, char**argv) {
        struct sigaction act; //创建新的信号
        struct sigaction old_act; //用与记录旧的信号,当然你也可以不用记录
        int sig; //信号量,将来我们发送的信号要以这个信号量发送
        sig = atoi(argv[1]);

        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_SIGINFO;
        act.sa_sigaction = new_op; //设置信号的响应操作

        if(sigaction(sig, &act, &old_act) < 0) { //开始创建信号
                printf("install sigal error\n");
                return 1;
        }

        while(1) { //为了测试写的一个死循环
                sleep(2);
                printf("Now we wait for signal\n");
        }
}

void new_op(int signum, siginfo_t * info, void * myact) { //信号的响应操作
        printf("recive signal %d\n", signum);
        exit(1);
}



[root@liumengli signal]# ./signal_recive1 15

(我们以15号信号作为新的信号的信号量,我们即将发送就发送15好给目标进程,注意我们这里是用掉了系统的15号信号,这个信号是可以被从写的,但有2个信号KILL和STOP信号,KILL的信号是9,而STOP是根据硬件结构体系而不同,他们是不能被从写的,原因是,内核代码中在安装信号上有这么一句(act && (sig == SIGKILL || sig == SIGSTOP)))
return -EINVAL;同时他们不能被屏蔽也就是不能被不响应,其它信号是可以屏蔽的。这么做的原因是内核设计者为了给进程控制留下的最后底限,绝大部分信号的默认操作是终止进程,这个2个从名字上也看出一个是终止,另一个是暂停。这样我们对一个进程至少有最后的杀死手段就是向进程发送这2种信号,如何法后面会看到)
Now we wait for signal
Now we wait for signal
Now we wait for signal
Now we wait for signal

.......

另开一个终端

[root@liumengli signal]# ps -le
......

0 S     0 30681 28333  0  75   0 -   352 hrtime pts/2    00:00:00 signal_recive1
0 R     0 30828 28245  0  78   0 -  1368 -      pts/1    00:00:00 ps
[root@liumengli signal]# kill -15 30681
再返回看看被这个进程掌控的终端的反应

Now we wait for signal
recive signal 15
[root@liumengli signal]# 
按照我们预期的结束了。

 

先解释下kill指令,这是发送信号的一个指令,当然我们可以用函数(linux提供了),我们发送了15号信号,按照我们预期的结束了。通常人们认为 kill是杀死一个进程,其实不是,它是发送一个信号给你指定的进程,觉大部分进程不会去改系统的信号量,如果真的改了,他们改不了STOP和KILL这2个信号量,我改过,运行时候报了信号安装失败。如果你杀不死一个流氓进程的时候,不妨试试 kill -KILL目标进程的PID,kill -STOP是个蛮好玩的信号,可以试试,想恢复就发送kill -CONT.另一个,写过linux/unix环境编程的人会清楚知道,就算进程不是就绪状态,发送信号是会将它改成就绪态,这个看下发送信号的源代码就知道了,他会把目标进程挂到就绪队列中。所以睡眠进程也会响应信号。

 

从我的程序中看出我没有哪儿有代码去检测一个信号是否到来,既用户并不关心信号什么时候会到来,用户关心的是信号来了我该做些什么。那么谁去检测信号是否到来的,换句话说就是:信号检测的代码写在哪里。它写在

217 ret_with_reschedule:
218 cmpl $0,need_resched(%ebx)
219 jne reschedule
220 cmpl $0,sigpending(%ebx)
221 jne signal_return

这段代码的第220行就是检测信号,以前我们看过进程调度,进程调度代码写在218行。这段代码会在任何返回用户空间时候会被执行,包括系统调用返回,中断返回,异常处理返回从睡眠唤醒的时候返回。

 

如果你的程序一直在用户空间,而且不会受到任何中断(不太可能实现,至少定时的时钟中断会来骚扰下,而且没有系统调用的支持,直接自己去操作一些硬件也不太可能),那么信号是不会被响应的。

 

从响应的角度来说,不是我发信号,对方就会立即反应,这中间会有延时,这个不难理解了。


稍微整理后:

#include <stdio.h>
#include <stdlib.h>
#include "signal.h"
#include "sys/types.h"
#include "unistd.h"

void my_action(int signum, siginfo_t * info, void * myact)
{ //信号的响应操作
        printf("recive signal %d\n", signum);
}

void CreateSigAndBind(int Sig,void (*fun)(int signum, siginfo_t * info, void * myact))
{
	struct sigaction act; //创建新的信号
	struct sigaction old_act; //用与记录旧的信号,当然你也可以不用记录

	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	act.sa_sigaction = fun; //设置信号的响应操作

	if(sigaction(Sig, &act, &old_act) < 0) { //开始创建信号
			printf("install sigal error\n");
			return ;
	}
}

int main(int argc, char**argv)
{
	CreateSigAndBind(43,&my_action);
	CreateSigAndBind(44,&my_action);

	while(1) { //为了测试写的一个死循环
		sleep(2);
		printf("Now we wait for signal\n");
	}
	return 1 ;
}




你可能感兴趣的:(Linux自定义信号)