【进程通信】信号的捕捉原理&&用户态与内核态的区别

简介: 【进程通信】信号的捕捉原理&&用户态与内核态的区别

1.前言

在之前的学习中,我们已经知道了信号是如何产生的,也知道了进程是如何记录一个信号的。如果进程收到了一个信号且没有屏蔽这个信号,则会将信号编号作为handler数组的下标执行该信号的处理函数。我们将处理特定信号的事件的过程叫做信号的捕捉。下面将带大家了解从操作系统的角度看是如何捕捉信号的。

2.了解内核态和用户态的区别

用户态和内核态是操作系统中两种不同的执行模式,对应着两种执行权限。处于内核态的执行模式下的执行权限比用户态的高。 遇到需要较高权限的代码,就进入内核态去执行。这两种模式对系统的安全和稳定性起到了至关重要的作用。下面来具体说说这两种状态。

  • 用户态:像上面说的,用户态其实具有的权限是有限的,用户态下的程序不能直接访问硬件资源或者进行某些敏感的操作 ,如系统调用等。
  • 内核态:内核态就是操作系统内核运行的模式,具有最高权限。内核态下的代码可以访问任何CPU指令并访问任何内存地址。内核态主要用于操作系统内核和设备驱动程序的运行。

2.1虚拟内存视角

从虚拟内存的角度上来看,内核态和用户态的区别体现在内存空间的分配和访问权限上。

我们知道,所有的进程都有一个统一的虚拟地址空间,以32为操作系统为例,3-4G的地址空间是内核空间(高地址),0-3G的地址空间是用户空间(低地址)。最重要的是,内核空间和用户空间都有各自的页表(内核级页表和用户级页表)。这也就意味着每一个进程的空间内,包含了操作系统本身这个程序的空间。

类似于共享空间,即使每一个进程空间都有内核空间,但所有进程的内核空间其实是共享的,这就说明了,内核级页表在任何进程中都是一样的,映射的都是同一个内核空间。这也解释了,为什么无论进程如何切换,我们都能找到操作系统。因为不同进程的3-4G的地址空间是同一个操作系统。

用户态和内核态访问进程空间的区别:

  • 处于用户态的程序只能访问用户空间的内存地址,而无法访问内核空间的地址。
  • 内核态可以访问整个虚拟空间地址,包括用户空间和内核空间。

2.2内核态和用户态的区别总结

  1. 权限不同:用户态权限有限,户态权限最高
  2. 安全性:由于权限较低,用户态不能访问系统核心,较为安全。而内核态具有完全控制权,一旦出错可能导致整个系统崩溃。
  3. 系统调用:用户态不能执行系统调用,需要切换到内核态。
  4. 切换条件:用户态切换到内核态通常是发生中断或者是异常,是需要条件的。而内核态可以随时切换为用户态。

共同点:内核态和用户态之间的切换都涉及到上下文的切换,都会增加额外的开销

到此,我们就大概了解了什么是用户态与内核态,也知道了这两者之间的区别。但是这和信号的捕捉有什么关系呢?

处理信号会涉及到内核态和用户态之间的转换吗?答案是会的。无论是发生中断还是异常,都会伴随信号的产生以及捕捉 。当信号到达一个进程的时候,操作系统会进行一些必要的操作来处理这个信号。这个过程就涉及到内核态和用户态之间的切换。

3.内核如何实现信号的捕捉

如果信号的的处理动作是用户自定义的,在信号递达时就调用这个函数,这就称为捕捉信号。但是由于信号的处理函数是在用户空间的,处理的过程就势必会伴随用户态内核态之间的切换。假设用户程序自定义了SIGQUIT信号的处理函数sighandler。程序捕捉该信号是的状态变化过程如下:

  1. 开始运行程序,程序处于用户态
  2. 运行时进程收到信号SIGQUIT,发生中断。CPU保存上下文,并把当前进程切换为内核态,以便处理信号的相关操作。
  3. 由于信号的处理动作是自定义的,切换回用户态执行sighandler函数。
  4. 信号处理函数执行完,通常会执行特殊的系统调用sigreturn(该函数回到上次被中断的地方),此时又要切换回内核态。假如自定义信号处理函数调用了exit,则终止进程。
  5. 执行sigreturn,切换回用户态,回到上次中断发生的地方并向下继续执行代码。

