内核空间和用户空间有什么区别?


Answers:


117

真正简单的答案是,内核在内核空间中运行,正常程序在用户空间中运行。用户空间基本上是沙盒的一种形式-它限制了用户程序,因此它们不会弄乱其他程序或OS内核拥有的内存(和其他资源)。这样就限制了(但通常并不能完全消除)他们执行坏事(例如使机器崩溃)的能力。

内核是操作系统的核心。通常,它可以完全访问所有内存和计算机硬件(以及计算机上的所有其他内容)。为了使机器尽可能稳定,通常只希望在内核模式/内核空间中运行最受信任的,经过良好测试的代码。

堆栈只是内存的另一部分,因此很自然地将其与其余的内存隔离开来。


4
所以说我的系统中是否有10个进程。是每个进程都有自己的堆栈,该堆栈分为一个用户堆栈和一个内核堆栈,还是所有进程共享一个内核堆栈?
kc3 2011年

10
@ kc3:至少部分取决于操作系统,但是我相信大多数人在内核代表一个进程(例如I / O)执行事务时使用的每个进程都有一个内核模式堆栈,至少还有一个内核堆栈,仅供内核内部使用(例如,用于调度)。
杰里·科芬

2
是否存在内核进程,并且与用户空间进程有什么关系或所问的不同?
Victor Choy

因此,就其本身而言,要运行用户空间进程,必须将其映射到内核空间
roottraveller

@roottraveller:我不确定是什么给了你这个主意,但是不,一点也不。同时,用户空间进程通常具有一些(或多或少隐藏的)内核空间内存,因此(例如)您的进程将具有用户空间栈和在您使用时使用的内核空间栈进行需要在内核模式下运行的OS调用。
杰里·科芬

64

