0%

简记 – 对内核和进程的理解

本文旨在宏观地对Linux内核的作用和进程的本质进行理解,如有不足之处,欢迎指正。

0x01 概述

Linux内核本质上是宏内核,也就是说内核作为一个单一的二进制文件,以特权模式运行在计算机中。而组成我们熟知的操作系统,只有一个内核是不够的,还需要有文件系统,Linux的虚拟文件系统(根文件系统)将不同的文件系统组织起来,并为其他程序提供了运行的平台。

如果你想看到Linux内核的位置,切换到/boot/目录下,其中的vmlinuz文件就是内核的二进制文件,可能不只一个,在开机引导的过程中会选择指定的一个内核进行装载。

内核启动之后,负责直接与硬件交互(或者说借助驱动程序),为正常的应用程序提供操作硬件的接口,例如磁盘I/O,标准输入输出,TCP/IP协议等。

img

操作系统架构示意图

0x02 进程的由来

进程,或者说多任务处理,是现代操作系统的一个重要标志。那么进程是怎么来的?

先说结论:进程是内核创建的。

在Linux内核源码中,include/linux/sched.c中,Linus使用C语言结构体定义了进程的数据结构task_struct。这个结构体在计算机开机之后的某一过程中,被内核代码加载到物理内存中,成为第一个进程,在早期Linux中,内核通过一系列设置,将进程的物理地址,虚拟地址设置就绪,模拟进程进入内核态的过程,利用iret指令激活第一个进程的用户态。现代的Linux也是基本一致的思路,不过更为复杂,利用3个特殊进程(pid=0,1,2),完成我们常用进程的创建。这是现代CPU等硬件的进步和更新设计思想引入的结果。

除了pid=0的进程之外,其他的任何进程,都是通过fork或者kernel_thread产生的,而fork或者kernel_thread实际上都是由内核控制的。所以进程都是内核创建的。

0x03 进程的内核态

了解Linux二进制程序的朋友都知道进程的地址空间分布,高地址的一块是内核空间,低地址的一块是用户空间。那么每个进程的高地址部分都有一个内核地址空间,似乎每开启了一个进程,就多了一个内核在运行。然而这只不过是假象,或者说是Linus等人的trick。

其实操作系统只有一个内核在运行,并且在物理内存中只有一个内核(一般为物理内存的低地址部分)。在每一个进程中都有一个内核地址空间,是靠虚拟地址机制完成的,也就是说每一个进程,都有一个虚拟地址空间,然而这些地址在物理内存中的分布是靠另外一套算法进行组织的,主要是分页机制。让每一个进程的虚拟地址空间中都有内核,并不是必须的操作。操作系统完全可以设计成内核只存在于物理地址中,然而这样会损失效率,因为要频繁地查询页表确定内核在物理内存中的位置。

因此,进程进入了内核态,从单一进程的角度是进入了进程的特权状态。而实际上是进程将操作权限交还给内核,这种交还可能是主动的,如系统调用,也可能是被动的,如进程调度。