11.Linux进程信号(三)

一.上集回顾

建议先学上篇博客,再向下学习,上篇博客的链接如下:

https://blog.csdn.net/weixin_60668256/article/details/154488665?fromshare=blogdetail&sharetype=blogdetail&sharerId=154488665&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link

将系统调用号,放到寄存器中,然后触发系统调用(int $0x80),然后自动查表,执行对应方法

本质上系统调用就是为了提供安全的访问环境

二.内核态和用户态

1.映射

在用户区这一部分,我们可以直接进行使用,不用任何的系统调用

我们根本就不关系系统调用的虚拟地址,我们直接在用户区调用系统调用号,这样可以了

无论多少的进程,我们对应的3,4DB的空间都是一样的

如果我们的不同进程的3,4GB区域映射的是不一样的区域,那么我们电脑里面可以存在不太的操作系统

不管是通过哪一个进程的地址空间,进入内核,都是通过软中断进入操作系统的

只有代码段地址的CPL的值是对应的0,才允许我们做3,4GB的映射

进入内核态之后,操作系统就直接拿寄存器的值了,就直接进行找对应的系统调用表了

所以我们的操作系统是一直在用户态和内核态进行切换的(时钟中断)

2.内核态和用户态的切换

a.信号捕捉方法执行的时候,为啥要做权限的切换呢?直接内核执行不就完了吗?

有安全风险,因为你不知道sighandler()处理函数里面是否有其他的非法操作(删除用户等),所以要从内核态切换回用户态,所有的风险操作都要交由用户来执行

b.为啥sighandler处理完之后,不直接返回main,还要再返回内核再进行返回main?

main函数和sighandler不一定有系统调用关系,而main和系统调用有调用关系,所以必须先返回内核,再进入我们的main

三.信号捕捉的操作

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>

void handler(int signo)
{
    std::cout << "get a sig: " << signo << std::endl;
    exit(1);
}

int main()
{
    struct sigaction act,oact;
    act.sa_handler = handler;

    ::sigaction(2,&act,&oact);
    while(true)
    {
        pause();
    }
    return 0;
}

这里也符合我们的预期

//上面只填充了我们对应的handler字段,那我们对应的sa_mask字段呢?

我们这里将代码进行修改一下:

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>

void handler(int signo)
{
    static int cnt = 0;
    cnt++;
    while(true)
    {
        std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
        sleep(1);
    }
    exit(1);
}

int main()
{
    struct sigaction act,oact;
    act.sa_handler = handler;

    ::sigaction(2,&act,&oact);
    while(true)
    {
        pause();
    }
    return 0;
}

代码证明,如下:

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>


void PrintBlock()
{
    sigset_t set,oset;
    sigemptyset(&set);
    sigemptyset(&oset);
    sigprocmask(SIG_BLOCK,&set,&oset);
    std::cout << "block: ";
    for(int signo = 31;signo > 0;signo--)
    {
        if(sigismember(&oset,signo))
        {
            std::cout << 1;
        }
        else
        {
            std::cout << 0;
        }
    }
    std::cout << std::endl;
}

void handler(int signo)
{
    static int cnt = 0;
    cnt++;
    while(true)
    {
        std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
        PrintBlock();
        sleep(1);
    }
    exit(1);
}

int main()
{
    struct sigaction act,oact;
    act.sa_handler = handler;

    ::sigaction(2,&act,&oact);
    while(true)
    {
        PrintBlock();
        pause();
    }
    return 0;
}

那我们只想自定义屏蔽我们对应的信号呢?

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>


void PrintBlock()
{
    sigset_t set,oset;
    sigemptyset(&set);
    sigemptyset(&oset);
    sigprocmask(SIG_BLOCK,&set,&oset);
    std::cout << "block: ";
    for(int signo = 31;signo > 0;signo--)
    {
        if(sigismember(&oset,signo))
        {
            std::cout << 1;
        }
        else
        {
            std::cout << 0;
        }
    }
    std::cout << std::endl;
}

void handler(int signo)
{
    static int cnt = 0;
    cnt++;
    while(true)
    {
        std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
        PrintBlock();
        sleep(1);
    }
    exit(1);
}

int main()
{
    struct sigaction act,oact;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,3);
    sigaddset(&act.sa_mask,4);
    sigaddset(&act.sa_mask,5);
    sigaddset(&act.sa_mask,6);
    sigaddset(&act.sa_mask,7);

    ::sigaction(2,&act,&oact);
    while(true)
    {
        PrintBlock();
        pause();
    }
    return 0;
}

信号结束之后,屏蔽信号又会变回去

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>


void PrintBlock()
{
    sigset_t set,oset;
    sigemptyset(&set);
    sigemptyset(&oset);
    sigprocmask(SIG_BLOCK,&set,&oset);
    std::cout << "block: ";
    for(int signo = 31;signo > 0;signo--)
    {
        if(sigismember(&oset,signo))
        {
            std::cout << 1;
        }
        else
        {
            std::cout << 0;
        }
    }
    std::cout << std::endl;
}

