使用信号唤醒休眠函数
会导致休眠的函数
我们调用sleep、pause等函数时,这些函数会使进程进入休眠状态,如果你不想继续休眠可以使用信号将其唤醒。
为什么要捕获信号呢?
如果信号的默认处理方式是终止,我们并没有把函数唤醒,倒是把整个进程终止了。
唤醒的方法
给信号登记一个空捕获函数即可,当然你也可以在捕获函数写你要的代码,不过如果仅仅只是用于唤醒的话,捕获函数的内容一般都是空的。
唤醒的过程
当信号发送给进程后,会中断当前休眠的函数,然后去执行捕获函数,捕获函数执行完毕返回后,不再调用休眠函数,而是执行休眠函数之后的代码,这样函数就被唤醒了。
我想继续休眠怎么办?
我希望长期休眠的,但是不小心被别人发送的信号给唤醒了,我想继续休眠怎么办?
手动重新启动休眠函数(重新调用休眠函数)
pause()函数唤醒过程
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_fun()
{
printf("time up\n");
}
int main(int argc, char **argv, char **environ)
{
signal(SIGINT,signal_fun);
pause();
printf("hello\n");
while(1);
return 0;
}
运行结果为:
进程在pause休眠,按下ctrl+c之后,pause函数就会被中断,并且调用捕获函数,然后捕获函数执行之后返回继续执行pause函数之后的代码,打印出来hello进入while循环,再次按下ctrl+c继续调用捕获函数进行打印。
sleep函数唤醒过程
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
void signal_fun()
{
printf("time up\n");
}
int main(int argc, char **argv, char **environ)
{
signal(SIGINT,signal_fun);
sleep(10);
printf("hello\n");
while(1);
return 0;
}
运行结果为:
如果sleep函数休眠的过程中按下ctrl+c发送信号,就会中断sleep函数,然后去执行捕获函数,捕获函数在运行完成之后,继续执行sleep函数之后的代码。
手动重启pause函数
那么如果不想唤醒上面sleep和pause。但是又需要SIGINT信号需要被捕获,那么接下来我们手动重启调用:
pause函数会返回:当一个信号被捕获,而且信号捕获函数有返回。我们或者在信号捕获函数写入代码return 或者不写也会自动返回,但是如果信号捕获函数里面是exit(0),那么进程被终止,信号捕获函数也就不会有返回值。这种情况下pause函数返回-1并且errno被设置为EINTR。
EINTR说明:当一个信号被捕获,而且信号捕获函数有返回的时候产生,说明的是当前pause函数被信号中断。
那么接下来我们进行代码演示进行手动重启:
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
void signal_fun()
{
printf("time up\n");
}
int main(int argc, char **argv, char **environ)
{
int ret = 0;
signal(SIGINT,signal_fun);
lable: ret = pause();
if(ret == -1&& errno ==EINTR)
{
goto lable;
}
printf("hello\n");
while(1);
return 0;
}
运行结果为:
我们可以看到不会打印hello 那么也就不会执行pause下面的代码。
手动重启sleep函数
我们首先查看一下sleep函数的返回值:
如果设置的时间到了,返回为0,如果sleep被信号中断就返回剩余的秒数。
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
void signal_fun()
{
printf("time up\n");
}
int main(int argc, char **argv, char **environ)
{
int ret = 0;
signal(SIGINT,signal_fun);
ret = 10;
lable: ret = sleep(ret);
if(ret != 0)
{
printf("ret = %d\n",ret);
goto lable;
}
printf("hello\n");
while(1);
return 0;
}
运行结果为:
上面的过程总共休眠10秒钟。
当你的休眠函数不希望被信号个打断时,我们就可以重启这个函数的调用。
休眠函数自动重启
比如使用read从键盘读取数据,当键盘没有输入任何数据时,read会休眠,不过函数被信号唤醒后,会自动重启read的调用。
read函数读数据时,并不一定会休眠,读硬盘上的普通文件时,不管文件有没有数据,read都不会休眠,而是会返回继续向下运行,如果read读的是键盘的话,如果键盘没有数据时read就会休眠。
当read函数休眠时,如果被信号唤醒了,当捕获函数返回后,read会自动重启。
代码演示;
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
void signal_fun(int signo)
{
printf("!!!!!\n");
}
int main(int argc, char **argv, char **environ)
{
int ret = 0;
signal(SIGINT,signal_fun);
char buf[100] = {0};
read(0,buf,sizeof(buf));
printf("hello\n");
return 0;
}
运行结果为:
我们可以看到,按下键盘ctrl+c之后当raed函数在收到信号,执行完信号捕获函数之后会继续重新调用read,而不会执行read后面的代码,所以不会打印出来helloworld。
输入数据的时候hello就会被打印出来,读取到数据read函数执行完成了,就会继续执行read函数之后的代码:
小结
我们不需要记住那些函数是需要手动重启的,只需要记住,对于绝大多数休眠函数来说,被信号中断后,如果你想继续休眠的话,需要自己去手动重启,否则就会继续向后运行。
如果你拿不准是自动重启的,还是需要手动重启的,有两个方法来判断:
自己去测试一下,如果被信号中断后,后续代码不会被执行的,就是自动的重启的,否者就是手动重启。
看函数手册里面返回值的描述,如果描述里面有明确说明该函数可以被信号中断的话,这个函数就是手动重启。