在大二上学习《操作系统》这门课的时候,课程主要是以linux系统为案例,当时老师推荐我们去阅读minix3.1.8的源代码,minix3是
基于微内核架构的类UNIX计算机操作系统,精巧而且完全开源,对于操作系统的入门者来说较容易理解。以下为本人在课程中整理的源代码阅读笔记,如果也有想要解读minix3.1.8源代码的朋友,推荐一款读代码的软件
source insight,利用其树状架构可以很清楚地追踪函数及变量定义。

一、Minix3的启动:

l  硬件读入引导程序,引导程序装入boot,Boot在内存中装入引导映像(包含内核、pm、fs等)

l  内核初始化(/Kernel/main.c):

1、初始化进程表和特权进程表:

状态设成空闲、区别任务还是其他进程设置p_nr值、将priv[i]映射到ppriv_addr[i]等

2、初始化引导映像中的程序,将进程名、进程指针等都复制到proc表中,并设置它们的特权(是否允许陷阱、是否能够运行kernel
call等等);如果是内核任务或者根系统进程则可以立即被调度,其他进程则不能被调度;

3、初始化栈、建立内存映射、初始化寄存器值

4、初始化服务器的栈指针,将proc_ptr 指向当前进程,入队,设为可调度状态

5、系统任务system获得CPU,system初始化后进入阻塞状态

6、时钟任务获得CPU,它初始化后企图接受时钟中断程序发来的消息并阻塞

7、其他驱动程序和服务器进程获得CPU并初始化

8、CPU最后交给第一个普通用户进程INIT

l  Init执行/etc/rc脚本,启动其他不在引导映像中的驱动器和服务器(服务等),Rc脚本检查上次是否正常关机

l  Init根据/etc/ttytab文件为终端创建子进程,执行/usr/bin/getty等待用户登录,成功登陆后执行shell,等待用户命令

 

二、消息机制的实现:(/Kernel/proc.c)

l  构造notify(宏定义BuildNotifyMessage):

1、将消息的类型改变为notify

2、调用get_uptime函数读取时钟节拍让通知中包含时间戳

3、判断消息的发起者,如果是HARDWARE,则置位目标进程的中断位图,如果是SYSTEM,则置位目标进程的信号位图

l  消息传递(do_ipc)

1、跟踪进程

2、同步消息传递则调用do_sync_ipc

异步消息传递调用mini_senda

l  同步的消息传递(do_sync_ipc)

1、检查各种错误:只有系统调用号码小于32的才允许调用;只有是接收信息才能将目的进程设为任意;指定的源进程和目的进程是否有效;发起进程是否允许向目的进程发送消息;该进程是否有特权发出调用

2、根据消息的类型调用函数

SENDREC:设置标志位关中断,继续调用SEND和RECEIVE

SEND:调用mini_send,判断是否完成发送

RECEIVE:清空IPC的状态位,调用mini_receive

NOTIFY:调用mini_notify

SENDNB:调用mini_send,未准备好时不堵塞

其他:出错

l  具体实现函数:

1、Mini_send:

检查目的进程是否因为等待本条消息而堵塞(等待的对象是本发送进程或是ANY),复制消息,接收进程通过复位RECEIVING位取消阻塞,将接收进程入队使它可以运行;如果接收进程不是在等待本条消息,检查死锁,将发送进程阻塞并入队到接收进程的等待发送队列的末尾

2、Mini_receive:

检查等待队列中是否有进程在等待发送消息,如果有则寻找目的进程并接收消息,将已完成接收消息的目的进程出队;否则如果队列为空则检查位图判断是否有挂起的通知,如果发现挂起的是本进程在等待的通知,则标记为不再挂起,并复制接收通知,返回OK;如果没有找到,则检查MF_ASYNMSG位看是否有挂起的senda,如果有则尝试异步接收;没有找到适合的消息,则再次检查死锁,将本进程堵塞

3、Mini_notify:

