内核空间和用户空间有什么区别?内核空间、内核线程、内核进程和内核堆栈是否意味着同一件事?另外,为什么我们需要这种差异化?
真正简化的答案是内核在内核空间中运行,而普通程序在用户空间中运行。用户空间基本上是沙盒的一种形式——它限制用户程序,因此他们不能弄乱其他程序或操作系统内核拥有的内存(和其他资源)。这限制了(但通常不会完全消除)他们做坏事的能力,比如让机器崩溃。
内核是操作系统的核心。它通常可以完全访问所有内存和机器硬件(以及机器上的所有其他东西)。为了使机器尽可能稳定,您通常只希望最受信任、经过充分测试的代码在内核模式/内核空间中运行。
堆栈只是内存的另一部分,因此很自然地它与其他内存是隔离的。
随机存取存储器 (RAM) 在逻辑上可以分为两个不同的区域,即内核空间和用户空间。(The Physical Addresses of the RAM are not actually divided only the Virtual Addresses,所有这些都由 MMU 实现)
内核在有权使用它的内存部分中运行。这部分内存不能被普通用户的进程直接访问,而内核可以访问所有部分内存。要访问内核的某些部分,用户进程必须使用预定义的系统调用,即 open
、read
、write
等。此外,C
库函数(如 printf
)调用系统调用 {3 } 反过来。
系统调用充当用户进程和内核进程之间的接口。访问权限被放置在内核空间中,以防止用户在不知不觉中弄乱内核。
因此,当发生系统调用时,会向内核发送软件中断。 CPU 可以暂时将控制权交给相关的中断处理程序。被中断中断的内核进程在中断处理程序完成其工作后恢复。
CPU环是最明显的区别
在 x86 保护模式下,CPU 始终处于 4 个环之一。 Linux内核只使用0和3:
0 表示内核
用户
这是内核与用户空间的最严格和最快速的定义。
为什么 Linux 不使用环 1 和 2:CPU Privilege Rings: Why rings 1 and 2 aren't used?
当前的戒指是如何确定的?
当前环由以下组合选择:
全局描述符表:内存中的 GDT 条目表,每个条目都有一个字段 Privl 对环进行编码。 LGDT 指令将地址设置为当前描述符表。另见:http://wiki.osdev.org/Global_Descriptor_Table
段寄存器 CS、DS 等,它们指向 GDT 中条目的索引。例如,CS = 0 表示 GDT 的第一个条目当前对正在执行的代码处于活动状态。
每个戒指能做什么?
CPU 芯片的物理构造使得:
ring 0 可以做任何事情
ring 3 无法运行多个指令并写入多个寄存器,最值得注意的是:无法更改自己的 ring!否则,它可能会将自己设置为 ring 0,而 ring 将无用。换句话说,不能修改当前的段描述符,它决定了当前的环。无法修改页表:x86 分页如何工作?换句话说,不能修改CR3寄存器,而分页本身阻止了对页表的修改。出于安全/易于编程的原因,这可以防止一个进程看到其他进程的内存。无法注册中断处理程序。这些是通过写入内存位置来配置的,这也可以通过分页来防止。处理程序在环 0 中运行,并且会破坏安全模型。也就是说,不能使用 LGDT 和 LIDT 指令。不能像 in 和 out 那样执行 IO 指令,因此具有任意硬件访问权限。否则,例如,如果任何程序可以直接从磁盘读取,文件权限将毫无用处。更准确地说,感谢 Michael Petch:操作系统实际上可以在 ring 3 上允许 IO 指令,这实际上是由任务状态段控制的。环 3 不可能允许自己这样做,如果它一开始就没有的话。 Linux总是不允许它。另请参阅:为什么 Linux 不通过 TSS 使用硬件上下文切换?
不能改变自己的戒指!否则,它可能会将自己设置为 ring 0,而 ring 将无用。换句话说,不能修改当前的段描述符,它决定了当前的环。
无法修改页表:x86 分页如何工作?换句话说,不能修改CR3寄存器,而分页本身阻止了对页表的修改。出于安全/易于编程的原因,这可以防止一个进程看到其他进程的内存。
无法注册中断处理程序。这些是通过写入内存位置来配置的,这也可以通过分页来防止。处理程序在环 0 中运行,并且会破坏安全模型。也就是说,不能使用 LGDT 和 LIDT 指令。
不能像 in 和 out 那样执行 IO 指令,因此具有任意硬件访问权限。否则,例如,如果任何程序可以直接从磁盘读取,文件权限将毫无用处。更准确地说,感谢 Michael Petch:操作系统实际上可以在 ring 3 上允许 IO 指令,这实际上是由任务状态段控制的。环 3 不可能允许自己这样做,如果它一开始就没有的话。 Linux总是不允许它。另请参阅:为什么 Linux 不通过 TSS 使用硬件上下文切换?
程序和操作系统如何在环之间转换?
当 CPU 开启时,它开始运行 ring 0 中的初始程序(很好,但它是一个很好的近似值)。您可以将这个初始程序视为内核(但它通常是一个引导加载程序,然后调用仍在环 0 中的内核)。
当用户态进程希望内核为它做一些事情(例如写入文件)时,它会使用产生中断的指令(例如 int 0x80 或系统调用)向内核发出信号。 x86-64 Linux 系统调用 hello world 示例:.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 会调用内核在启动时注册的中断回调处理程序。这是一个注册处理程序并使用它的具体裸机示例。这个处理程序在 ring 0 中运行,它决定内核是否允许这个动作,执行这个动作,并在 ring 3 中重新启动用户态程序。 x86_64
当使用 exec 系统调用时(或内核将启动 /init 时),内核准备新用户态进程的寄存器和内存,然后跳转到入口点并将 CPU 切换到 ring 3
如果程序试图做一些顽皮的事情,比如写入一个被禁止的寄存器或内存地址(因为分页),CPU 也会在 ring 0 中调用一些内核回调处理程序。但是由于用户空间很顽皮,内核这次可能会杀死进程,或用信号警告它。
当内核启动时,它会设置一个固定频率的硬件时钟,周期性地产生中断。该硬件时钟生成运行 ring 0 的中断,并允许它安排唤醒哪些用户态进程。这样,即使进程没有进行任何系统调用,也可以进行调度。
拥有多个戒指有什么意义?
分离内核和用户空间有两个主要优点:
制作程序更容易,因为您更确定一个不会干扰另一个。例如,一个用户态进程不必担心由于分页而覆盖另一个程序的内存,也不必担心将硬件置于另一个进程的无效状态。
它更安全。例如,文件权限和内存分离可以防止黑客应用程序读取您的银行数据。当然,这假设您信任内核。
如何玩弄它?
我创建了一个裸机设置,应该是直接操作环的好方法:https://github.com/cirosantilli/x86-bare-metal-examples
不幸的是,我没有耐心做一个用户空间的例子,但我确实做到了分页设置,所以用户空间应该是可行的。我很想看到一个拉取请求。
或者,Linux 内核模块在 ring 0 中运行,因此您可以使用它们来尝试特权操作,例如读取控制寄存器:How to access the control registers cr0,cr2,cr3 from a program? Getting segmentation fault
这是一个convenient QEMU + Buildroot setup,可以在不杀死您的主机的情况下进行尝试。
内核模块的缺点是其他 kthread 正在运行,可能会干扰您的实验。但理论上你可以用你的内核模块接管所有的中断处理程序并拥有系统,这实际上是一个有趣的项目。
负环
虽然英特尔手册中实际上并未提及负环,但实际上存在比环 0 本身具有更多功能的 CPU 模式,因此非常适合“负环”名称。
一个例子是虚拟化中使用的管理程序模式。
有关详细信息,请参阅:
https://security.stackexchange.com/questions/129098/what-is-protection-ring-1
https://security.stackexchange.com/questions/216527/ring-3-exploits-and-existence-of-other-rings
手臂
在 ARM 中,环被称为异常级别,但主要思想保持不变。
ARMv8 中存在 4 个异常级别,常用的有:
EL0:用户空间
EL1:内核(ARM 术语中的“主管”)。用svc指令(SuperVisor Call)进入,以前统一汇编前称为swi,是用来进行Linux系统调用的指令。 Hello world ARMv8 示例:hello.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 = . - 味精 GitHub 上游。在 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 调用的具体裸机示例。
EL2:管理程序,例如 Xen。使用 hvc 指令(HyperVisor Call)输入。管理程序之于操作系统,就像操作系统之于用户空间。例如,Xen 允许您在同一系统上同时运行多个操作系统(如 Linux 或 Windows),并且它将操作系统彼此隔离以确保安全性和易于调试,就像 Linux 对用户级程序所做的那样。管理程序是当今云基础架构的关键部分:它们允许多台服务器在单个硬件上运行,使硬件使用率始终接近 100% 并节省大量资金。例如,AWS 一直使用 Xen,直到 2017 年它转向 KVM 成为新闻。
EL3:又一个层次。待办事项示例。使用 smc 指令输入(安全模式调用)
ARMv8 Architecture Reference Model DDI 0487C.a - 第 D1 章 - AArch64 系统级程序员模型 - 图 D1-1 很好地说明了这一点:
https://i.stack.imgur.com/PpAku.png
随着 ARMv8.1 Virtualization Host Extensions (VHE) 的出现,ARM 的情况发生了一些变化。这个扩展允许内核有效地在 EL2 中运行:
https://i.stack.imgur.com/iasWq.png
之所以创建 VHE,是因为 KVM 等 Linux 内核内虚拟化解决方案已经超越 Xen(例如,参见上面提到的 AWS 向 KVM 的迁移),因为大多数客户端只需要 Linux VM,并且您可以想象,它们都在一个单一的项目中,KVM 比 Xen 更简单并且可能更高效。因此,现在主机 Linux 内核在这些情况下充当管理程序。
请注意 ARM 可能是出于事后诸葛亮的好处,它对特权级别的命名约定比 x86 更好,而不需要负级别:0 是较低的,3 是最高的。较高级别往往比较低级别更频繁地创建。
当前的EL可以通过MRS
指令查询:what is the current execution mode/exception level, etc?
ARM 不需要所有异常级别都存在,以允许不需要该功能以节省芯片面积的实现。 ARMv8“异常级别”说:
一个实现可能不包括所有的异常级别。所有实现必须包括 EL0 和 EL1。 EL2 和 EL3 是可选的。
例如 QEMU 默认为 EL1,但可以使用命令行选项启用 EL2 和 EL3:qemu-system-aarch64 entering el1 when emulating a53 power up
在 Ubuntu 18.10 上测试的代码片段。
内核空间和虚拟空间是虚拟内存的概念......这并不意味着Ram(您的实际内存)分为内核空间和用户空间。每个进程都有虚拟内存,分为内核空间和用户空间。
所以说“随机存取存储器(RAM)可以分为两个不同的区域,即内核空间和用户空间。”是错的。
&关于“内核空间与用户空间”的事情
当一个进程被创建时,它的虚拟内存被划分为用户空间和内核空间,其中用户空间区域包含进程的数据、代码、堆栈、堆,内核空间包含进程的页表等内容,内核数据结构和内核代码等。要运行内核空间代码,控制必须转移到内核模式(使用0x80软件中断进行系统调用),内核堆栈基本上在内核空间中当前执行的所有进程之间共享。
内核空间和用户空间是特权操作系统功能和受限用户应用程序的分离。分离对于防止用户应用程序洗劫您的计算机是必要的。如果任何旧的用户程序可以开始将随机数据写入您的硬盘驱动器或从另一个用户程序的内存空间读取内存,那将是一件坏事。
用户空间程序不能直接访问系统资源,因此访问由操作系统内核代表程序处理。用户空间程序通常通过系统调用向操作系统发出此类请求。
内核线程、进程、堆栈并不意味着同一件事。它们是内核空间的类似构造,就像它们在用户空间中的对应物一样。
每个进程都有自己的 4GB 虚拟内存,通过页表映射到物理内存。虚拟内存主要分为两部分:3 GB 用于进程使用,1 GB 用于内核使用。您创建的大多数变量都位于地址空间的第一部分。那部分称为用户空间。最后一部分是内核所在的位置,对所有进程都是通用的。这称为内核空间,大部分空间映射到启动时加载内核映像的物理内存的起始位置。
地址空间的最大大小取决于 CPU 上地址寄存器的长度。
在具有 32 位地址寄存器的系统上,地址空间的最大大小为 232 字节,即 4 GiB。同样,在 64 位系统上,可以寻址 264 个字节。
这样的地址空间称为虚拟内存或虚拟地址空间。它实际上与物理 RAM 大小无关。
在 Linux 平台上,虚拟地址空间分为内核空间和用户空间。
称为 task size limit 或 TASK_SIZE
的特定于体系结构的常量标记了拆分发生的位置:
从 0 到 TASK_SIZE-1 的地址范围被分配给用户空间;
从 TASK_SIZE 到 232-1(或 264-1)的剩余部分分配给内核空间。
例如,在特定的 32 位系统上,用户空间可能占用 3 GiB,内核空间可能占用 1 GiB。
类 Unix 操作系统中的每个应用程序/程序都是一个进程;每一个都有一个唯一的标识符,称为进程标识符(或简称为进程 ID,即 PID)。 Linux 提供了两种创建进程的机制:1. fork()
系统调用,或 2. exec()
调用。
内核线程是一个轻量级进程,也是一个正在执行的程序。单个进程可能由多个线程组成,这些线程共享相同的数据和资源,但在程序代码中采用不同的路径。 Linux 提供 clone()
系统调用来生成线程。
内核线程的示例用途是:RAM 的数据同步,帮助调度程序在 CPU 之间分配进程等。
简而言之:内核运行在内核空间中,内核空间可以完全访问所有内存和资源,可以说内存分为两部分,一部分给内核,一部分给用户自己的进程,(用户空间)运行正常的程序,用户space 不能直接访问内核空间,因此它向内核请求使用资源。通过系统调用(glibc 中的预定义系统调用)
有一个语句可以简化不同的“用户空间只是内核的测试负载”......
非常清楚:处理器架构允许 CPU 在两种模式下运行,内核模式和用户模式,硬件指令允许从一种模式切换到另一种模式。
内存可以标记为用户空间或内核空间的一部分。
CPU运行在用户态时,CPU只能访问用户空间的内存,而cpu试图访问内核空间的内存,结果是“硬件异常”,当CPU运行在内核态时,CPU可以直接访问到内核空间和用户空间......
内核空间是指一个内存空间只能被内核访问。在 32bit linux 上它是 1G(从 0xC0000000 到 0xffffffff 作为虚拟内存地址)。内核创建的每个进程也是一个内核线程,所以对于一个进程,有两个堆栈:一个在用户空间,另一个在内核内核线程的空间。
内核堆栈占用 2 个页面(32 位 linux 中为 8k),包括一个 task_struct(大约 1k)和实际堆栈(大约 7k)。后者用于在内核函数中存储一些自动变量或函数调用参数或函数地址。这是代码(Processor.h (linux\include\asm-i386)):
#define THREAD_SIZE (2*PAGE_SIZE)
#define alloc_task_struct() ((struct task_struct *) __get_free_pages(GFP_KERNEL,1))
#define free_task_struct(p) free_pages((unsigned long) (p), 1)
__get_free_pages(GFP_KERNEL,1)) 表示将内存分配为 2^1=2 页。
但是进程栈是另外一回事,它的地址就在0xC0000000(32bit linux)下面,它的大小可以相当大,用于用户空间函数调用。
所以这里有一个关于系统调用的问题,它在内核空间中运行,但在用户空间被进程调用,它是如何工作的? linux会将其参数和函数地址放入内核堆栈还是进程堆栈? Linux的解决方案:所有系统调用都由软件中断INT 0x80触发。在 entry.S (linux\arch\i386\kernel) 中定义,这里有一些行例如:
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.long SYMBOL_NAME(sys_write)
.long SYMBOL_NAME(sys_open) /* 5 */
.long SYMBOL_NAME(sys_close)
Linux Kernel 指的是在 Kernel 模式下运行的所有东西,它由几个不同的层组成。在最低层,内核通过 HAL 与硬件交互。在中间层,UNIX 内核分为 4 个不同的区域。四个区域中的第一个处理字符设备、原始和熟 TTY 以及终端处理。第二个区域处理网络设备驱动程序、路由协议和套接字。第三个区域处理磁盘设备驱动程序、页面和缓冲区缓存、文件系统、虚拟内存、文件命名和映射。第四个也是最后一个区域处理进程调度、调度、创建和终止以及信号处理。最重要的是,我们拥有内核的顶层,其中包括系统调用、中断和陷阱。此级别用作每个较低级别功能的接口。程序员使用各种系统调用和中断来与操作系统的功能进行交互。
简而言之,内核空间是 linux 内核运行的内存部分(在 linux 的情况下是顶部 1 GB 虚拟空间),而用户空间是用户应用程序运行的内存部分(在 Linux 的情况下是底部 3 GB 的虚拟内存。如果你想知道更多,请参阅下面给出的链接:)
http://learnlinuxconcepts.blogspot.in/2014/02/kernel-space-and-user-space.html
内核空间和用户空间是逻辑空间。
https://i.stack.imgur.com/DzNLZ.png
当处于/高于特定特权模式时,可以执行特定的机器指令。
由于这种设计,您正在为执行环境提供系统保护或沙盒。
内核是一段代码,它管理你的硬件并提供系统抽象。所以它需要能够访问所有的机器指令。它是最值得信赖的软件。所以我应该以最高特权执行。并且环级别 0 是最高特权模式。所以 Ring Level 0 也被称为内核模式。
用户应用程序是来自任何第三方供应商的软件,您不能完全信任它们。如果有恶意的人可以完全访问所有机器指令,他可以编写代码来使您的系统崩溃。因此,应向应用程序提供对有限指令集的访问权限。 Ring Level 3 是最低权限模式。因此,您的所有应用程序都以该模式运行。因此,Ring Level 3 也称为用户模式。
注意:我没有获得 Ring Level 1 和 2。它们基本上是具有中间权限的模式。因此,设备驱动程序代码可能是使用此权限执行的。 AFAIK,linux 仅使用 Ring Level 0 和 3 分别用于内核代码执行和用户应用程序。
因此,在内核模式下发生的任何操作都可以被视为内核空间。并且在用户模式下发生的任何操作都可以被视为用户空间。
试图给出一个非常简单的解释
虚拟内存分为内核空间和用户空间。内核空间是内核进程将运行的虚拟内存区域,用户空间是用户进程将运行的虚拟内存区域。
这种划分是内存访问保护所必需的。
每当引导加载程序在将内核加载到 RAM 中的某个位置后启动内核时(通常在基于 ARM 的控制器上),它需要确保控制器处于禁用 FIQ 和 IRQ 的超级用户模式。
正确答案是:没有内核空间和用户空间这回事。处理器指令集具有设置破坏性事物的特殊权限,例如页表映射的根,或访问硬件设备内存等。
内核代码具有最高级别的权限,用户代码最低。这可以防止用户代码使系统崩溃、修改其他程序等。
通常,内核代码保存在与用户代码不同的内存映射下(就像用户空间保存在彼此不同的内存映射中一样)。这就是“内核空间”和“用户空间”术语的来源。但这不是一个硬性规定。例如,由于 x86 间接要求始终映射其中断/陷阱处理程序,因此必须将内核的一部分(或某些操作系统全部)映射到用户空间。同样,这并不意味着此类代码具有用户权限。
为什么需要内核/用户划分?一些设计师不同意它实际上是必要的。微内核架构基于这样的想法,即代码的最高特权部分应尽可能小,所有重要操作都在用户特权代码中完成。您需要研究为什么这可能是一个好主意,它不是一个简单的概念(并且以既有优点也有缺点而闻名)。
内存获取分为两个不同的区域:
用户空间,这是一组运行正常用户进程的位置(即除内核之外的所有位置)。内核的作用是管理在这个空间中运行的应用程序,以免相互干扰,以及机器。
内核空间,即内核代码存放的位置,并在其下执行。
在用户空间下运行的进程只能访问有限的内存部分,而内核可以访问所有内存。在用户空间中运行的进程也无权访问内核空间。用户空间进程只能通过内核暴露的接口——系统调用访问内核的一小部分。如果进程执行系统调用,则会向内核发送软件中断,然后内核调度适当的中断处理程序并继续处理程序完成后的工作。
这种划分需要架构支持,有一些指令可以在特权模式下访问。
在页表中,如果用户进程尝试访问位于内核地址范围内的地址,我们将获得访问详细信息,那么它将给出特权冲突错误。
因此,要进入特权模式,需要运行像陷阱这样的指令,它将 CPU 模式更改为特权并提供对指令和内存区域的访问权限
在 Linux 中有两个空间,第一个是用户空间,另一个是内核空间。用户空间仅包含您要运行的用户应用程序。作为内核服务,有进程管理、文件管理、信号处理、内存管理、线程管理等等。如果您从用户空间运行应用程序,该应用程序仅与内核服务交互。并且该服务与硬件和内核之间存在的设备驱动程序进行交互。内核空间和用户空间分离的主要好处是我们可以通过virus.bcaz 对用户空间中存在的所有用户应用程序进行安全保护,并且服务存在于内核空间中。这就是为什么 linux 不受病毒影响的原因。
不定期副业成功案例分享