随机存取存储器(RAM),逻辑上可以分为两个不同的区域分别是-在内核空间和用户空间(物理地址的RAM都没有实际划分只是虚拟地址,全部由本实施MMU

内核在有权使用的内存部分中运行。普通用户的进程无法直接访问内存的这一部分,而内核可以访问内存的所有部分。要访问内核的某些部分,用户进程必须使用预定义的系统调用open,例如readwrite等等。此外,C库函数如依次printf调用系统调用write

系统调用充当用户进程和内核进程之间的接口。访问权限放置在内核空间上,以防止用户在不知不觉中搞乱内核。

因此,当发生系统调用时,软件中断将发送到内核。CPU可以将控制权临时移交给相关的中断处理程序例程。被中断暂停的内核进程将在中断处理程序例程完成其工作之后恢复。


2
该答案的第一部分是不正确的。RAM不分为内核空间和用户空间。虚拟内存是。我尝试编辑答案,但编辑队列已满天。请解决。有关更多信息,请参见下面的Varun答案。
MeLikeyCode '17

1
@MeLikeyCode在尝试提供一个可以广泛理解的答案的上下文中,这不是合理的简化吗?

2
@问题官员,我认为,大幅度的简化可能会提供错误的基本理解。每个人都应该弄清楚物理内存没有分为用户空间和内核空间,但是内核提供了虚拟内存的抽象,虚拟内存又分为内核和用户空间,后来又映射到了物理内存上。
dshil

22

内核空间和虚拟空间是虚拟内存的概念。这并不意味着Ram(您的实际内存)分为内核和用户空间。每个进程都有虚拟内存,分为内核空间和用户空间。

因此说:“随机存取存储器(RAM)可以分为两个不同的区域,即内核空间和用户空间。” 是错的。

关于“内核空间与用户空间”的问题

创建进程并将其虚拟内存分为用户空间和内核空间时,其中用户空间区域包含进程的数据,代码,堆栈,堆栈和内核空间包含诸如进程的页表之类的内容,内核数据结构和内核代码等。要运行内核空间代码,控制必须转换为内核模式(使用0x80软件中断进行系统调用),并且内核堆栈基本上由当前在内核空间中执行的所有进程共享。


1
一个普通的进程有自己的内核空间和用户空间吗?
Victor Choy

@VictorChoy,一个进程执行两种模式:用户和内核。进程看到的内存是它唯一的。但是,对于以内核模式运行(执行内核代码)的每个进程,只有一个空间-内核空间。
dshil

另外,请注意,在创建进程时,它的虚拟内存分为两部分(用户和内核),其中虚拟地址的一部分保留给用户模式,另一部分保留给内核模式。
dshil

19

CPU环是最明显的区别

在x86保护模式下,CPU始终处于4个振铃之一。Linux内核仅使用0和3:

  • 内核为0
  • 3个用户

这是内核vs用户态的最难,最快速的定义。

为什么Linux不使用环1和2:CPU特权环:为什么不使用环1和2?

如何确定当前环?

当前铃声是通过以下方式选择的:

  • 全局描述符表:GDT条目的内存表,每个条目都有一个Privl对环进行编码的字段。

    LGDT指令将地址设置为当前描述符表。

    另请参阅:http : //wiki.osdev.org/Global_Descriptor_Table

  • 段寄存器CS,DS等指向GDT中某个条目的索引。

    例如,CS = 0意味着GDT的第一个条目当前对于执行代码是活动的。

每个环能做什么?

CPU芯片的物理构造是:

  • 环0可以做什么

  • 环3无法运行多个指令并无法写入多个寄存器,最值得注意的是:

    • 不能改变自己的戒指!否则,它可能会将自己设置为环0,并且环将无用。

      换句话说,不能修改确定当前环的当前段描述符

    • 无法修改页表:x86分页如何工作?

      换句话说,不能修改CR3寄存器,分页本身会阻止页表的修改。

      出于安全性/易于编程的原因,这可防止一个进程看到其他进程的内存。

    • 无法注册中断处理程序。通过写入存储位置来配置这些地址,这也可以通过分页来防止。

      处理程序在环0中运行,并且会破坏安全模型。

      换句话说,不能使用LGDT和LIDT指令。

    • 无法执行IO指令(如in和)out,因此可以进行任意硬件访问。

      否则,例如,如果任何程序可以直接从磁盘读取,则文件权限将无用。

      更确切地说,要感谢Michael Petch:操作系统实际上可能在环3上允许IO指令,这实际上是由Task状态段控制的。

      如果环3最初没有它,则它不可能授予自己这样做的权限。

      Linux总是不允许这样做。另请参阅:为什么Linux不通过TSS使用硬件上下文切换?

程序和操作系统如何在环之间转换?

  • 当CPU开启时,它将在环0中开始运行初始程序(虽然不错,但是这是一个很好的近似值)。您可以认为此初始程序是内核(但通常是引导加载程序,然后仍在环0中调用内核)。

  • 当用户级进程希望内核为其执行某些操作(例如写入文件)时,它将使用生成中断的指令,例如int 0x80syscall向内核发出信号。x86-64 Linux 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),内核准备新的userland进程的寄存器和内存,然后跳转到入口点并将CPU切换到ring 3

  • 如果程序试图做一些顽皮的事情,例如写入禁止的寄存器或内存地址(由于页面调度),CPU还会在环0中调用一些内核回调处理程序。

    但是由于用户空间很顽皮,内核这次可能会终止进程,或者发出警告并带有信号。

  • 内核启动时,它将设置一个固定频率的硬件时钟,该时钟会定期产生中断。

    该硬件时钟生成运行于环0的中断,并允许其安排要唤醒的用户区进程。

    这样,即使进程未进行任何系统调用,调度也可能发生。

多环有什么意义?

分离内核和用户域有两个主要优点:

  • 更加容易地制作程序,因为您可以肯定一个程序不会干扰另一个程序。例如,一个用户区进程不必担心由于页面调度而覆盖另一程序的内存,也不必担心将硬件置于另一进程的无效状态。
  • 它更安全。例如,文件许可权和内存分离可能会阻止黑客应用读取您的银行数据。当然,这假定您信任内核。

怎么玩呢?

