#include<stdlib.h>#include<string.h>#include<stdio.h>#include<unistd.h>intmain(){int i =1;while(1){printf("%d 正在工作\n",i);if(i ==4){//给自己发生一个编号为6的信号,默认的行为就是终止一个进程abort();}
i++;sleep(1);}return0;}
3.4 alarm函数(闹钟)
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:
设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止。每个进程都有且只有唯一的一个定时器。
取消定时器alarm(0),返回旧闹钟余下秒数。
参数:
seconds:指定的时间,以秒为单位
返回值:
返回0或剩余的秒数
定时,与进程状态无关(自然定时法)!就绪、运行、挂起(阻塞、暂停)、终止、僵
尸等,无论进程处于何种状态,alarm都计时。
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>intmain(){unsignedint ret =0;//第一次设置闹钟
ret =alarm(5);printf("上一次闹钟剩余的时间:%u\n", ret);sleep(3);
ret =alarm(3);//之前没有超时的闹钟被新的设备给覆盖掉printf("上一次闹钟剩下的时间是:%u\n", ret);printf("按下任意键继续。。\n");getchar();return0;}
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/time.h>#include<unistd.h>#include<signal.h>//信号处理函数voidfun(int signo){printf("捕捉到信号:%d\n",signo);}intmain(){int ret =-1;struct itimerval tmo;//第一次触发时间
tmo.it_value.tv_sec =3;
tmo.it_value.tv_usec =0;//触发周期
tmo.it_interval.tv_sec =2;
tmo.it_interval.tv_usec =0;//捕捉信号 SIGALRMsignal(SIGALRM, fun);//设置定时器
ret =setitimer(ITIMER_REAL,&tmo,NULL);if(-1== ret){perror("setitimer");return1;}//进程收到闹钟超时信号之后就会终止该进程printf("按下任意键继续\n");getchar();return0;}
4.信号集
4.1 信号集概述
在PCB中有两个非常重要的信号集。一个称之为“阻塞信号集”,另一个称之为“未决
信号集”。
这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对其进
行位操作。而需自定义另外一个集合,借助信号集操作函数来对PCB中的这两个信号
集进行修改
4.2 自定义信号集函数
为了方便对多个信号进行处理,一个用户进程常常需要对多个信号做出处理,在 Linux
系统中引入了信号集(信号的集合)。
信号集是一个能表示多个信号的数据类型,sigset_t set,set即一个信号集。
既然是一个集合,就需要对集合进行添加/删除等操作。
相关函数说明如下:
#include <signal.h>
int sigemptyset(sigset_t *set); //将set集合置空
int sigfillset(sigset_t *set); //将所有信号加入set集合
int sigaddset(sigset_t *set, int signo); //将signo信号加入到set集合
int sigdelset(sigset_t *set, int signo); //从set集合中移除signo信号
int sigismember(const sigset_t *set, int signo); //判断信号是否存在
除sigismember外,其余操作函数中的set均为传出参数。sigset_t类型的本质
是位图。但不应该直接使用位操作,而应该使用上述函数,保证跨系统操作有效。
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<signal.h>#include<unistd.h>voidshow_set(sigset_t *s){for(int i =1; i <32; i++){if(sigismember(s, i)){printf("1");}else{printf("0");}}putchar('\n');}//信号集处理函数intmain(){int i =0;//信号集集合
sigset_t set;//清空集合sigemptyset(&set);show_set(&set);//将所有信号加入到set集合中sigfillset(&set);show_set(&set);//将信号2和3从信号集中移除sigdelset(&set, SIGINT);sigdelset(&set, SIGQUIT);show_set(&set);//将信号2添加到集合中sigaddset(&set, SIGINT);show_set(&set);return0;}
4.3 sigprocmask函数
信号阻塞集也称信号屏蔽集、信号掩码。每个进程都有一个阻塞集,创建子进程时
子进程将继承父进程的阻塞集。信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞
(在信号发生时记住它,直到进程准备好时再将信号通知进程)。
所谓阻塞并不是禁止传送信号, 而是暂缓信号的传送。若将被阻塞的信号从信号阻
塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。
我们可以通过 sigprocmask() 修改当前的信号掩码来改变信号的阻塞情况。
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:
检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,新的信号阻塞集由 set 指定,而原先的信号阻塞集合由 oldset 保存。
参数:
how : 信号阻塞集合的修改方法,有 3 种情况:
SIG_BLOCK:向信号阻塞集合中添加 set 信号集,新的信号掩码是set和旧信号掩码的并集。相当于 mask = mask|set。
SIG_UNBLOCK:从信号阻塞集合中删除 set 信号集,从当前信号掩码中去除 set 中的信号。相当于 mask = mask & ~ set。
SIG_SETMASK:将信号阻塞集合设为 set 信号集,相当于原来信号阻塞集的内容清空,然后按照 set 中的信号重新设置信号阻塞集。相当于mask = set。
set : 要操作的信号集地址。
若 set 为 NULL,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到 oldset 中。
oldset : 保存原先信号阻塞集地址
返回值:
成功:0,
失败:-1,失败时错误代码只可能是 EINVAL,表示参数 how 不合法。
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<signal.h>#include<unistd.h>//信号处理函数1voidfun1(int signum){printf("捕捉到信号:%d\n",signum);}//信号处理函数2voidfun2(int signum){printf("捕捉到信号:%d\n",signum);}//信号注册函数intmain(){int ret =-1;//信号集
sigset_t set;
sigset_t old;//Ctrl + Csignal(SIGINT, fun1);//Ctrl + '\'signal(SIGQUIT, fun2);printf("按下任意键,阻塞信号2\n");getchar();sigemptyset(&old);sigemptyset(&set);sigaddset(&set, SIGINT);//设置屏蔽编号为2的信号
ret =sigprocmask(SIG_BLOCK,&set,&old);if(-1== ret){perror("sigprocmask");return1;}printf("设置屏蔽编号为2的信号成功\n");printf("按下任意键接触编号为2的信号的阻塞\n");getchar();//将信号屏蔽集设置为原来的集合
ret =sigprocmask(SIG_SETMASK,&old,&set);if(-1== ret){perror("sigprocmask");return1;}printf("按下任意键退出\n");getchar();while(1){sleep(1);}return0;}