Linux 基础之内核态与用户态

Linux 基础之内核态与用户态,第1张

内核主要是和硬件打交道,管理最核心的内容,如硬盘、内存、CPU等。

内核只有一个功能,就是进程调度,其他功能需要外插设备甚至网络等。

微内核调度资源需要去其他地方找,所以会慢。但也适合于万物互联,但现在的 4G 速度达不到。

鸿蒙是微内核的设计。当 5G 实现,万物互联,微内核的时代就可能到来。

宏内核就是上图中所有功能集成在一块,一般手机、电脑都是宏内核。

这是在硬件上做的一种安全机制,让用户不能直接 *** 作内核。

CPU 分不同的指令级别:一个是内核可以 *** 作的级别;另一个是应用程序访问的级别。

CPU 的指令级别一般分 4 个级别 0~4(ring)。linux 用了两个级别 0 和 3,内核用的是 0,应用程序只能访问到 ring 3 级。对于系统的关键访问需要经过 kernel 的同意,由内核命令去执行。

JVM 在用户态。

*** 作系统的内存管理,主要分为三个方面。

第一,物理内存的管理,相当于会议室管理员管理会议室。

第二,虚拟地址的管理,也即在项目组的视角,会议室的虚拟地址应该如何组织。

第三,虚拟地址和物理地址如何映射,也即会议室管理员如果管理映射表。

那么虚拟地址和物理地址如何映射呢?

每一个进程都有一个列表vm_area_struct,指向虚拟地址空间的不同的内存块,这个变量的名字叫mmap。

其实内存映射不仅仅是物理内存和虚拟内存之间的映射,还包括将文件中的内容映射到虚拟内存空间。这个时候,访问内存空间就能够访问到文件里面的数据。而仅有物理内存和虚拟内存的映射,是一种特殊情况。

如果我们要申请小块内存,就用brk。brk函数之前已经解析过了,这里就不多说了。如果申请一大块内存,就要用mmap。对于堆的申请来讲,mmap是映射内存空间到物理内存。

另外,如果一个进程想映射一个文件到自己的虚拟内存空间,也要通过mmap系统调用。这个时候mmap是映射内存空间到物理内存再到文件。可见mmap这个系统调用是核心,我们现在来看mmap这个系统调用。

用户态的内存映射机制包含以下几个部分。

物理内存根据NUMA架构分节点。每个节点里面再分区域。每个区域里面再分页。

物理页面通过伙伴系统进行分配。分配的物理页面要变成虚拟地址让上层可以访问,kswapd可以根据物理页面的使用情况对页面进行换入换出。

对于内存的分配需求,可能来自内核态,也可能来自用户态。

对于内核态,kmalloc在分配大内存的时候,以及vmalloc分配不连续物理页的时候,直接使用伙伴系统,分配后转换为虚拟地址,访问的时候需要通过内核页表进行映射。

对于kmem_cache以及kmalloc分配小内存,则使用slub分配器,将伙伴系统分配出来的大块内存切成一小块一小块进行分配。

kmem_cache和kmalloc的部分不会被换出,因为用这两个函数分配的内存多用于保持内核关键的数据结构。内核态中vmalloc分配的部分会被换出,因而当访问的时候,发现不在,就会调用do_page_fault。

对于用户态的内存分配,或者直接调用mmap系统调用分配,或者调用malloc。调用malloc的时候,如果分配小的内存,就用sys_brk系统调用;如果分配大的内存,还是用sys_mmap系统调用。正常情况下,用户态的内存都是可以换出的,因而一旦发现内存中不存在,就会调用do_page_fault。

内核态相当于一个介于硬件与应用之间的层,内核有ring 0的权限,可以执行任何cpu指令,也可以引用任何内存地址,包括外围设备, 例如硬盘, 网卡,权限等级最高。

用户态则权利有限,例如在内存分配中,有一部分内存是仅为内核态使用的,用户态code则不允许访问那些内存地址,每个进程只允许访问自己申请到的内存。而且不允许访问外围设备。另外在执行cpu指令的时候也可以被高优先级抢占。

大多数时间各类程序都是执行在用户态下,毕竟内核就是基础而已。

很多博客都提到了一个状态转换的例子:c语言在malloc的时候需要涉及到从用户态到内核态的转换,malloc是个函数,所以算作系统调用,是用户态的主动申请转换。但是在实际实现中,未必一定需要进入内核态拿到新的内存,详细看这个博客: https://blog.csdn.net/zdy0_2004/article/details/47787631

3.为什么两者切换耗时

linux下每个进程的栈有两个,一个是用户态栈,一个是内核态栈。在需要从用户态栈切换到内核的时候,需要进行执行栈的转换,保存用户态的状态,包括寄存器状态,然后执行内核态 *** 作, *** 作完成后要恢复现场,切换到用户态,这个过程是耗时的。当然这里有很多细节,但是我不懂,只了解宏观上的原因。

很多人都说mutex加锁解锁很慢,为什么呢,因为他也是需要从用户态到内核态的切换,因此有一些无锁技巧例如CAS(Compare and swap),还有spin lock这种停等不作context切换的锁。

这个博客说的挺好,介绍了mutex咋切换的细节,反正我是写不出来: https://www.jianshu.com/p/5725db8f07dc

3.一些讨论

https://blog.codinghorror.com/understanding-user-and-kernel-mode/

这个博客在介绍user mode和kernel mode,但是评论里都在争论各类语言在处理exception的时候是否切换到了内核态,跑偏了

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/8308068.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-15
下一篇2023-04-15

发表评论

登录后才能评论

评论列表(0条)

    保存