1.用户通过键盘Ctrl +C,产生2号信号SIGINT(信号被创建)
2.信号产生但是没有被处理(未决)
-在内核中将所有的没有被处理的信号存储在一个集合中(未决信号集)
- SIGINT信号状态被存储在第二个标志位上
-这个标志位的值为0,说明信号不是未决状态
-这个标志位的值为1,说明信号处于未决状态
3.这个未决状态的信号需要被处理,在处理之前需要和另一个信号集(阻塞信号集)进行比较
-阻塞信号集默认为0,不会阻塞任何信号,如果需要阻塞某些信号,需要调用某些api
-如果阻塞信号集对应信号为0:则信号被处理
-如果阻塞信号集对应信号为1:则信号阻塞继续处于未决状态,直到阻塞信号集变为0.
信号集处理相关函数
/*
#include <signal.h>
int sigemptyset(sigset_t *set);
-功能:清空信号集中的数据,将信号集中的所有的标0记位置为
-参数:
-set:传出参数,需要操作的信号集
-返回值:
成功:0
失败:-1
int sigfillset(sigset_t *set);
-功能:将信号集中的所有的标记位置为1
-参数:
-set:传出参数,需要操作的信号集
-返回值:
成功:0
失败:-1
int sigaddset(sigset_t *set, int signum);
-功能:设置信号集(set)中的signum位为1(阻塞信号)
-参数:
-set:传出参数,需要操作的信号集
-signum:信号位
-返回值:
成功:0
失败:-1
int sigdelset(sigset_t *set, int signum);
-功能:设置信号集(set)中的signum位为0(不阻塞信号)
-参数:
-set:传出参数,需要操作的信号集
-signum:信号位
-返回值:
成功:0
失败:-1
int sigismember(const sigset_t *set, int signum);
-功能:判断signum是否阻塞
-参数:
set:信号集
signum:信号
-返回值:
1:signum被阻塞
0:signum不阻塞
-1:错误,调用失败
以上函数都是对自定义的信号集进行操作,以下可以对系统的信号集进行操作:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
*/
#include <stdio.h>
#include <signal.h>
int main(){
//创建信号集
sigset_t set;
//判断SIGINT是否阻塞
int isclock = sigismember(&set, SIGINT);
printf("判断初始:isclock = %d \n", isclock);
//清空信号集内容int sigemptyset(sigset_t *set)
int ret = sigemptyset(&set);
if(ret == -1){
perror("sigemptyset");
return -1;
}
//再次判断SIGINT是否阻塞
isclock = sigismember(&set, SIGINT);
printf("清空: isclock = %d \n", isclock);
//设置为阻塞1
ret = sigaddset(&set, SIGINT);
if(ret == -1){
perror("sigaddset");
return -1;
}
//再次判断SIGINT是否阻塞
isclock = sigismember(&set, SIGINT);
printf("设置为阻塞:isclock = %d \n", isclock);
//设置不阻塞
ret = sigdelset(&set, SIGINT);
if(ret == -1){
perror("sigdelset");
return -1;
}
//再次判断SIGINT是否阻塞
isclock = sigismember(&set, SIGINT);
printf("设置为不阻塞:isclock = %d \n", isclock);
return 0;
}
内核阻塞原理
添加set阻塞:
SIG_BLOCK:
内核mask: 10010
阻塞set : 00101
|: ------------
mask|set: 10111 //添加了set阻塞
=================================
解除set阻塞:
SIG_UNBLOCK:
~set: 11010
mask: 10010
mask &= ~set: ----------------------
10010 //回到最初mask
sigprocmask
/*
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
-功能:将自定义信号集中的数据设置到内核中(设置阻塞、解除阻塞、替换)
-参数:
-how: 如何对内核阻塞信号集进行处理
-SIG_BLOCK: 将用户设置的阻塞信号添加到内核中,内核中原来的数据不变
mask | set
-SIG_UNBLOCK: 根据用户设置的数据,岁内核中的数据进行解除阻塞
mask &= ~set
-SIG_SETMASK: 覆盖内核中原来的值
-set:已经初始好的用户自定义的信号集
-oldset: 保存设置之前的内核中的阻塞信号状态,可以是NULL
-返回值:
成功: 0
失败: -1 并返回错误号(EFAULT | EINVAL)
int sigpending(sigset_t *set);
-功能:
获取内核中的未决信号集
-参数:
set: 传出参数,保存内核中的未决信号集
-返回值:
成功: 0
失败: -1
*/
//编写程序,把所有的常规信号(1-31)的未决信号打印(0: 非阻塞、1:阻塞)
//设置某些信号是阻塞的,通过键盘产生这些信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(){
//设置2(SIGINT(ctrl+c))/3(SIGQUIT(ctrl+\))号信号阻塞
sigset_t set;
int ret = sigemptyset(&set);
if(ret == -1){
perror("sigemptyset");
return -1;
}
//将2/3号信号置为1
ret =sigaddset(&set, SIGINT);
if(ret == -1){
perror("sigaddset SIGINT");
return -1;
}
ret =sigaddset(&set, SIGQUIT);
if(ret == -1){
perror("sigaddset SIGQUIT");
return -1;
}
//修改内核中的阻塞信号集
ret = sigprocmask(SIG_BLOCK, &set, NULL);
if(ret == -1){
perror("sigprocmask");
return -1;
}
int j=100;
while(--j){
//获取当前的未决信号集数据
sigset_t penSet;
ret = sigemptyset(&penSet);
if(ret == -1){
perror("sigemptyset penSet");
return -1;
}
ret = sigpending(&penSet);
if(ret == -1){
perror("sigpending");
return -1;
}
//遍历前32位
for(int i=1; i<=32; i++){
if(sigismember(&penSet, i) == 1){
printf("1");
}else if(sigismember(&penSet, i) == 0){
printf("0");
}else{
perror("sigismember");
return -1;
}
}
printf("\n");
sleep(1);
if(i%10 == 0){
//解除阻塞
printf("j = %d\n", j);
sigprocmask(SIG_UNBLOCK, &set, NULL);
}
}
return 0;
}
sigaction(推荐)
/*
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
-功能:检查或者改变信号的处理,信号捕捉
-参数:
-signum:信号编号或宏值(推荐)
-act: 捕捉信号好的处理动作
-oldact:上一次信号捕捉相关设置,一般不使用(NULL)
-返回值:
-成功:0
-失败:-1
struct sigaction {
void (*sa_handler)(int);//函数指针,信号捕捉后的处理函数
void (*sa_sigaction)(int, siginfo_t *, void *); //不常用
sigset_t sa_mask;//设置的临时阻塞信号集,在信号捕捉函数执行过程中,临时阻塞某些信号,执行完成后就不再起作用
int sa_flags;//使用哪一个信号处理(0:sa_handler | SA_SIGINFO:sa_sigaction)
void (*sa_restorer)(void);//已弃用(NULL)
};
*/
/*
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
-功能:设置某个信号的捕捉行为
-参数:
-signum:要捕捉的信号
-handler:如何处理
-SIG_IGN:忽略信号
-SIG_DFL:使用默认的信号,无作为
-回调函数:内核调用
-返回值:
-成功:返回上一次注册信号处理的地址,第一次调用返回NULL
-失败:返回SIG_ERR,设置错误号
SIGKILL 和 SIGSTOP 不能被捕捉和hulue
*/
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void myalarm(int num){
printf("捕捉到了信号:%d \n", num);
printf("*************\n");
}
//五秒后每隔三秒定时
int main(){
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = myalarm;
sigemptyset(&act.sa_mask);//清空临时阻塞信号集
//注册捕捉信号
sigaction(SIGALRM, &act, NULL);
struct itimerval new_value;
//设置间隔时间
new_value.it_interval.tv_sec = 2;//间隔时间
new_value.it_interval.tv_usec = 0;
//设置延迟时间
new_value.it_value.tv_sec = 3;//第一次定时也会发送信号
new_value.it_value.tv_usec = 0;
int ret = setitimer(ITIMER_REAL, &new_value, NULL);//非阻塞
printf("定时器开始了。。。。。。\n");
if(ret == -1){
perror("setitimer");
return -1;
}
int i = 1;
while(1){
printf("i = %d \n", i++);
sleep(1);
}
return 0;
}
僵尸进程的回收
/*
SIGCHLD信号产生的是三个条件:
-1:子进程结束
-2: 子进程暂停(收到SIGSTOP信号暂停)
-3:子进程继续运行(处于停止态,收到SIGCONT后唤醒)
以上三种情况都会给父进程发送该信号,父进程默认忽略该信号
可以用SIGCHLD解决僵尸进程问题
*/
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
void myFunc(int num){
printf("捕捉到了信号: %d \n", num);
//回收子进程的pcb资源
while(1){
int ret = waitpid(-1, NULL, WNOHANG);
if(ret > 0){
printf("%d 已被回收\n", ret);
}else if(ret == 0){
//还有子进程活着
break;
}else{
//无子进程
break;
}
}
}
int main(){
//提前设置好阻塞信号集,阻塞SIGCHLD,因为有可能子进程已经结束了父进程还没有注册阻塞信号集
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, NULL);
//创建爱你子进程
pid_t pid;
for(int i=0; i<20; i++){
pid = fork();
if(pid == 0){
break;//不让子进程再创建子进程
}
}
if(pid > 0){
//P
//捕捉子进程死亡时发送的SIGCHLD信号,然后回收子进程的pid
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = myFunc;
sigemptyset(&act.sa_mask);
sigaction(SIGCHLD, &act, NULL);
//注册完信号捕捉之后 解除阻塞
sigprocmask(SIG_UNBLOCK, &set, NULL);
while(1){
printf("parent process pid = %d \n", getpid());
sleep(2);
}
}else if(pid == 0){
//C
printf("child process pid = %d \n", getpid());
//wait()/waitpid(-1, &wstatus, 0)
}else{
perror("fork");
return -1;
}
return 0;
}