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

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

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的值来屏蔽其它信号。


相关文章
|
3天前
|
存储 调度 C++
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
23 1
|
5天前
|
分布式计算 JavaScript 前端开发
多线程、多进程、协程的概念、区别与联系
多线程、多进程、协程的概念、区别与联系
18 1
|
10天前
|
算法 Linux 调度
Linux进程——进程的创建(fork的原理)
Linux进程——进程的创建(fork的原理)
11 2
|
23天前
|
Linux Shell
蓝易云 - 【Linux-Day8- 进程替换和信号】
这两个概念在Linux系统编程和shell脚本编写中都非常重要,理解它们可以帮助你更好地理解和控制Linux系统的行为。
26 9
|
18天前
|
存储 安全 Python
进程通信 , 信号量 , 队列 , 管道 , 共享内存
进程通信 , 信号量 , 队列 , 管道 , 共享内存
|
18天前
|
并行计算 安全 数据库
多线程与多进程之间的区别
多线程与多进程之间的区别
|
25天前
|
消息中间件 Linux C语言
进程通信:管道与队列
进程通信:管道与队列
|
6天前
|
调度
进程与线程的区别与联系
进程与线程的区别与联系
让“父进程”可以有自己的工作,不需要因为为了“子进程”回收资源而堵塞。但也要满足“子进程”退出后的资源能被立马回收。(不能使用任何的进程通信机制 比如:信号等)
让“父进程”可以有自己的工作,不需要因为为了“子进程”回收资源而堵塞。但也要满足“子进程”退出后的资源能被立马回收。(不能使用任何的进程通信机制 比如:信号等)

热门文章

最新文章

相关实验场景

更多