Linux内核空间和用户空间

use*_*930 7 kernel space

我对内核和用户空间的确切结构以及占用的内存部分感到困惑.我当前(可能是错误的)理解是这样的:

  1. 创建一个进程,并将此进程的虚拟内存分成用户空间和内核空间区域,其中用户空间区域包含进程和内核空间区域的数据,代码,堆栈,堆等.包含诸如进程的页表和内核代码之类的东西.我不确定内核代码是什么...驱动程序代码或类似的东西?

  2. 此外,系统调用表是否始终映射到进程内核空间中的同一区域?(说"进程的内核空间"是否正确?

  3. 如果我编写自己的驱动程序/模块并插入它,那么该驱动程序代码是否会自动复制到每个创建的新进程的内核空间中?如果不是......这究竟是如何工作的?

在此先感谢任何输入,有助于澄清我的问题的文献/链接也是可以的.

干杯,砖

小智 29

你已经掌握了大致正确的想法,但要做出这样的调整:整个机器只有一个"内核空间",所有进程都共享它.

当进程处于活动状态时,它可以在"用户模式"或"内核模式"下运行.

在用户模式中,CPU执行的指令位于存储器映射的用户空间侧.该程序正在运行自己的代码或来自用户空间库的代码.在用户模式下,进程的能力有限.CPU中有一个标志,告诉它不允许使用特权指令,内核内存虽然存在于进程的内存映射中,但是不可访问.(你不希望让任何程序只读取和写入内核的内存 - 所有安全性都将消失.)

当一个进程想要做一些事情而不是在自己的(用户空间)虚拟内存中移动数据时,例如打开一个文件,它必须进行系统调用.每个CPU架构都有自己独特的制作系统调用的古怪方法,但它们都归结为:执行魔术指令,CPU打开"特权模式"标志,并跳转到内核空间中的特殊地址,即"系统调用"入口点".

现在该进程正在内核模式下运行.正在执行的指令位于内核内存中,它们可以读写任何想要的内存.内核检查进程刚刚发出的请求并决定如何处理它.

在该open示例中,内核接收对应于参数的2或3个参数int open(const char *filename, int flags[, int mode]).第一个参数提供了内核空间何时需要访问用户空间的示例.你open("foo", O_RDONLY)这么说,字符串"foo"是用户空间中程序的一部分.syscall机制只传递一个指针,而不是一个字符串,因此内核必须从用户内存中读取字符串.

要查找所请求的文件,内核可以查询文件系统驱动程序(以确定文件的位置)和阻止设备驱动程序(从磁盘加载必要的块)或网络设备驱动程序和协议(从远程源加载文件) ).所有这些都是内核的一部分,即在内核空间中,无论它们是内置的还是作为模块加载的.

如果不能立即满足请求,则内核可以使进程进入休眠状态.这意味着该过程将从CPU中取出,直到从磁盘或网络收到响应.另一个过程可能有机会立即运行.稍后,当响应进入时,您的进程将再次开始运行(仍处于内核模式).现在它找到了文件,open系统调用可以完成(检查权限,创建文件描述符)并返回用户空间.

返回用户空间是一个简单的问题,即将CPU恢复到非特权模式,并将寄存器恢复到用户 - >内核转换之前的状态,指令指针指向magic syscall指令之后的指令.

除了系统调用之外,还有其他一些事情可以导致从用户模式转换到内核模式,包括:

  1. 页面错误 - 如果您的进程访问的虚拟内存地址没有分配给它的物理地址,则CPU进入内核模式并跳转到页面错误处理程序.然后,内核决定虚拟地址是否有效,它要么创建一个物理页面,要么在它停止的用户空间中恢复进程,要么发送一个SIGSEGV.
  2. 中断 - 某些硬件(网络,磁盘,串行端口等)通知CPU需要注意.CPU进入内核模式并跳转到处理程序,内核响应它,然后恢复在中断之前运行的用户空间进程.

使用系统调用来加载模块,系统调用要求内核将模块的代码和数据复制到内核空间并在内核模式下运行其初始化代码.

这很长,所以我停下来了.我希望专注于用户内核转换的演练提供了足够的示例来巩固这个想法.