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;
}
相关推荐
csdn_aspnet1 小时前
CentOS 7 上安装 MySQL 8.0
linux·mysql·centos
郝学胜-神的一滴1 小时前
Linux下,获取子进程退出值和异常终止信号
linux·服务器·开发语言·c++·程序人生
shumeigang1 小时前
nginx实用配置
运维·nginx
檀越剑指大厂2 小时前
【Nginx系列】Tengine:基于 Nginx 的高性能 Web 服务器与反向代理服务器
服务器·前端·nginx
AI科技星2 小时前
张祥前统一场论动量公式P=m(C-V)误解解答
开发语言·数据结构·人工智能·经验分享·python·线性代数·算法
戴草帽的大z2 小时前
在 rk3588上通过网络命名空间实现 eth0/eth1 网卡隔离与程序独立部署
linux·网络·rk3588·ip·iproute·网卡隔离·ip netns
fufu03112 小时前
俄罗斯方块
linux·运维·服务器
Ronin3052 小时前
【Linux网络】应用层协议HTTP
linux·网络·http·应用层协议
阿部多瑞 ABU3 小时前
Unicode全字符集加解密工具 - 命令行交互版:功能完整的终端解决方案
经验分享·交互·ai编程·1024程序员节