至此,中断信号的产生到处理,一共经历了四次状态的切换。(假设中间没有终止进程)

为了更好理解,可以观察下图:

4.sigaction函数

sigaction和signal都是用来处理信号的函数,但是sigaciton是一种更为强大和灵活的喜好处理机制,提供了更多的选项和更高的可靠性。

函数原型如下:

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
  • signo表示要捕捉的信号
  • act是指向sigaction结构体的指针,用于指定信号新的处理行为。为NULL使用系统默认的信号处理函数。
  • lodact也是指向sigaction结构体的指针,用于保存旧的信号处理行为。为NULL表示不保存。
  • 调用成功返回0,否则-1.

4.1sigactoin结构体

sigaction结构体定义如下:

struct sigaction {
    void (*sa_handler)(int);          // 信号处理函数
    void (*sa_sigaction)(int, siginfo_t *, void *); // 用于处理附加信息的信号处理函数
    sigset_t sa_mask;                 // 在处理信号时阻塞的信号集
    int sa_flags;                     // 行为标志
    void (*sa_restorer)(void);        // 不常用,可以忽略
};

sigaction结构体中不仅包含了信号的处理函数,还包含了很多其它的选项,例如可以在处理信号的时候阻塞其它信号(sa_mask),以防止竞争条件。

4.2扩展

当某个信号的处理函数正在执行时,内核将自动将当前信号加入进程信号的阻塞集合中。当信号处理完之后又会将该信号从阻塞信号集中移除。 这种机制保证了在处理信号的时候不会重复处理同种信号。也可以通过设置sa_mask的值来屏蔽其它信号。


相关文章
|
2天前
|
消息中间件 安全 数据处理
Python中的并发编程:理解多线程与多进程的区别与应用
在Python编程中,理解并发编程是提高程序性能和响应速度的关键。本文将深入探讨多线程和多进程的区别、适用场景及实际应用,帮助开发者更好地利用Python进行并发编程。
|
11天前
|
消息中间件 安全 Java
线程和进程的区别及应用场景
线程和进程的区别及应用场景
|
12天前
|
消息中间件 分布式计算 网络协议
从管道路由到共享内存:进程间通信的演变(内附通信方式经典面试题及详解)
进程间通信(Inter-Process Communication, IPC)是计算机科学中的一个重要概念,指的是运行在同一系统或不同系统上的多个进程之间互相发送和接收信息的能力。IPC机制允许进程间共享数据、协调执行流程,是实现分布式系统、多任务操作系统和并发编程的基础。
从管道路由到共享内存:进程间通信的演变(内附通信方式经典面试题及详解)
|
15天前
|
消息中间件 安全 Java
线程和进程的区别及应用场景
线程和进程的区别及应用场景
|
3天前
|
Python
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
5 0
|
4天前
|
存储 NoSQL Unix
【Linux】进程信号(下)
【Linux】进程信号(下)
16 0
|
4天前
|
安全 Linux Shell
【Linux】进程信号(上)
【Linux】进程信号(上)
13 0
|
4天前
|
安全 Linux 数据格式
【Linux】进程通信----管道通信(下)
【Linux】进程通信----管道通信(下)
14 0
|
4天前
|
Unix Linux
【Linux】进程通信----管道通信(上)
【Linux】进程通信----管道通信(上)
21 0
|
8天前
|
消息中间件 Java 调度
线程和进程的区别及其在操作系统中的实现机制
线程和进程的区别及其在操作系统中的实现机制