信号集操作函数
内核通过读取未决信号集来判断信号是否应被处理。信号屏蔽字 mask 可以影响未决信号集。而我们可以在应 用程序中自定义 set 来改变 mask。已达到屏蔽指定信号的目的。
信号集设定
sigset_t set;
//typedef unsigned long sigset_t;
int sigemptyset(sigset_t* set); 将某个信号集清 0 成功:0;失败:-1
int sigfillset(sigset_t* set); 将某个信号集置 1 成功:0;失败:-1
int sigaddset(sigset_t* set,int signum); 将某个信号加入信号集 成功:0;失败:-1
int sigdelset(sigset_t* set,int signum); 将某个信号清出信号集 成功:0;失败:-1
int sigismember(const sigset_t* set,int signum);判断某个信号是否在信号集中 返回值:在集合:1;不在:0; 出错:-1
sigset_t 类型的本质是位图。但不应该直接使用位操作,而应该使用上述函数,保证跨系统操作有效。
sigprocmask 函数
用来屏蔽信号、解除屏蔽也使用该函数。其本质,读取或修改进程的信号屏蔽字(PCB 中)
严格注意
屏蔽信号:只是将信号处理延后执行(延至解除屏蔽);而忽略表示将信号丢处理。
函数格式
int sigprocmask(int how,const sigset_t* set,sigset_t* oldset);
成功:0;失败:-1,设置 errno
参数
set:传入参数,是一个位图,set 中哪位置 1,就表示当前进程屏蔽哪个信号。
oldset:传出参数,保存旧的信号屏蔽集。
how 参数取值: 假设当前的信号屏蔽字为 mask
- SIG_BLOCK: 当 how 设置为此值,set 表示需要屏蔽的信号。相当于 mask=mask|set
- SIG_UNBLOCK: 当 how 设置为此,set 表示需要解除屏蔽的信号。相当于 mask=mask&~set
- SIG_SETMASK: 当 how 设置为此,set 表示用于替代原始屏蔽及的新屏蔽集。相当于 mask=set 若,调用 sigprocmask 解除了对当前若干个信号的阻塞,则在 sigprocmask 返回前,至少将其中一 个信号递达。
sigpending 函数
读取当前进程的未决信号集
int sigpending(sigset_t* set);
set 传出参数。 返回值:成功:0;失败:-1,设置 errno
编写程序。把所有常规信号的未决状态打印至屏幕。
/** 打印未决信号集*/#include<stdio.h>
#include<signal.h>
#include<unistd.h>void printped(sigset_t * ped)
{int i;//常用的是前32个信号for(i = 1; i < 32; i++ ){if(sigismember(ped,i) == 1){ //在集合中putchar('1');}else{putchar('0');} } printf("\n");
}int main(void ){sigset_t myset, oldset ,ped;sigemptyset(&myset);sigaddset(&myset,SIGQUIT);//屏蔽系统本身3号信号sigaddset(&myset,SIGINT);//屏蔽系统本身的2号信号sigaddset(&myset,SIGTSTP);sigaddset(&myset,SIGSEGV);sigaddset(&myset,SIGKILL); sigprocmask(SIG_BLOCK,&myset,&oldset);while(1){sigpending(&ped);//判断集合中每个信号的对应位是0还是1printped(&ped);sleep(1);}return 0;
}
9号信号和19号信号不允许设置屏蔽
信号捕捉
signal 函数
注册一个信号捕捉函数:
typedef void(*sighandler_t)(int); sighandler_t signal(int signum,sighandler_t handler);
该函数由 ANSI 定义,由于历史原因在不同版本的 Unix 和不同版本的 Linux 中可能有不同的行为。因此应该尽 量避免使用它,取而代之使用 sigaction 函数。
void (*signal (int signum,void ( *sighandler_t)(int)))(int); #include<stdio.h>
#include<sys/time.h>
#include<signal.h>void myfunc(int signo)
{printf("hello world\n");
}int main(void)
{struct itimerval it,oldit;signal(SIGALRM,myfunc); //注册SIGALRM信号的捕捉处理函数//sighandler_t tml=signal(SIGALRM,myfunc);it.it_value.tv_sec=5;//定时5秒中it.it_value.tv_usec=0;//0微秒it.it_interval.tv_sec=3;//第一个和第二个之间间隔时间3秒it.it_interval.tv_usec=0;if(setitimer(ITIMER_REAL,&it,&oldit) == -1){perror("setitimer error");return -1; } while(1);return 0;
}
第一个出来是5秒,随后都是3秒出来一个
#include<stdio.h>
#include<signal.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
typedef void(*sighandler_t)(int);void catchsigint(int singo)
{printf("-----catch\n");
}int main(void)
{sighandler_t handler;handler=signal(SIGINT,catchsigint);//捕捉if(handler == SIG_ERR){perror("signal error");exit(1);} while(1);return 0;
}
sigaction 函数
修改信号处理动作(通常在 Linux 用其来注册一个信号的捕捉函数)
int sigaction (int signum,const struct sigaction*act,struct sigaction*oldact) 成功:0;失败:-1,设置 errno
参数:
- act:传入参数,新的处理方式。
- oldact:传出参数,旧的处理方式。
struct sigaction 结构体
- struct sigaction {
- void (*sa_handler)(int); //函数名
- void (sa_sigaction)(int,siginfo_t,void*);
- sigset_t sa_mask; //用于指定在信号捕捉期间屏蔽的信号
- int sa_flags; //标志位
- void (*sa_restorer)(void);
};
sa_restorer:该元素是过时的,不应该使用,POSIX.1 标准将不指定该元素。(弃用) sa_sigaction:当 sa_flags 被指定为 SA_SIGINFO 标志时,使用该信号处理程序。(很少使用)
重点掌握:
-
sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为 SIG_IGN 表忽略 或 SIG_DFL 表执行默 认动作
-
sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽 生效,是临时性设置。
-
sa_flags:通常设置为 0,表使用默认属性。
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<signal.h>void docatch(int signo) {printf("%d signal is catched\n",signo); }int main(void) {int ret;struct sigaction act;act.sa_handler = docatch;sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask,SIGQUIT);act.sa_flags = 0; //默认属性 信号捕捉函数执行期间自动屏蔽本信号 ret = sigaction(SIGINT,&act,NULL);if(ret < 0){ perror("sigaction error");exit(1);} while(1);return 0; }
信号捕捉特性
- 进程正常运行时,默认 PCB 中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号。当注册了 某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽 的信号不由☆来指定。而是用 sa_mask 来指定。调用完信号处理函数,再恢复为☆。
- XXX 信号捕捉函数执行期间,XXX 信号自动被屏蔽。
- 阻塞的常规信号不支持排队,产生多次只记录一次。(后 32 个实时信号支持排队)