我创建了一个裸机设置,应该是直接操作环的好方法:https : //github.com/cirosantilli/x86-bare-metal-examples

不幸的是,我没有耐心举一个用户区示例,但是我确实进行了分页设置,因此用户区应该是可行的。我很乐意看到拉取请求。

另外,Linux内核模块在环0中运行,因此您可以使用它们来尝试特权操作,例如,读取控制寄存器:如何从程序访问控制寄存器cr0,cr2,cr3?获取细分错误

这是一个方便的QEMU + Buildroot设置,可以在不杀死主机的情况下进行尝试。

内核模块的缺点是其他kthreads正在运行,并且可能会干扰您的实验。但是从理论上讲,您可以使用内核模块来接管所有的中断处理程序,并拥有系统,这实际上是一个有趣的项目。

负环

尽管英特尔手册中并未实际提及负环,但实际上有些CPU模式具有比环0本身更多的功能,因此非常适合“负环”名称。

一个示例是虚拟化中使用的管理程序模式。

有关更多详细信息,请参见:

在ARM中,环被称为“异常级别”,但主要思想保持不变。

ARMv8中存在4个异常级别,通常用作:

  • EL0:用户区

  • EL1:内核(ARM术语中的“主管”)。

    svc指令(SuperVisor调用)一起输入,该指令以前称为swi 统一汇编之前,它是用于进行Linux系统调用的指令。您好世界ARMv8示例:

    你好

    .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
    

    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调用)。

    虚拟机管理程序是针对操作系统的,就像操作系统是针对用户的。

    例如,Xen允许您在同一系统上同时运行多个操作系统,例如Linux或Windows,并且为了安全性和调试的方便性,它们相互隔离,就像Linux对用户层程序所做的那样。

    虚拟机监控程序是当今云基础架构的关键部分:它们允许多个服务器在单个硬件上运行,从而使硬件使用率始终接近100%,并节省了大量资金。

    例如,直到2017年AWS 迁移到KVM时才使用Xen 。

  • EL3:再上一层。TODO示例。

    输入smc说明(安全模式调用)

ARMv8架构参考模型DDI 0487C.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 :当前的执行模式/异常级别等是什么?

ARM不需要提供所有异常级别,以实现不需要该功能以节省芯片面积的实现。ARMv8“异常级别”说:

一个实现可能不包括所有的异常级别。所有实现都必须包括EL0和EL1。EL2和EL3是可选的。

例如,QEMU默认为EL1,但是可以使用命令行选项启用EL2和EL3:qemu-system-aarch64在模拟a53通电时输入el1

代码片段已在Ubuntu 18.10上进行了测试。


11

内核空间和用户空间是特权操作系统功能和受限用户应用程序的分离。必须进行分隔,以防止用户应用程序洗劫您的计算机。如果任何旧的用户程序开始将随机数据写入硬盘或从另一个用户程序的存储空间读取内存,将是一件坏事。

用户空间程序无法直接访问系统资源,因此访问由操作系统内核代表该程序进行处理。用户空间程序通常通过系统调用向操作系统发出此类请求。

内核线程,进程,堆栈并不意味着同一件事。它们是内核空间的类似构造,就像它们在用户空间中的构造一样。


8

每个进程都有自己的4GB虚拟内存,该内存通过页表映射到物理内存。虚拟内存主要分为两部分:3 GB用于进程,1 GB用于内核。您创建的大多数变量都位于地址空间的第一部分。该部分称为用户空间。最后一部分是内核所在的位置,并且对于所有进程都是通用的。这称为内核空间,并且该空间的大多数都映射到物理内存的起始位置,在引导时将内核映像加载到这些位置。


1
您的答案是特定于Windows的;您应该澄清一下。
马修,

1
您说的是,对于4GB虚拟内存中的每个进程,1 GB的内核空间对于每个进程都是相同的,并且仅包含映射。.我不明白为什么!!,为什么仅映射到起始位置就需要1GB?
VISHAL DAGA

5

地址空间的最大大小取决于CPU上地址寄存器的长度。

