用户模式和内核模式的区别是什么? 为什么激活它们? 如何激活它们? 它们的用例是什么?
我猜你说的是 Windows。简而言之,内核模式可以完全访问硬件,而用户模式则不行。例如,许多(如果不是大多数的话)设备驱动程序是以内核模式编写的,因为它们需要控制硬件的细节。
参见 这本维基百科。
内核模式 在内核模式中,正在执行的代码具有完整的和无限制的 访问底层硬件 可以执行任何 CPU 指令和 引用任何内存地址 模式通常保留给 最低级、最受信任的函数 系统崩溃 内核模式是灾难性的 将停止整个 PC 用户模式 在用户模式中,正在执行的代码无法直接访问 硬件或参考存储器。代码 在用户模式下运行必须委托给 系统 API 访问硬件或 记忆。由于所提供的保护 在这种孤立状态下,崩溃了 用户模式总是可恢复的。大多数 运行在您的计算机上的代码 将在用户模式下执行。
内核模式
在内核模式中,正在执行的代码具有完整的和无限制的 访问底层硬件 可以执行任何 CPU 指令和 引用任何内存地址 模式通常保留给 最低级、最受信任的函数 系统崩溃 内核模式是灾难性的 将停止整个 PC
用户模式
在用户模式中,正在执行的代码无法直接访问 硬件或参考存储器。代码 在用户模式下运行必须委托给 系统 API 访问硬件或 记忆。由于所提供的保护 在这种孤立状态下,崩溃了 用户模式总是可恢复的。大多数 运行在您的计算机上的代码 将在用户模式下执行。
继续读
理解用户和内核模式
其他答案已经解释了用户模式和内核模式之间的区别。如果你真的想了解更多的细节,你应该得到一份 Windows Internals ,一本由 Mark Russinovich 和 David Solomon 合著的描述各种 Windows 操作系统的架构和内部细节的优秀著作。
这是计算机可以操作的两种不同模式。在此之前,当计算机像一个大房间,如果有什么崩溃-它停止了整个计算机。所以计算机架构师决定改变它。现代微处理器至少在两种不同的硬件状态下实现。
用户模式:
内核模式:
转换是如何发生的。
从用户模式到内核模式的切换不是由 CPU 自动完成的。CPU 被中断(计时器、键盘、 I/O)打断。当中断发生时,CPU 停止执行当前正在运行的程序,切换到内核模式,执行 interrupt handler。此处理程序保存 CPU 的状态,执行其操作,恢复状态并返回到用户模式。
Http://en.wikibooks.org/wiki/windows_programming/user_mode_vs_kernel_mode
Http://tldp.org/howto/kernelanalysis-howto-3.html
Http://en.wikipedia.org/wiki/direct_memory_access
Http://en.wikipedia.org/wiki/interrupt_request
运行 Windows 的计算机中的处理器有两种不同的模式: 用户模式和内核模式。处理器根据在处理器上运行的代码类型在两种模式之间切换。应用程序以用户模式运行,核心操作系统组件以内核模式运行。虽然许多驱动程序在内核模式下运行,但有些驱动程序可能在用户模式下运行。
启动用户模式应用程序时,Windows 将为该应用程序创建进程。该流程为应用程序提供了一个私有虚拟地址空间和一个私有句柄表。因为应用程序的虚拟地址空间是私有的,所以一个应用程序不能更改属于另一个应用程序的数据。每个应用程序都是独立运行的,如果一个应用程序崩溃,则崩溃仅限于该应用程序。其他应用程序和操作系统不会受到崩溃的影响。
除了是私有的,用户模式应用程序的虚拟地址空间也是有限的。以用户模式运行的处理器无法访问为操作系统保留的虚拟地址。限制用户模式应用程序的虚拟地址空间可以防止应用程序更改关键操作系统数据,甚至可能损坏关键操作系统数据。
所有在内核模式下运行的代码都共享一个虚拟地址空间。这意味着内核模式驱动程序并不与其他驱动程序和操作系统本身隔离。如果内核模式驱动程序意外地写入了错误的虚拟地址,那么属于操作系统或其他驱动程序的数据可能会受到损害。如果内核模式驱动程序崩溃,整个操作系统都会崩溃。
如果你是一个 Windows 用户一旦通过这个链接,你会得到更多。
用户模式和内核模式之间的通信
什么
内核模式和用户模式之间的差异基本上不依赖于操作系统,只有通过硬件设计将某些指令限制为只能在内核模式下运行才能实现。诸如内存保护之类的所有其他目的只能通过这种限制来实现。
怎么做到的
这意味着处理器处于内核模式或用户模式。通过使用某些机制,体系结构可以保证每当切换到内核模式时,操作系统代码都会被提取来运行。
为什么
拥有这样的硬件基础设施可以在普通操作系统中实现:
CPU 环是最明显的区别
在 x86保护模式下,CPU 总是处于4个环中的一个。Linux 内核只使用0和3:
这是内核与用户界面的最严格、最快速的定义。
为什么 Linux 不使用环1和环2: CPU 特权戒指: 为什么戒指1和2不使用?
电流环是如何确定的?
当前的环是通过以下组合选择的:
全局描述符表: 一个内存中的 GDT 条目表,每个条目都有一个字段 Privl,它对环进行编码。
Privl
LGDT 指令将地址设置为当前描述符表。
另见: < a href = “ http://wiki.osdev.org/global _ Descriptor _ Table”rel = “ noReferrer”> http://wiki.osdev.org/global_descriptor_table
段寄存器 CS、 DS 等,它指向 GDT 中条目的索引。
例如,CS = 0意味着 GDT 的第一个条目当前对于正在执行的代码是活动的。
CS = 0
每个戒指能做什么?
CPU 芯片的物理结构如下:
环0可以做任何事情
Ring 3不能运行多个指令并写入多个寄存器,最明显的是:
不能更改自己的戒指! 否则,它可以将自己设置为戒指0,戒指将是无用的。
换句话说,不能修改当前的 段描述符,这决定了当前的环。
无法修改页面表: x86分页是如何工作的?
换句话说,不能修改 CR3寄存器,并且分页本身阻止修改页表。
这样可以防止一个进程查看其他进程的内存,这是出于安全性/易于编程的原因。
无法注册中断处理程序。这些处理程序是通过写入内存位置来配置的,也可以通过分页来防止。
处理程序在环0中运行,并且会破坏安全模型。
换句话说,不能使用 LGDT 和 LIDT 指令。
不能执行像 in和 out这样的 IO 指令,因此具有任意的硬件访问。
in
out
否则,例如,如果任何程序都可以直接从磁盘读取,那么文件权限将是无用的。
更准确地说,要感谢 迈克尔 · 佩奇: 实际上操作系统可以允许在环3上执行 IO 指令,这实际上是由 任务状态段控制的。
不可能的是,如果环3一开始就没有它,它就会允许自己这样做。
Linux 总是不允许它。参见: 为什么 Linux 不通过 TSS 使用硬件上下文切换?
程序和操作系统如何在环之间转换?
当 CPU 打开时,它开始在环0中运行初始程序(算是吧,但这是一个很好的近似值)。您可以认为这个初始程序是内核(但它通常是 一个引导加载程序,然后调用环0中的内核)。
当一个 userland 进程希望内核为它做一些事情,比如写入一个文件时,它使用一条指令来生成一个中断,比如 ABC0或 syscall来向内核发出信号。X86-64 Linux syscall hello world 示例:
syscall
.data hello_world: .ascii "hello world\n" hello_world_len = . - hello_world .text .global _start _start: /* write */ mov $1, %rax mov $1, %rdi mov $hello_world, %rsi mov $hello_world_len, %rdx syscall /* exit */ mov $60, %rax mov $0, %rdi syscall
编译并运行:
as -o hello_world.o hello_world.S ld -o hello_world.out hello_world.o ./hello_world.out
GitHub 上游。
当这种情况发生时,CPU 调用内核在启动时注册的中断回调处理程序。这是 注册处理程序并使用它的具体裸金属示例。
这个处理程序在环0中运行,它决定内核是否允许这个操作,执行这个操作,并在环3中重新启动 userland 程序。X86 _ 64
当使用 exec系统调用时(或者当使用内核 将启动 /init时) ,新用户进程的内核 准备寄存器和内存,然后跳转到入口点并将 CPU 切换到环3
exec
/init
如果程序试图做一些不规则的事情,比如写入禁止的寄存器或内存地址(因为分页) ,CPU 也会调用环0中的某些内核回调处理程序。
但是由于用户界面很顽皮,这次内核可能会终止进程,或者给它一个带信号的警告。
当内核引导时,它设置一个固定频率的硬件时钟,该时钟周期性地产生中断。
这个硬件时钟生成运行环0的中断,并允许它调度哪个 userland 进程被唤醒。
这样,即使进程没有进行任何系统调用,也可以进行调度。
有多个环的意义是什么?
分离内核和用户有两个主要优势:
怎么玩?
我已经创建了一个裸金属设置,应该是一个很好的方式操作环直接: https://github.com/cirosantilli/x86-bare-metal-examples
不幸的是,我没有耐心去做一个用户界面的例子,但是我做了分页设置,所以用户界面应该是可行的。我很乐意看到你的请求。
另外,Linux 内核模块在 ring 0中运行,因此您可以使用它们来尝试特权操作,例如读取控制寄存器: 如何进入控制寄存器 cr0,cr2,cr3从一个程序? 得到内存区段错误
这里是一个 方便的 QEMU + Buildroot 设置试验,而不杀死你的宿主。
内核模块的缺点是其他 kthread 正在运行,可能会干扰您的实验。但是在理论上,您可以用您的内核模块接管所有的中断处理程序并拥有系统,这实际上将是一个有趣的项目。
没有
虽然负环实际上并没有在 Intel 手册中引用,但是实际上有一些 CPU 模式比环0本身具有更多的功能,因此非常适合“负环”这个名称。
虚拟化中使用的管理程序模式就是一个例子。
详情请参阅:
手臂
在 ARM 中,环被称为异常级别,但主要思想保持不变。
ARMv8中存在4个异常级别,通常用于:
用户界面
EL1: 内核(ARM 术语中的“主管”)。
输入 svc指令(SuperVisor Call) ,以前称为 swi 在统一大会之前,这是用于进行 Linux 系统调用的指令。Hello world ARMv8示例:
svc
swi
你好 S
.text .global _start _start: /* write */ mov x0, 1 ldr x1, =msg ldr x2, =len mov x8, 64 svc 0 /* exit */ mov x0, 0 mov x8, 93 svc 0 msg: .ascii "hello syscall v8\n" len = . - msg
在 Ubuntu 16.04上用 QEMU 测试一下:
sudo apt-get install qemu-user gcc-arm-linux-gnueabihf arm-linux-gnueabihf-as -o hello.o hello.S arm-linux-gnueabihf-ld -o hello hello.o qemu-arm hello
这里是一个混凝土裸金属的例子,注册 SVC 处理程序并执行 SVC 调用。
例如 Xen。
使用 hvc指令(HyperVisor 调用)输入。
hvc
系统管理程序之于操作系统,就像操作系统之于用户界面一样。
例如,Xen 允许您在同一个系统上同时运行多个操作系统,如 Linux 或 Windows,并且它将操作系统彼此隔离以保证安全性和易于调试,就像 Linux 对用户界面程序所做的那样。
Hypervisor 是当今云基础设施的关键组成部分: 它们允许多个服务器在单个硬件上运行,使硬件使用率始终接近100% ,并节省了大量资金。
例如,AWS 使用 Xen 直到2017年 它向 KVM 的转移成为了新闻。
还有一个层次,TODO 的例子。
用 smc指令(安全模式调用)输入
smc
ARMv8架构参考模型 DDI0487C.a-第 D1章-AArch64系统级程序员的模型-图 D1-1很好地说明了这一点:
随着 ARMv8.1虚拟主机扩展(VHE)的出现,ARM 的情况发生了一些变化。这个扩展允许内核在 EL2中高效运行:
VHE 之所以被创建,是因为 Linux 内核虚拟化解决方案(如 KVM)已经超过了 Xen (参见 AWS 向 KVM 的转变) ,因为大多数客户只需要 Linux VM,而且正如你可以想象的,在一个单独的项目中,KVM 更简单,可能比 Xen 更高效。因此,现在主机 Linux 内核在这些情况下充当系统管理程序。
请注意,也许是事后诸葛亮的功劳,ARM 在特权级别上比 x86有更好的变数命名原则,而不需要负级别: 0级是最低级别,3级是最高级别。较高的水平往往比较低的水平更容易被创造出来。
可以使用 MRS指令 当前的执行模式/异常级别等是什么?查询当前 EL
MRS
ARM 不要求所有的异常级别都存在,以允许那些不需要特性来节省芯片面积的实现。ARMv8“异常水平”表示:
一个实现可能不包括所有的异常级别。所有的实现必须包括 EL0和 EL1。 EL2和 EL3是可选的。
例如,QEMU 默认为 EL1,但是可以通过命令行选项启用 EL2和 EL3: Qemu-system-aarch64在模拟 a53加电时进入 el1
在 Ubuntu 18.10上测试的代码片段。