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;
}
相关推荐
chlk12315 小时前
Linux文件权限完全图解:读懂 ls -l 和 chmod 755 背后的秘密
linux·操作系统
舒一笑16 小时前
Ubuntu系统安装CodeX出现问题
linux·后端
改一下配置文件16 小时前
Ubuntu24.04安装NVIDIA驱动完整指南(含Secure Boot解决方案)
linux
碳基沙盒17 小时前
OpenClaw 多 Agent 配置实战指南
运维
深紫色的三北六号1 天前
Linux 服务器磁盘扩容与目录迁移:rsync + bind mount 实现服务无感迁移(无需修改配置)
linux·扩容·服务迁移
SudosuBash1 天前
[CS:APP 3e] 关于对 第 12 章 读/写者的一点思考和题解 (作业 12.19,12.20,12.21)
linux·并发·操作系统(os)
哈基咪怎么可能是AI2 天前
为什么我就想要「线性历史 + Signed Commits」GitHub 却把我当猴耍 🤬🎙️
linux·github
十日十行2 天前
Linux和window共享文件夹
linux
Sinclair3 天前
简单几步,安卓手机秒变服务器,安装 CMS 程序
android·服务器
木心月转码ing3 天前
WSL+Cpp开发环境配置
linux