【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法

简介: 【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法

一、用户态和内核态的理解

       在操作系统中,用户态和内核态是两种主要的执行模式,它们代表了不同的访问级别和权限,用于确保系统的安全和稳定性。

1.1、用户态

       用户态是操作系统中用户进程的运行状态。在这种状态下,进程只能访问受限的系统资源,并且不能执行某些特权操作。用户态下的进程没有权限直接访问硬件或执行某些敏感的系统调用。它们必须通过系统调用接口来请求内核态的服务。

1.2、内核态

      内核态是操作系统的核心部分(即内核)的运行状态。在这种状态下,代码可以访问系统内的所有内存空间,并且可以执行特权指令。内核态下的代码具有最高级别的权限,可以访问硬件、执行敏感操作,并管理系统资源。

1.3、用户态和内核态的切换时机

       当用户态下的进程需要执行特权操作时,它会通过系统调用接口向内核发出请求。这时,操作系统会保存用户态的上下文,然后切换到内核态来执行相应的服务。除了系统调用外,如硬件中断、软件异常也会导致系统从用户态切换到内核态。在这三种情况下,操作系统都会保存用户态的上下文,并在内核态下处理这些事件。

二、信号被处理的时机

      如下图所示,进程从内核态切换回用户态的时候,信号会被检测并处理。如果该信号的处理方法为默认处理方法,就不需要从内核态切换回用户态来处理我们写的方法,但如果该信号的处理方法为我们自己的自定义处理方法,就要从内核态切换回用户态来执行,执行完毕程序再进入内核。下面是进程处理信号并执行我们写的自定义处理方法的过程:

       执行信号自定义的处理方法时,可以由操作系统直接帮我们做了,但是为了避免我们写的处理方法存在非法操作,就必须切换回用户态, 由用户态的权限来约束我们是否能执行这个处理方法

三、用户态和内核态切换的内核级理解

在我们的进程地址空间中存在1G的内核空间,这部分会用来映射加载到内存中的操作系统。

        在内核中,所有的系统调用函数其实是被用一个函数指针数组所管理起来的,该函数指针数组会通过页表的映射与1G的内核空间的某些虚拟地址建立起映射关系,这样当正文代码部分要调用某个系统调用函数时,只需要拿着虚拟地址在内核空间中寻找再通过页表的映射就可以在内存中找到该系统调用函数。

       在系统中会存在多个进程,每个进程的地址空间的[0,3G]的地址空间映射的内容都不相同,但是,每个进程都要进行系统调用 ,也就意味着地址空间中的1G内核级空间所映射的内容可以完全相同。进一步说,我们的进程无论如何总能找到操作系统,我们的进程访问操作系统,都是通过进程地址空间中[3GB,4GB]这一部分空间来访问的。

       既然操作系统已经被映射到我们进程的地址空间上,那是否意味着我们可以随便访问操作系统中的内容了呢?答案肯定不是的。在CPU中有寄存器可以标识当前进程的状态(比如说00就表示处于用户态,11就表示处于内核态),如果进程处于用户态就阻止进程访问操作系统

四、设置自定义信号处理的函数

设置自定义信号处理的函数除了signal函数外,还有一个sigaction函数。

      第一个参数为几号信号,第二个参数为一个描述新方法的结构体, 第三个参数为一个描述旧方法的结构体。

       该结构体中第一个成员变量为新设置的信号处理方法,第二个参数为类似于sa_handler,但它提供了更强大的功能,因为该函数还可以接收有关信号发送者的信息,可以设置为nullptr,第三个参数为一个信号集,可以用来设置在处理signum信号的同时所屏蔽的信号,第四个参数用于修改信号处理的某些默认行为,第五个参数通常不用于现代操作系统,不设置。 下面是一个sigaction函数使用的例子。

自定义2号信号的处理方法,让进程不断打印pending位图:

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
 
void print(sigset_t& pending)
{
    for(int i = 31; i>= 1; i--)
    {
        if(sigismember(&pending, i))
            cout << "1";
        else
            cout << "0";
    }
    cout << endl;
}
 
//自定义2号信号的处理方法,让进程不断打印pending位图
void handler(int sig)
{
    sigset_t pending;
    sigemptyset(&pending);
 
    while(true)
    {
        sigpending(&pending);
        print(pending);
        sleep(1);
    }
}

