内核空间和用户空间的区别是什么?内核空间、内核线程、内核进程和内核堆栈是同一个意思吗?还有,为什么我们需要这种区分?
真的的简化答案是,内核在内核空间中运行,普通程序在用户空间中运行。用户空间基本上是一种沙盒形式——它限制用户程序,使它们不能干扰其他程序或操作系统内核拥有的内存(和其他资源)。这限制了(但通常并不完全消除)他们做坏事的能力,比如机器崩溃。
内核是操作系统的核心。它通常可以完全访问所有内存和机器硬件(以及机器上的所有其他东西)。为了使机器尽可能稳定,通常只需要在内核模式/内核空间中运行最受信任、测试良好的代码。
堆栈只是内存的另一部分,所以很自然地,它与内存的其余部分一起被隔离。
内核空间和用户空间是特权操作系统功能和受限用户应用程序的分离。为了防止用户应用程序洗劫您的计算机,这种分离是必要的。如果任何旧的用户程序可以开始写随机数据到您的硬盘驱动器或从另一个用户程序的内存空间读取内存,这将是一件坏事。
用户空间程序不能直接访问系统资源,因此访问是由操作系统内核代表程序处理的。用户空间程序通常通过系统调用向操作系统发出这样的请求。
内核线程、进程、堆栈并不意味着相同的东西。它们是内核空间的类似构造,与用户空间中的构造类似。
每个进程都有自己的4GB 虚拟内存,这些内存通过页表映射到物理内存。虚拟内存主要分为两部分: 3GB 用于进程,1GB 用于内核。您创建的大多数变量位于地址空间的第一部分。这部分称为用户空间。最后一部分是内核所在的位置,对于所有进程都是通用的。这称为内核空间,其中大部分空间被映射到物理内存的起始位置,内核映像在引导时被加载到这些位置。
在逻辑上,可以将 随机存取存储器(RAM)划分为两个不同的区域,即内核空间和用户空间。(RAM 的 < em > 物理地址 实际上并不仅仅划分 < em > 虚拟地址 ,所有这些都由 MMU实现)
内核运行在有权使用它的内存部分。这部分内存不能被普通用户的进程直接访问,而内核可以访问内存的所有部分。要访问内核的某些部分,用户进程必须使用预定义的系统调用,例如 open、 read、 write等。此外,C库函数(如 printf)依次调用系统调用 write。
open
read
write
C
printf
系统调用充当用户进程和内核进程之间的接口。访问权限被放置在内核空间上,以防止用户在不知情的情况下干扰内核。
因此,当系统调用发生时,软件中断被发送到内核。中央处理器可能会暂时把控制权移交给相关的 interrupt handler 程序。被中断停止的内核进程在 interrupt handler 程序完成其工作后继续运行。
在 Linux 中有两个空间,一个是用户空间,另一个是内核空间。用户空间只包含要运行的用户应用程序。作为内核服务,存在进程管理、文件管理、信号处理、内存管理、线程管理等多种服务。如果您从该应用程序仅与内核服务交互的用户空间运行该应用程序。该服务与存在于硬件和内核之间的设备驱动程序进行交互。 内核空间和用户空间分离的主要好处是,我们可以通过 virus.bcaz 实现用户空间中所有用户应用程序的安全性,而服务则存在于内核空间中。这就是为什么 linux 不会受到病毒的影响。
作者: Sunil Yadav 在 Quora 上说:
Linux 内核指的是在内核模式下运行的所有内容 由几个不同的层组成。在最底层,内核 通过 HAL 与硬件交互 UNIX 内核分为4个不同的区域 区域处理字符设备,生的和熟的 TTY 和终端 第二个区域处理网络设备驱动程序、路由 第三个区域处理磁盘设备驱动程序, 页和缓冲区缓存,文件系统,虚拟内存,文件命名和 第四个也是最后一个区域处理流程调度, 调度,创建和终止以及信号处理。 最重要的是,我们有内核的顶层,其中包括 系统调用、中断和陷阱 接口到每个较低级别的函数。程序员使用 各种系统调用和中断与特性交互 操作系统。
简而言之: 内核运行在内核空间中,内核空间对所有内存和资源都有完全的访问权限,可以说内存分为两部分,一部分为内核,一部分为用户自己的进程,(用户空间)运行普通程序,用户空间不能直接访问内核空间,所以它请求内核使用资源。通过 syscall (glibc 中预定义的系统调用)
有一个声明,简化了不同的“ User Space 仅仅是内核的一个测试负载”..。
要非常清楚: 处理器架构允许 CPU 在两种模式下运行,内核模式和用户模式,硬件指令允许从一种模式切换到另一种模式。
内存可以标记为用户空间或内核空间的一部分。
当 CPU 以用户模式运行时,CPU 只能访问存在于用户空间中的内存,而 CPU 尝试访问内核空间中的内存,结果是一个“硬件异常”,当 CPU 以内核模式运行时,CPU 可以直接访问内核空间和用户空间..。
试图给出一个非常简单的解释
虚拟内存分为内核空间和用户空间。 内核空间是运行内核进程的虚拟内存区域,而用户空间是运行用户进程的虚拟内存区域。
这个部分是内存访问保护所必需的。
无论何时,引导加载程序在将内核加载到 RAM 中的某个位置后启动内核(通常在基于 ARM 的控制器上) ,它都需要确保控制器处于管理模式,禁用 FIQ 和 IRQ。
IN short kernel space 是运行 Linux 内核的内存部分(对于 Linux 来说是顶部1GB 的虚拟空间) ,而 user space 是运行用户应用程序的内存部分(对于 Linux 来说是底部3GB 的虚拟内存)。如果你想知道更多,请看下面的链接:)
Http://learnlinuxconcepts.blogspot.in/2014/02/kernel-space-and-user-space.html
内核空间意味着内存空间只能由内核触及。在32位 Linux 上,它是1G (从0xC000000到0xffffff 作为虚拟内存地址)。内核创建的每个进程也是一个内核线程,因此对于一个进程,有两个堆栈: 一个堆栈在用户空间中用于这个进程,另一个堆栈在内核空间中用于内核线程。
内核堆栈占用了2个页面(32位 linux 中为8k) ,包括一个 task _ struct (大约1k)和真正的堆栈(大约7k)。后者用于在内核函数中存储一些自动变量或函数调用参数或函数地址。下面是代码(Processor.h (linux 包括 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(32位 linux) ,它的大小可以比较大,用于用户空间函数调用。
因此,这里有一个问题来为系统调用,它是运行在内核空间,但被调用的进程在用户空间,它是如何工作的?Linux 会把它的参数和函数地址放到内核堆栈或进程堆栈中吗?Linux 的解决方案: 所有系统调用都由软件中断 INT0x80触发。 在 entry y.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)
内核空间和虚拟空间是虚拟内存的概念... ... 这并不意味着 Ram (你的实际内存)被划分为内核和用户空间。 每个进程都有一个分为内核和用户空间的虚拟内存 所以说 随机存取存储器(RAM)可以分为两个不同的区域,即内核空间和用户空间是错的。 关于“内核空间 VS 用户空间”的问题 当一个进程被创建时,它的虚拟内存被划分为用户空间和内核空间,其中用户空间区域包含数据、代码、堆、进程堆和内核空间包含进程的页表、内核数据结构和内核代码等内容。 要运行内核空间代码,控制必须转换到内核模式(使用0x80软件中断进行系统调用) ,内核堆栈基本上是在当前在内核空间中执行的所有进程之间共享的。
所以说 随机存取存储器(RAM)可以分为两个不同的区域,即内核空间和用户空间是错的。
关于“内核空间 VS 用户空间”的问题
当一个进程被创建时,它的虚拟内存被划分为用户空间和内核空间,其中用户空间区域包含数据、代码、堆、进程堆和内核空间包含进程的页表、内核数据结构和内核代码等内容。 要运行内核空间代码,控制必须转换到内核模式(使用0x80软件中断进行系统调用) ,内核堆栈基本上是在当前在内核空间中执行的所有进程之间共享的。
地址空间的最大大小取决于 CPU 上地址寄存器的长度。
在具有32位地址寄存器的系统上,地址空间的最大大小为232字节,即4GiB。 类似地,在64位系统上,可以寻址264字节。
这样的地址空间称为 虚拟存储器或 虚拟地址空间虚拟地址空间。它实际上与物理 RAM 大小无关。
在 Linux 平台上,虚拟地址空间分为内核空间和用户空间。
一个特定于体系结构的常量 任务规模限制任务规模限制,或者 TASK_SIZE,标记了分离发生的位置:
TASK_SIZE
地址范围从0到 TASK_SIZE-1分配给用户空间;
从 TASK_SIZE到232-1(或264-1)的其余部分被分配给内核空间。
例如,在一个特定的32位系统上,用户空间可以占用3GiB,内核空间可以占用1GiB。
类Unix系统中的每个应用程序/程序都是一个进程,每个进程都有一个称为唯一标识符的进程ID (或简称为 进程 ID,即 PID)。Linux 提供了两种创建进程的机制: 1。fork()系统调用,或2。exec()呼叫。
fork()
exec()
内核线程是一个轻量级进程,也是一个正在执行的程序。 一个进程可能由几个线程组成,这些线程共享相同的数据和资源,但是在程序代码中采用不同的路径。Linux 提供了一个 clone()系统调用来生成线程。
clone()
使用内核线程的例子有: 内存数据同步,帮助调度器在 CPU 之间分配进程,等等。
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上测试的代码片段。
正确的答案是: 没有内核空间和用户空间这样的东西。处理器指令集有特殊的权限来设置破坏性的东西,如页表映射的根,或访问硬件设备内存等。
内核代码拥有最高级别的权限,而用户代码拥有最低级别的权限。这可以防止用户代码崩溃系统、修改其他程序等等。
通常,内核代码保存在与用户代码不同的内存映射中(就像用户空间保存在彼此不同的内存映射中一样)。这就是“内核空间”和“用户空间”术语的来源。但这并不是一条硬性规定。例如,由于 x86间接地要求其中断/陷阱处理程序始终映射,因此内核的部分(或部分操作系统全部)必须映射到用户空间。同样,这并不意味着这样的代码拥有用户特权。
为什么需要内核/用户划分?一些设计师不同意它实际上是必要的。微内核体系结构的基本思想是,代码的最高特权部分应该尽可能小,所有重要的操作都在用户特权代码中完成。您将需要研究为什么这可能是一个好主意,它不是一个简单的概念(并且因为既有优点又有缺点而闻名)。
内核空间和用户空间是逻辑空间。
大多数现代处理器都设计成以不同的特权模式运行。X86计算机可以以4种不同的特权模式运行。
当处于/上述特定特权模式时,可以执行特定的指令。
由于这种设计,您可以提供系统保护,或者对执行环境进行“沙盒”。
内核是一段代码,它管理您的硬件并提供系统抽象。所以它需要所有指令的访问权限。它是最值得信赖的软件。所以我应该以最高特权被处决。而 戒指等级0是最具特权的模式。所以 戒指等级0也称为 内核模式。
用户应用程序是来自任何第三方供应商的软件,你不能完全信任他们。有恶意的人可以编写一个代码来破坏你的系统,如果他可以完全访问所有的指令。因此,应用程序应提供访问有限的指令集。而 戒指第三层是最没有特权的模式。因此,所有应用程序都在这种模式下运行。因此,戒指第三层也称为 用户模式。
注意: 我没有得到戒指等级1和2。它们基本上是具有中间特权的模式。所以可能是设备驱动程序代码使用这种特权执行。在 AFAIK 中,Linux 分别只使用 Ring Level 0和 Ring Level 3执行内核代码和用户应用程序。
因此,在内核模式下发生的任何操作都可以视为内核空间。 在用户模式下发生的任何操作都可以视为用户空间。
这种划分需要体系结构的支持,有一些指令是以特权模式访问的。
在分页表中,如果用户进程试图访问位于内核地址范围内的地址,就会产生权限冲突故障。
因此,要进入特权模式,需要运行指令,如陷阱,改变 CPU 模式的特权和访问指令以及内存区域