Linux:信号集

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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;
}

信号终于结束了 太多了

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值