void handler(int signo)
{
    static int cnt = 0;
    cnt++;
    while(true)
    {
        std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
        PrintBlock();
        sleep(1);
        break;
    }
}

int main()
{
    struct sigaction act,oact;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,3);
    sigaddset(&act.sa_mask,4);
    sigaddset(&act.sa_mask,5);
    sigaddset(&act.sa_mask,6);
    sigaddset(&act.sa_mask,7);

    ::sigaction(2,&act,&oact);
    while(true)
    {
        PrintBlock();
        pause();
    }
    return 0;
}

pending位图的1是执行完之前制成0,还是执行之后再清0呢?

执行之前就清0,执行之后在清理,我们就无法区分是之后来的,还是之前没有处理干净的

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>


void PrintBlock()
{
    sigset_t set,oset;
    sigemptyset(&set);
    sigemptyset(&oset);
    sigprocmask(SIG_BLOCK,&set,&oset);
    std::cout << "block: ";
    for(int signo = 31;signo > 0;signo--)
    {
        if(sigismember(&oset,signo))
        {
            std::cout << 1;
        }
        else
        {
            std::cout << 0;
        }
    }
    std::cout << std::endl;
}

void PrintPending()
{
    sigset_t pending;
    ::sigpending(&pending);
    std::cout << "Pending: ";
    for(int signo = 31;signo > 0;signo--)
    {
        if(sigismember(&pending,signo))
        {
            std::cout << 1;
        }
        else
        {
            std::cout << 0;
        }
    }
    std::cout << std::endl;
}

void handler(int signo)
{
    static int cnt = 0;
    cnt++;
    while(true)
    {
        std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
        // PrintBlock();
        PrintPending();
        sleep(1);
        break;
    }
}

int main()
{
    struct sigaction act,oact;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,3);
    sigaddset(&act.sa_mask,4);
    sigaddset(&act.sa_mask,5);
    sigaddset(&act.sa_mask,6);
    sigaddset(&act.sa_mask,7);

    ::sigaction(2,&act,&oact);
    while(true)
    {
        // PrintBlock();
        PrintPending();
        pause();
    }
    return 0;
}

四.可重入函数

stl是不能进行重入的

五.volatile

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>


int flag = 0;

void change(int signo)//信号捕捉的执行流
{
    (void)signo;
    flag = 1;
    printf("change flag 0->1, getpid: %d\n",getpid());
}

int main()
{
    printf("I am main process, pid is : %d\n",getpid());
    signal(2,change);
    while(!flag)//主执行流
    {

    }
    printf("我是正常退出的!\n");
    return 0;
}

如果我们进行代码优化

所以他一直用的就是我们对应的寄存器中的值,内存中的值进行修改之后,但是寄存器中的值还没进行修改

cpp 复制代码
#include <iostream>
#include <signal.h>
#include <unistd.h>


volatile int flag = 0;

void change(int signo)//信号捕捉的执行流
{
    (void)signo;
    flag = 1;
    printf("change flag 0->1, getpid: %d\n",getpid());
}

int main()
{
    printf("I am main process, pid is : %d\n",getpid());
    signal(2,change);
    while(!flag)//主执行流
    {

    }
    printf("我是正常退出的!\n");
    return 0;
}

六.SIGCHLD信号

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h> // 注意:原代码中使用了sleep函数,需包含此头文件
#include <wait.h>

void handler(int sig)
{
    pid_t id;
    while( (id = waitpid(-1, NULL, WNOHANG)) > 0) {
        printf("wait child success: %d\n", id);
    }
    printf("child is quit! %d\n", getpid());
}

int main()
{
    signal(SIGCHLD, handler);
    pid_t cid;
    if((cid = fork()) == 0){//child
        printf("child : %d\n", getpid());
        sleep(3);
        exit(1);
    }
    while(1){
        printf("father proc is doing some thing!\n");
        sleep(1);
    }
    return 0;
}
相关推荐
GeW10 小时前
新质生产力路线图Linux、数据库、国产化替代一次讲透
linux
aningx12 小时前
cachyos 从 GRUB 切换到 rEFInd
linux
第404号工位13 小时前
40块钱二手瘦客户端,如何跑满家庭物联网中枢?(拒绝Docker爆盘实录)
linux·架构·node.js
对象存储与RustFS13 小时前
网盘限速逼你开会员?用 RustFS 自己搭一个不限速的“私人网盘
服务器·后端·docker
骑着蜗牛撵大象32715 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
众壹新能源科技15 小时前
逆变器自带无功不够用,加装5MVar SVG后功率因数从0.85回到0.99
运维·人工智能·自动化
..Dauntless..15 小时前
【Linux】一切皆文件和缓冲区
linux·运维·服务器
裕晟资质规划15 小时前
三类资质变更报告时限差在哪?军工 15 日、集成印制 10 日
大数据·服务器·网络
QMCY_jason15 小时前
x86 ubuntu2204 无gui 安装 openeuler 2203 aarch64版本
linux·运维·服务器
重生之小比特15 小时前
【Linux系统】12 进程程序替换 自定义Shell 命令行解释器
linux·运维·服务器