在具有32位地址寄存器的系统上,地址空间的最大大小为2 32字节,即4 GiB。同样,在64位系统上,可以寻址2 64个字节。

这样的地址空间称为虚拟内存虚拟地址空间。它实际上与物理RAM大小无关。

在Linux平台上,虚拟地址空间分为内核空间和用户空间。

特定于体系结构的常量(称为任务大小限制,或TASK_SIZE)标记发生拆分的位置:

  • 从0到TASK_SIZE-1 的地址范围分配给用户空间;

  • 其余的TASK_SIZE2 32 -1(或2 64 -1)分配给内核空间。

例如,在特定的32位系统上,用户空间可能占用3 GiB,内核空间可能占用1 GiB。

类Unix操作系统中的每个应用程序都是一个进程。它们中的每一个都有一个唯一的标识符,称为进程标识符(或简称为进程ID,即PID)。Linux提供了两种创建进程的机制:1. fork()系统调用,或2. exec()调用。

内核线程是轻量级进程,也是正在执行的程序。单个进程可能包含共享相同数据和资源但通过程序代码采用不同路径的多个线程。Linux提供了一个clone()系统调用来生成线程。

内核线程的示例用法是:RAM的数据同步,帮助调度程序在CPU之间分配进程等。


4

简要地说:内核在内核空间中运行,内核空间具有对所有内存和资源的完全访问权限,可以说内存分为两部分,一部分用于内核,一部分用于用户自己的进程,(用户空间)运行普通程序,用户空间无法直接访问内核空间,因此它要求内核使用资源。通过syscall(glibc中的预定义系统调用)

有一条语句简化了不同的“ 用户空间只是内核的测试负载 ” ...

很清楚:处理器体系结构允许CPU在两种模式下运行:内核模式和用户模式,硬件指令允许从一种模式切换到另一种模式。

内存可以标记为用户空间或内核空间的一部分。

当CPU在用户模式下运行时,CPU只能访问用户空间中的内存,而CPU尝试访问内核空间中的内存时,结果是“硬件异常”,当CPU在内核模式下运行时,CPU可以直接访问到内核​​空间和用户空间...


2

内核空间意味着只能由内核触摸存储空间。在32位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)

您提出的最后一个问题很好。但是只是软件中断INT 0x80,然后呢?对我来说这不是一个很好的解释。您能否详细说明解决方案如何解决您自己提出的问题?
Victor Choy

2

苏尼尔·亚达夫,在Quora上:

Linux内核指的是在内核模式下运行的所有内容,它由几个不同的层组成。在最低层,内核通过HAL与硬件交互。在中间层,UNIX内核分为4个不同的区域。四个区域中的第一个区域处理字符设备,生和熟TTY以及终端处理。第二个区域处理网络设备驱动程序,路由协议和套接字。第三个区域处理磁盘设备驱动程序,页面和缓冲区高速缓存,文件系统,虚拟内存,文件命名和映射。第四个也是最后一个区域处理流程分派,调度,创建和终止以及信号处理。最重要的是,我们具有内核的顶层,其中包括系统调用,中断和陷阱。该级别用作每个较低级别功能的接口。程序员使用各种系统调用和中断来与操作系统功能进行交互。



1

试图给出一个非常简化的解释

虚拟内存分为内核空间和用户空间。内核空间是将运行内核进程的虚拟内存区域,用户空间是将运行用户进程的虚拟内存区域。

此划分是内存访问保护所必需的。

引导加载程序在将内核加载到RAM中的某个位置后(通常在基于ARM的控制器上)每次启动内核时,都需要确保控制器处于超级用户模式且FIQ和IRQ被禁用。


1

内核空间和用户空间是逻辑空间。

大多数现代处理器都设计为在不同的特权模式下运行。x86机器可以在4种不同的特权模式下运行。 在此处输入图片说明

并且当处于/高于特定特权模式时,可以执行特定机器指令。

由于这种设计,您可以为系统提供保护或对执行环境进行沙盒处理。

