我对内核和用户空间的确切结构以及占用的内存部分感到困惑。 我当前(可能是错误的)理解是:
创建一个进程,并将此进程的虚拟内存分成用户空间和内核空间区域,其中用户空间区域包含进程的数据,代码,堆栈,堆等并且内核空间区域包含诸如进程的页表和内核代码之类的东西。我不确定内核代码是什么...驱动程序代码或类似的东西?
此外,系统调用表是否始终映射到进程内核空间中的同一区域? (说“过程的内核空间”是否正确?
如果我编写自己的驱动程序/模块并插入它,那么该驱动程序代码是否会自动复制到每个创建的新进程的内核空间中?如果不是......这究竟是如何工作的?
提前感谢任何输入,有助于澄清我的问题的文献/链接也可以。
干杯, 砖
答案 0 :(得分:29)
你的大致想法大部分是正确的,但要做出这样的调整:只有一个"内核空间"对于整个机器,所有进程都共享它。
当进程处于活动状态时,它可以在"用户模式"或"内核模式"。
在用户模式下,CPU执行的指令位于存储器映射的用户空间一侧。该程序正在运行自己的代码或来自用户空间库的代码。在用户模式下,进程的能力有限。 CPU中有一个标志,告诉它不允许使用特权指令,内核存储器虽然存在于进程的存储器映射中,但是不可访问。 (你不会想让任何程序只读取和写入内核的内存 - 所有的安全性都会消失。)
当一个进程想要在其自己的(用户空间)虚拟内存中移动数据以外的其他操作时,例如打开一个文件,它必须进行系统调用。每个CPU架构都有自己独特的制作系统调用的古怪方法,但它们都归结为:执行魔术指令,CPU打开特权模式"标志,并跳转到内核空间中的特殊地址,"系统调用入口点"。
现在该进程正在内核模式下运行。正在执行的指令位于内核内存中,它们可以读写任何想要的内存。内核检查进程刚刚发出的请求,并决定如何处理它。
在open
示例中,内核接收与int open(const char *filename, int flags[, int mode])
的参数对应的2或3个参数。第一个参数提供了内核空间何时需要访问用户空间的示例。您说open("foo", O_RDONLY)
所以字符串"foo"
是用户空间中程序的一部分。 syscall机制只传递一个指针,而不是一个字符串,所以内核必须从用户内存中读取字符串。
要查找所请求的文件,内核可以查询文件系统驱动程序(以确定文件的位置)和阻止设备驱动程序(从磁盘加载必要的块)或网络设备驱动程序和协议(从中加载文件)远程来源)。所有这些都是内核的一部分,即在内核空间中,无论它们是内置的还是作为模块加载的。
如果不能立即满足请求,则内核可以使进程进入休眠状态。这意味着该过程将从CPU中取出,直到从磁盘或网络收到响应。另一个过程可能有机会立即运行。稍后,当响应进入时,您的进程将再次开始运行(仍处于内核模式)。现在它已找到该文件,open
系统调用可以完成(检查权限,创建文件描述符)并返回用户空间。
返回用户空间是一个简单的问题,即将CPU恢复到非特权模式并将寄存器恢复到用户 - >内核转换之前的状态,指令指针指向魔术系统调用指令之后的指令
除了系统调用之外,还有其他一些事情可以导致从用户模式转换到内核模式,包括:
使用系统调用来加载模块,该系统调用要求内核将模块的代码和数据复制到内核空间并在内核模式下运行其初始化代码。
这很长,所以我停下来。我希望专注于用户内核转换的演练已经提供了足够的例子来巩固这个想法。
答案 1 :(得分:2)
进程的虚拟内存映射中没有内核空间区域。虚拟内存映射具有:text,bss,data,heap,已加载程序的堆栈和共享库。在Linux上,您可以检查任何用户空间进程的/ proc / $ PID / maps。
当用户空间进程通过系统调用访问某些内核域代码时,内核代码将代表其堆栈中的进程执行。显然,从系统调用返回后,所有内核/驱动程序代码都将不在堆栈中。为了澄清更多,如果在某一时刻,任何进程都没有使用内核代码的某些部分,它将不会成为任何进程的虚拟内存映射的一部分。
如果您使用的是Linux,我会推荐Robert Love的“Linux内核开发”一书。