与mini_send类似,如果通知的接收者堵塞且在等待接收信息,那么构造消息发送,并使接收者离开等待队列;如果接收者没有等待消息,则置位挂起信号的位图并返回

4、Mini_senda:

清空表,重设表头地址和表中参数的数目;检查要发送的数量,将入口不为空且内核还没有处理的位读入;检查各种出错可能;检查接收者是否在等待本条消息,若是则复制发送;若不是,则告知接收者。如果接收者没有立即接收消息,则记录表头地址和参数数目

 

三、 进程调度

l  头文件定义在(/servers/sched/schedproc.h)

结构体包括进程的端点,标志位,最高允许特权级,当前特权级和时间片长度

l  初始化和接收消息(/servers/sched/main.c)

1、本地启动并初始化

2、等待下一条消息,如果是通知的话判断消息发送者是不是时钟,是的话检查时间戳是否已经过期,不做答复;如果不是时钟,则答复给发送者“功能没有实现”

3、向sched服务器发送消息请求操作

l  具体调度(/servers/sched/schedule.c)

1、do_noquantum:

时间片用完后,如果本进程的优先级高于普通进程的最低优先级,则将进程降级,由内

核将该进程挂到队伍末尾

2、do_stop_scheduling:

接收来自PM和RS的所有消息,设置标志位表示schedule的插槽停止使用

3、do_start_scheduling:

接收来自PM和RS的所有消息,端口有效时根据消息设置该进程的schedproc的端点等内容,检查schedproc的类型,如果是刚生成的,则设置优先级为可允许的最高优先级,分配完整时间片,如果是继承父母的,则设置优先级和时间片长度都与父母进程的一致。设置标志位表明schedproc的插槽正在使用。接管调度的过程,利用内核调用回复消息填充进程当前的优先级和时间片。利用系统调用分配时间片。标记自己为新的调度。

4、do_nice:

保留请求进程的旧信息(包括旧的优先级、旧的最高优先级),将新的优先级设为最高优先级,由内核将其挂回队伍中,如果挂回过程中发生错误,则恢复旧的值

l  定时升级(balance_queues)

设置定时,每100ticks将时间片运转完的进程的优先级提高一级

 

四、时钟服务

l  启动定时器中断处理(bsp_timer_int_handler):

1、先获得现时的tick数(lost_ticks为clock_task未执行的时候外部记录的tick数,要将其加到当前时间中),更新时间

2、调用中断处理函数

3、若下次timeout时间已到,证明计时器过时,要通知clock task

l  获得节拍数(get_uptime):

返回自系统启动后经过的ticks数

l  新设计时器(set_timer):

1、将新的timer结构体插入clock_timers队列,注意这个队列是按照溢出时间由小到大排列的

2、用clock_timers->tmr_exp_time设定新的下次timeout的时间

l  删除计时器(reset_timer):

1、将不再需要的计时器从有效和过期的timer队列中删除,更新下次timeout的时间

2、若timer队列为空,则设定下次timeout时间为NEVER

l  更新负载(load_update):

1、将准备好的进程入队,计算平均负载

2、更新结构的时间

l  中断处理函数(ap_timer_int_handler):

1、若进程可以计费则更新它的系统时间和剩余节拍数

2、递减虚拟计时器,如果适用,递减当前进程的虚拟和真正计时器

3、如果计时器过期,则发出信号

4、更新平均负载

 

技术
©2019-2020 Toolsou All rights reserved,
PostgreSQL: 九. 索引(精华)2020年7月13日 微信小程序 页面间通信使用mt-range实现一个数字随着滑动杆变化的效果华为Mate 40 Pro+ 5G曝光:徕卡电影镜头、陶瓷机身keras数据生成器--数据增强(精华)2020年6月26日 C#类库 文件读写操作帮助类冲突声明(conflicting declaration)解决[AndroidO] [RK3399] -- GPIO驱动与控制方式(精华)2020年8月9日 C#基础知识点 反射vue 监听 Treeselect 选择项的改变