Linux信号捕捉
程序员文章站
2022-07-12 11:19:18
...
内核是怎样实现信号的捕捉呢?处理流程如下图所示:
系统往往在从内核态切回用户态时会进行信号的处理。
信号捕捉函数:
sighandler_t signal(int signum, sighandler_t handler);
int sigaction(int signo, struct sigacton *act, struct sigaction* oact);
这两个函数都可以对信号进行捕捉,sigaction相对于signal来说相对复杂一点,区别在于:
1、signal在调用handler之前先把信号的handler指针恢复;sigaction调用之后不会恢复handler指针,直到再次调用sigaction修改handler指针。
2、signal在调用过程不支持信号block;sigaction调用后在handler调用之前会把屏蔽信号(屏蔽信号中自动默认包含传送的该信号)加入信号中,handler调用后会自动恢复信号到原先的值。
具体看一下sigaction函数:
#include<signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction可以修改相关与指定信号相关联的处理动作,成功返回0,出错返回-1。
关于结构体 struct sigaction
struct sigaction
{
void (*sa_handler) (int); //信号处理方式
sigset_t sa_mask; //屏蔽的信号
int sa_flag;
void (*sa_sigaction)(int , siginfo_t *, void *);
}
将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用⾃自定义函数捕捉信号,或者说向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则 用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
看一个关于sinaction的实例:
#include<stdio.h>
#include<signal.h>
void handler(int sig)
{
printf("get a sig : %d\n", sig);
}
int main()
{
struct sigaction act, oact;
act.sa_handler = &handler;//设置捕捉函数
sigemptyset(&act.sa_mask);//使用act之前先对其进行清空
sigaction(2, &act, &oact);//捕捉2号信号
while(1);
return 0;
}
运行结果:
我们可以通过sigaction实现自己的sleep()函数。
实现原理:
比如我们要进行sleep(3),可以这样实现,在运行在这条语句时将进程挂起,等到3秒钟后产生一个信号将进程从挂起**即可。
我们要实现进程的挂起可以使用pause()函数实现。
pause函数:
#include<unistd.h>
int pause(void );
函数介绍:
会使进程挂起,直到有信号递达该进程才会取消挂起。
该函数只有出错返回值,如果处理信号的默认动作是终止该进程,那么进程将被终止pause没有返回的机会;如果处理信号的默认动作是忽略,那么进程将继续保持挂起状态不能被执行,pause也不能返回;只有信号被捕捉处理时,捕捉函数调用完成之后pause才会返回-1,此时挂起状态被破坏。
对于时间的控制我们可以使用alarm()函数实现。
alarm函数:
#include<unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,在seconds秒后会给当前进程发送SIGALRM信号,该信号默认动作是终止当前进程。这个函数的返回值是0或者是以前是定闹钟时间还剩下的秒数。
实现代码:
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
//只需要睡眠,不做操作即可
void handler(int sig)
{
}
void mysleep(int time)
{
struct sigaction act, oact;
//设置捕捉函数
act.sa_handler = handler;
//清空信号集
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
//设置捕捉动作
sigaction(SIGALRM, &act, &oact);
//定下闹钟
alarm(time);
//挂起进程
pause();
int ret = alarm(0);
//将进程的信号集,恢复原来的状态
sigaction(SIGALRM,&oact, NULL);
}
int main()
{
while(1)
{
mysleep(1);
printf("use mysleep\n");
}
return 0;
}
运行结果,每隔一秒钟输出