《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》——2.14 进程0由0特权级翻转到3特权级,成为真正的进程

本文涉及的产品
公网NAT网关,每月750个小时 15CU
简介: 本节书摘来自华章计算机《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》一书中的第2章,第2.14节,作者:新设计团队著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.14 进程0由0特权级翻转到3特权级,成为真正的进程

Linux操作系统规定,除进程0之外,所有进程都要由一个已有进程在3特权级下创建。在Linux 0.11中,进程0的代码和数据都是由操作系统的设计者写在内核代码、数据区,并且,此前处在0特权级,严格说还不是真正意义上的进程。为了遵守规则,在进程0正式创建进程1之前,要将进程0由0特权级转变为3特权级。方法是调用move_to_user_mode()函数,模仿中断返回动作,实现进程0的特权级从0转变为3。
执行代码如下:

//代码路径:init/main.c:
void main(void)
{
    …
    move_to_user_mode();
    …
}

//代码路径:include/system.h:    //参看1.3.4节
#define move_to_user_mode() \    //模仿中断硬件压栈,顺序是ss、esp、eflags、cs、eip
__asm__("movl %%esp,%%eax\n\t" \    
         "pushl $0x17\n\t" \    //SS进栈,0x17即二进制的10111(3特权级、LDT、数据段)
         "pushl %%eax\n\t" \     //ESP进栈
         "pushfl\n\t" \        //EFLAGS进栈    
         "pushl $0x0f\n\t" \    //CS进栈,0x0f即1111(3特权级、LDT、代码段)
         "pushl $1f\n\t" \    //EIP进栈
         "iret\n" \        //出栈恢复现场、翻转特权级从0到3
         "1:\tmovl $0x17,%%eax\n\t" \    //下面的代码使ds、es、fs、gs与ss一致
               "movw %%ax,%%ds\n\t" \
               "movw %%ax,%%es\n\t" \
               "movw %%ax,%%fs\n\t" \
               "movw %%ax,%%gs" \
            :::»ax»)

IA-32体系结构翻转特权级的方法之一是用中断。第1章介绍过,当CPU接到中断请求时,能中断当前程序的执行序,将CS:EIP切换到相应的中断服务程序去执行,执行完毕又执行iret指令返回被中断的程序继续执行。
这期间,CPU硬件还做了两件事:一件是硬件保护现场和恢复现场,另一件是可以翻转特权级。
从代码的执行序上看,中断类似函数调用,都是从一段正在执行的代码跳转到另一段代码执行,执行之后返回原来的那段代码继续执行。为了保证执行完函数或中断服务程序的代码之后能准确返回原来的代码继续执行,需要在跳转到函数或中断服务程序代码之前,将起跳点的下一行代码的CS、EIP的值压栈保存,保护现场。函数或中断服务程序的代码执行完毕,再将栈中保存的值出栈给CS、EIP,此时的CS、EIP指向的就是起跳点的下一行,恢复现场。所以,CPU能准确地执行主调程序或被中断的程序。实际需要保护的寄存器还有EFLAGS等。
中断与函数调用不同的是,函数调用是程序员事先设计好的,知道在代码的哪个地方调用,编译器可以预先编译出压栈保护现场和出栈恢复现场的代码;而中断的发生是不可预见的,无法预先编译出保护、恢复的代码,只好由硬件完成保护、恢复的压栈、出栈动作。所以,int指令会引发CPU硬件完成SS、ESP、EFLAGS、CS、EIP的值按序进栈,同理,CPU执行iret指令会将栈中的值自动按反序恢复给这5个寄存器。
CPU响应中断的时候,根据DPL的设置,可以实现指定的特权级之间的翻转。前面的sched_init函数中的set_system_gate(0x80,&system_call)就是设置的int 0x80中断由3特权级翻转到0特权级,3特权级的进程做了系统调用int 0x80,CPU就会翻转到0特权级执行系统代码。同理,iret又会从0特权级的系统代码翻转回3特权级执行进程代码。
move_to_user_mode()函数就是根据这个原理,利用iret实现从0特权级翻转到3特权级。
由于进程0的代码到现在一直处在0特权级,并不是从3特权级通过int翻转到0特权级的,栈中并没有int自动压栈的5个寄存器的值。为了iret的正确使用,设计者手工写压栈代码模拟int的压栈,当执行iret指令时,CPU自动将这5个寄存器的值按序恢复给CPU,CPU就会翻转到3特权级的段,执行3特权级的进程代码。
为了iret能翻转到3特权级,不仅手工模拟的压栈顺序必须正确,而且SS、CS的特权级还必须正确。注意:栈中的SS值是0x17,用二进制表示就是00010111,最后两位表示3,是用户特权级,倒数第3位是1,表示从LDT中获取段描述符,第4~5位的10表示从LDT的第3项中得到进程栈段的描述符。
当执行iret时,硬件会按序将5个push压栈的数据分别出栈给SS、ESP、EFLAGS、CS、EIP。压栈顺序与通常中断返回时硬件的出栈动作一样,返回的效果也是一样的。
执行完move_to_user_mode( ),相当于进行了一次中断返回,进程0的特权级从0翻转为3,成为名副其实的进程。

