一、用户态和内核态的理解
在操作体系中,用户态和内核态是两种主要的执行模式,它们代表了差异的访问级别和权限,用于确保体系的安全和稳定性。
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号信号,发现信号都被屏蔽了。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |