概述
- UNIX 的内核是一个部分模块的单块程序
- MINIX 是微内核结构,是一组进程的集合,内核功能较少,进程之间以及用户进程之间使用进程级通信机制(IPC)进行通信
内部结构
- 注意:内核层的 system task 是系统任务,第二第三层统称为系统进程 system process
- 除了第一层为内核层,其他层都是user mode。
- 进程都潜在有一定特权,这是第二层、第三层、第四层内的进程的不同之处。
- kernel call 与system call的区别
- 内核调用(kernel call)是由系统服务提供的以使驱动程序和服务器完成工作的低层函数。例如读硬件的I/O端口。
- system call 是用POSIX规范定义的高层调用,如read、fork和unlink,这些调用供第四层的用户程序使用。
- 用户程序包含很多POSIX调用,但不包含内核调用。
内核层(Kernel)
多数用 C 写,汇编负责内核直接与硬件交互的最底层部分。
主要功能是为上层驱动程序和服务提供一组特权内核调用。
内核
- 进程调度,负责进程在就绪态、运行态、阻塞态之间转换
- 处理进程间的消息
- 支持对 I/O 端口和中断的访问,在处理器中需要使用特权内核模式(kernel mode),这些指令在用户指令中是没有的。
时钟任务
- 类似设备驱动程序,与产生时钟信号的硬件交互
- 只提供内核接口,用户不能访问
系统任务
- 为驱动程序和服务器提供内核调用,用户进程不可调用
- 包括读写 I/O 端口、跨地址空间复制数据
设备驱动程序(Device Drives)
- 请求系统任务代表它们读写 I/O 端口
- 请求系统任务将读取的数据副本到另一个地址空间
- 实现操作系统工作一:管理资源
服务器进程(server processes)
进程管理器(Process Manage)
- 执行所有涉及或者终止进程的MINIX3系统调用,例如fork,exec和wait。
- 还负责执行与信号有关的系统调用,例如alarm和kill。
- 管理内存,如发出brk系统调用。
文件系统(File System)
- 执行文件系统的调用,如read,mount和chdir。
- 实现操作系统工作二:通过实现系统调用提供一个扩展的计算机(PM & FS)
信息服务器(information server0,IS)
- 负责提供其他驱动程序和服务器的调试和状态信息的工作。
再生服务器(reincarnation server,RS)
- 启动或者重启那些不与内核一起加载到内存的设备驱动程序。
网络服务器(network server)
- 可以通过驱动程序来请求I/O。
用户进程(user processes)
- 如shell程序、编辑器、编译器和用户程序。
- 系统进程比用户进程拥有更高的执行优先级
- Init进程
- 在系统引导时启动
- 是第一个用户进程,也是引导映像(boot image)的最后一个进程
- Daemon 守护进程
- 周期性运行或总是等待某个事件的后台进程
总结
设备驱动层和服务器层的进程统称为系统进程,是操作系统的组成部分。
Init是第一个用户进程,PID=1
进程管理器是用户空间内运行的第一个进程,PID=0
MINIX3 中的进程管理
MINIX3 的启动
软盘
只有第一个分区可以引导(引导块,boot block),由它装入引导程序(bootstrap),引导程序很小,并装入一个更大的程序boot,由boot装入操作系统。
boot block -> bootstrap -> boot -> boot image -> Kernel -> 系统任务、时钟任务 -> init 进程 -> 其他用户进程
硬盘
可能包含很多分区,第一个扇区包含主引导记录。
主引导记录(master boot record)= 第1个扇区内的一小段程序(用于选择活动分区)+ 磁盘分区表(partition table)
程序部分被执行以读入分区表并选择活动分区,活动分区的第一个扇区有一个引导程序,被装入并执行以查找启动程序boot。
master boot record -> partition table -> active parttition -> boot block (扇区1) -> bootstrap-> boot -> boot image
在 BIOS 中设置启动盘的搜索顺序
- 软盘启动
- 硬盘启动
- 程序部分被执行读入分区表并选择活动分区,活动分区第一个扇区中的引导程序被装入执行并以查找并启动程序boot
boot
- 是MINIX3的次级装入程序,不仅可以装入操作系统,而且作为一个监控程序,允许用户改变、设置和保存不同的参数。
引导映像(boot image)
- MINIX3 的 boot 程序将在软盘或硬盘分区上找一个包含多个部分(各种程序,如PM、FS 等)的文件,并将各部分装到内存适当的位置。
- 引导映像的各个部分是独立的程序,内核、FS、PM、再生服务器必须是引导映像的一部分。
- 在初始化阶段,内核先启动系统任务和时钟任务,再启动进程管理器和文件系统,接着是其他一些服务器和驱动程序,这些都完成之后被阻塞,然后第1个用户进程 init 才开始运行。
进程树的初始化
Init 是第1个用户进程,也是作为引导映像(boot image)的一部分加载的最后一个进程
- init进程首先执行
/etc/rc
脚本,结合再生服务器启动其他驱动程序和服务器,启动守护进程。 - 其中一个使用程序为服务(service),是再生服务器的用户接口
- 最后启动守护进程
- init不是进程树的树根,其结构也不能判断系统进程的启动顺序
进程管理器
是用户空间运行的第1个进程,PID=0再生服务器
是其他所有再引导映像中启动的进程的父进程
- init进程首先执行
MINIX3 中的进程间通信
- 原语
- Send
- Receive
- Sendrec
- 发送一条消息,并等待同一个进程的应答
实现:内核中的消息传递机制将消息从发送者复制到接收者
- 同步通讯:当一个进程发送消息到目标进程而目标进程并不在等待消息时,发送进程将阻塞,直到目标进程调用 receive 为止。
- 当 A 向 B 发送消息时,如果 B 向 A 发送消息,会导致死锁。
每个任务、驱动程序或服务器只允许与一些特定的进程通信
Notify
- 不阻塞消息发送
- 只发送一个很简单的通知,使用置位就可以存储。
- 异步通讯,可以避免死锁
- 消息通知(notification message)的简单性在于,不能被接受的通知可以很容易的存储起来,以便当接受者调用receive时可以收到这个通知。
- 实现
- 每个系统进程都有这样一个位图,其中的每一位对应于一个系统进程,用于记录其他系统进程发给他的通知。
- 通过位图(bitmap)来接收,notify 只包含发送者信息和一个内核加上的时间戳
- 当系统进程调用receive时,通过检查自己的位图,可以知道其他系统进程是否发通知给它
MINIX3 中的进程调度
- 结构:多级排队系统
- 共 16 个队列
- 最低优先级位 IDLE 进程,只在没有其他任务时运行
- 系统和时钟任务处于最高优先级队列中
- 驱动程序和服务器进程通常具有一个较大的时间片,通常是运行结束被阻塞而非时间片到了被中断
- 只有当前一个队列中没有就绪的程序时,才会运行低优先级的进程队列
- 不同优先级的进程时间片长度不一
- 驱动程序进程和服务器进程通常需要运行至其阻塞,因此它们分配较大的时间片
- 优先级调度 + 改进的轮转调度算法
- 进程转为非就绪时,它的时间片没有用完,则表明发生I/O阻塞,则该进程将再次转为就绪,将其放在对手,分配的时间片为上次时间片中的剩余时间。
- 一个用完时间片的进程,以正常的时间片轮转调度方式放在队尾
- 优先级降低
- 如果一个用完了时间片的进程,仍然是上一次运行的进程,则可以认为此进程在无限循环中,阻止了其他低优先级进程的运行。
- 此时,将把该进程放到一个较低优先级队列的队尾以降低它的优先级。
- 优先级提高
- 如果一个进程用完了时间片,但没有妨碍其他进程运行,则它的优先级将提高,知道该进程所允许的最大优先级为止。
- 进程的优先级可根据实际情况调整
MINIX3的系统任务
由于MINIX中内核独立于主系统部件,因此外部进程被禁止执行实际的I/O操作,也不能改动系统表以及完成其他操作系统一般都有的功能。
解决这个问题的方法是让内核为驱动程序和服务器提供一组服务。这些对普通用户进程可用的服务,允许驱动程序和服务器执行实际的I/O操作、存取内核表以及他们所需要的功能,而不要在内核内部完成。
这些服务是由系统任务(system task)进行处理的。其工作原理为:系统任务接受来自驱动程序和服务器的对于这些服务的调用请求,因为系统任务是处于内核空间的一部分,所以可以调度并使用他们。
与系统调用的区别
在传统的单体内核操作系统中,系统调用(system call)指的是对于所有内核提供服务的调用,由POSIX标准描述对进程可用的系统调用。
而在MINIX3中,操作系统的组件是在用户空间中运行的,虽然有着作为系统进程的特权。
将用户进程发出的系统调用将被转换为发往服务器进程的消息。服务器进程之间、服务器进程与设备驱动程序之间以及服务器进程与内核之间可以通过消息进行通信
系统任务将接受对于所有内核服务的请求,称为内核调用。其关系如下:
用户进程 -> 系统调用 -> 服务进程 -> 内核调用 -> 系统任务
例如,fork
系统调用要求改变进程表的内核部分,为此进程管理器将发送sys_fork
调用给系统任务,而它可以操作内核空间中的数据。
系统调用接收的消息类型
一共接收28条消息,每一种消息对应于一个内核调用
- 进程管理类
- sys_fork,sys_exec
- sys_nice改变进程的属性
- sys_privctl改变进程特权,被RS使用
- 信号类
- sys_kill
- sys_getksig,sys_endksig,sys_sigsend用于协助处理信号
- 内存类
- sys_newmap 用于更新进程表的内核部分
- sys_segctl,sys_memset 用于访问进程数据空间之外的内存
- sys_umap 用于虚拟地址转换物理地址
- sys_vircopy,sys_physcopy 用于使用虚拟地址或物理地址进行内存复制
- 时间类
- sys_times 对应于time
- 系统控制
- sys_abort 在正常关机请求或系统崩溃后,进程管理器产生
- sys_getinfo相应对内核信息的不同请求
调用
系统调用
为POSIX标准描述的对进程可用的系统调用。在MINIX3中,用户进程不再直接向内核请求服务。
内核调用
系统任务接受所有对于内核服务的请求。
类系统调用
用于进程间通信的
send,receive,notify
等消息原语。
系统任务的实现
由
kernel/
主目录下的一个头文件system.h
和一个C源文件system.c
编译而成。kernel/system.h
提供了绝大多数内核调用的函数原型
system.c
系统任务的主题逻辑
实现步骤
以
read
系统调用为例,读取一个数据库最坏需要11条消息,最少需要4条。最坏情况
消息3请求系统任务执行I/O操作
消息4确认信号
当发生系统中断时,系统任务通过消息5告诉等待中的驱动程序这个事件
消息6和消息7分别是请求复制数据到文件系统的消息以及回复
消息8告诉文件系统数据已经准备好了(完成复制到缓冲区)
消息9和消息10是将数据由缓冲区复制到用户的请求消息和回复(跨进程复制)
消息11是给用户的回复
最好情况
在缓冲区中已经存在数据
消息2和消息3分别是将数据复制给用户的请求和回复
例子
以exec为例,进程管理器完成exec
系统调用的大部分工作
为设置进程表内核数据结构中的栈指针,由do_exec
来处理
调用exec的进程,发送消息给进程管理器后阻塞
- 不同于其他系统调用(相应结果将解除阻塞),exec没有响应
do_exec
为进程解除阻塞,并使其成为就绪
参考资料
- Operating System:Design and Implementation,Third Edition