内核是一段代码,它管理您的硬件并提供系统抽象。因此,它需要访问所有机器指令。它是最受信任的软件。所以我应该以最高特权被处决。和环的级别0是最优越的模式。因此,环0级也称为内核模式

用户应用程序是任何第三方供应商提供的软件,您无法完全信任它们。有恶意意图的人如果可以完全访问所有机器指令,则可以编写代码以使系统崩溃。因此,应向应用程序提供对有限指令集的访问。而环3级是最特权模式。因此,所有应用程序都以该模式运行。因此,第3级环网也称为“ 用户模式”

注意:我没有获得Ring Level 1和Ring2。它们基本上是具有中级特权的模式。因此可能使用此特权执行设备驱动程序代码。AFAIK,Linux仅将Ring Level 0和Ring 3分别用于内核代码执行和用户应用程序。

因此,在内核模式下发生的任何操作都可以视为内核空间。在用户模式下发生的任何操作都可以视为用户空间。


0

正确的答案是:没有内核空间和用户空间。处理器指令集具有特殊的权限,可以设置破坏性的内容,例如页表映射的根或访问硬件设备的内存等。

内核代码具有最高级别的特权,而用户代码则具有最低的特权。这样可以防止用户代码使系统崩溃,修改其他程序等。

通常,内核代码与用户代码保存在不同的内存映射中(就像用户空间保存在彼此不同的内存映射中一样)。这是“内核空间”和“用户空间”术语的来源。但这不是硬性规定。例如,由于x86始终要求其中断/陷阱处理程序始终被映射,因此必须将部分内核(或某些OS)映射到用户空间。同样,这并不意味着此类代码具有用户特权。

为什么需要内核/用户划分?一些设计师不同意它实际上是必要的。微内核体系结构基于这样的思想,即特权最高的代码段应尽可能小,所有重要的操作都应在用户特权的代码中完成。您将需要研究为什么这可能是一个好主意,它不是一个简单的概念(并且因其优点和缺点而闻名)。


0

内存获取分为两个不同的区域:

  • 用户空间,这是正常用户进程运行的一组位置(即除内核之外的所有内容)。内核的作用是管理在此空间中运行的应用程序,避免彼此和机器混乱。
  • 内核空间是内核代码的存储位置,并在其下执行。

在用户空间下运行的进程只能访问内存的有限部分,而内核可以访问所有内存。在用户空间中运行的进程也无权访问内核空间。用户空间进程只能通过内核公开的接口访问内核的一小部分-系统调用。如果进程执行系统调用,则会向内核发送软件中断,然后分派适当的中断处理程序并继续处理程序完成后的工作。


-7

在Linux中,有两个空间,第一个是用户空间,另一个是内核空间。用户空间仅包含您要运行的用户应用程序。作为核心服务,存在进程管理,文件管理,信号处理,内存管理,线程管理,并且这里有许多服务。如果您从用户空间运行该应用程序,则该应用程序仅与内核服务交互。并且该服务与硬件和内核之间存在的设备驱动程序进行交互。内核空间和用户空间分离的主要好处是我们可以通过用户空间中存在的所有用户应用程序的virus.bcaz来获得安全性,而服务也存在于内核空间中。这就是为什么linux不会感染该病毒的原因。


5
除了它是“内核”而不是“内核”之外,您的答案也不是完全正确的。现代病毒(在Windows 98之后,现代病毒是指一切)完全不与“内核服务”交互,所有事情都在用户空间内完成。Linux没有太多病毒(Linux当然也有病毒)的事实是,它具有很好的权限管理,而且-最重要的事实-大多数Linux用户不是:“ omaigosh JustinBieber.NewSong.exe!我必须听到NAO !!! 1111“用户的声音,这些用户单击并安装所有内容而没有任何线索。
alexclooze 2012年

3
同样,Linux没有像Windows那样被广泛使用-编写病毒不会造成像病毒作者想要的那样严重的破坏。用户空间应用程序不与内核服务通信,它们调用内核提供的特殊功能,即syscalls。
alexclooze
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.