再让进程收到2号信号的同时阻塞3,4,5号信号:

int main()
{
    struct sigaction act,oact;
    act.sa_handler = handler;
    act.sa_flags = 0;
 
    cout << getpid() << endl;
 
    //清空要添加的信号集
    sigemptyset(&act.sa_mask);
    //让进程收到2号信号的同时阻塞3,4,5号信号
    sigaddset(&act.sa_mask, 3);
    sigaddset(&act.sa_mask, 4);
    sigaddset(&act.sa_mask, 5);
 
    sigaction(2, &act, &oact);
 
    while(true)
    {}
    return 0;
}

最终运行结果:

       第一次给进程发送2号信号,信号被执行了所以pending位图中没有2号信号,再次发送2号信号发现pending位图中有2号信号,证明2号信号被屏蔽了,因为进程在处理某个信号时如果再次给它发送同一个信号该信号会被自动屏蔽,依次再向进程发送3,4,5号信号,发现信号都被屏蔽了。

相关文章
|
9月前
|
安全 网络协议 Linux
深入理解Linux内核模块:加载机制、参数传递与实战开发
本文深入解析了Linux内核模块的加载机制、参数传递方式及实战开发技巧。内容涵盖模块基础概念、加载与卸载流程、生命周期管理、参数配置方法,并通过“Hello World”模块和字符设备驱动实例,带领读者逐步掌握模块开发技能。同时,介绍了调试手段、常见问题排查、开发规范及高级特性,如内核线程、模块间通信与性能优化策略。适合希望深入理解Linux内核机制、提升系统编程能力的技术人员阅读与实践。
832 1
|
9月前
|
Ubuntu Linux
Ubuntu 23.04 用上 Linux 6.2 内核,预计下放到 22.04 LTS 版本
Linux 6.2 带来了多项内容更新,修复了 AMD 锐龙处理器设备在启用 fTPM 后的运行卡顿问题,还增强了文件系统。
|
9月前
|
Ubuntu Linux
Ubuntu 23.10 现在由Linux内核6.3提供支持
如果你想在你的个人电脑上测试一下Ubuntu 23.10的最新开发快照,你可以从官方下载服务器下载最新的每日构建ISO。然而,请记住,这是一个预发布版本,所以不要在生产机器上使用或安装它。
|
9月前
|
监控 Ubuntu Linux
什么Linux,Linux内核及Linux操作系统
上面只是简单的介绍了一下Linux操作系统的几个核心组件,其实Linux的整体架构要复杂的多。单纯从Linux内核的角度,它要管理CPU、内存、网卡、硬盘和输入输出等设备,因此内核本身分为进程调度,内存管理,虚拟文件系统,网络接口等4个核心子系统。
979 0
|
9月前
|
Web App开发 缓存 Rust
|
9月前
|
Ubuntu 安全 Linux
Ubuntu 发行版更新 Linux 内核,修复 17 个安全漏洞
本地攻击者可以利用上述漏洞,攻击 Ubuntu 22.10、Ubuntu 22.04、Ubuntu 20.04 LTS 发行版,导致拒绝服务(系统崩溃)或执行任意代码。
Linux驱动程序开发用户态和内核态 模块机制
Linux驱动程序开发用户态和内核态 模块机制
|
消息中间件 缓存 Linux
Linux 用户态与内核态的交互——netlink 篇
理论篇 在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,例如iprote2网络管理工具,它与内核的交互就全部使用了netlink,著名的内核包过滤框架Netfilter在与用户空间的通读,也在最新版本中改变为netlink,无疑,它将是Linux用户态与内核态交流的主要方法之一。它的通信依据是一个对应于进程的标识,一般定为该进程的 ID。当通信的一端处于中断过程时,该标识为 0。当使用 netlink 套接字进行通信,通信的双方都是用户态进程,则使用方法类似于消息队列。但通信双方有一端是中断过程,使用方法则
1236 0
|
Unix Shell Linux
Linux探秘之用户态与内核态
从宏观上来看,Linux 操作系统的体系架构分为用户态和内核态(或者用户空间和内核)。
6976 0