相关实践学习
高可用应用架构
欢迎来到“高可用应用架构”课程,本课程是“弹性计算Clouder系列认证“中的阶段四课程。本课程重点向您阐述了云服务器ECS的高可用部署方案,包含了弹性公网IP和负载均衡的概念及操作,通过本课程的学习您将了解在平时工作中,如何利用负载均衡和多台云服务器组建高可用应用架构,并通过弹性公网IP的方式对外提供稳定的互联网接入,使得您的网站更加稳定的同时可以接受更多人访问,掌握在阿里云上构建企业级大流量网站场景的方法。 学习完本课程后,您将能够: 理解高可用架构的含义并掌握基本实现方法 理解弹性公网IP的概念、功能以及应用场景 理解负载均衡的概念、功能以及应用场景 掌握网站高并发时如何处理的基本思路 完成多台Web服务器的负载均衡,从而实现高可用、高并发流量架构
相关文章
|
9天前
|
Linux C语言
Linux内核队列queue.h
Linux内核队列queue.h
|
13天前
|
存储 安全 数据管理
探索Linux的挂载操作🌈
在Linux这个强大的操作系统中,挂载操作是一个基本而重要的概念。它涉及到文件系统、设备和数据访问,对于理解Linux的工作方式至关重要。那么,挂载操作究竟是什么,为什么我们需要它,如果没有它,我们将面临什么问题呢?让我们一起深入探讨。
探索Linux的挂载操作🌈
|
22天前
|
Linux Windows
Linux之基本指令操作
Linux之基本指令操作
|
2天前
|
算法 Linux 调度
深入理解Linux内核的进程调度机制
【4月更文挑战第17天】在多任务操作系统中,进程调度是核心功能之一,它决定了处理机资源的分配。本文旨在剖析Linux操作系统内核的进程调度机制,详细讨论其调度策略、调度算法及实现原理,并探讨了其对系统性能的影响。通过分析CFS(完全公平调度器)和实时调度策略,揭示了Linux如何在保证响应速度与公平性之间取得平衡。文章还将评估最新的调度技术趋势,如容器化和云计算环境下的调度优化。
|
4天前
|
监控 Linux
linux监控指定进程
请注意,以上步骤提供了一种基本的方式来监控指定进程。根据你的需求,你可以选择使用不同的工具和参数来获取更详细的进程信息。
10 0
|
5天前
|
消息中间件 监控 Linux
Linux进程和计划任务管理
通过这些命令和工具,你可以有效地管理Linux系统中的进程和计划任务,监控系统的运行状态并保持系统的稳定和可靠性。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
98 2
|
8天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【4月更文挑战第12天】 在多任务操作系统如Linux中,进程调度机制是系统的核心组成部分之一,它决定了处理器资源如何分配给多个竞争的进程。本文深入探讨了Linux内核中的进程调度策略和相关算法,包括其设计哲学、实现原理及对系统性能的影响。通过分析进程调度器的工作原理,我们能够理解操作系统如何平衡效率、公平性和响应性,进而优化系统表现和用户体验。
18 3
|
11天前
|
监控 Java Linux
linux下监控java进程 实现自动重启服务
linux下监控java进程 实现自动重启服务
|
12天前
|
监控 Linux Shell
初识Linux下进程2
初识Linux下进程2
|
12天前
|
Linux 编译器 Windows
【Linux】10. 进程地址空间
【Linux】10. 进程地址空间
19 4