操作系统-Lab4
实验要求
源码理解
运行源码
make run
之后报错
和Lab3一样
进程调度
在kernel_main
函数中,为三个任务分配时间片和优先级如下
书中说是时钟中断每隔10ms发生一次,时钟中断处理程序如下
进程调度函数schedule
调度的逻辑就是:找到剩余时间片最多(也就是优先级最高)的那个进程
实现过程
添加一个系统调用:print_str(char* s)
实现完毕后出现bug:
原因是:该系统调用需要传递参数,但是由于源代码中的sys_call
不支持传递参数,所以报错
解决方法:修改sys_call
的汇编代码,将ebx
中内容压入栈中,然后再进行系统调用,这样只要将参数存放在ebx
中即可。(后续如果需要更多参数,就对应压入更多的寄存器中的值)
添加一个系统调用:sleep(int milli_seconds)
该系统调用实现的是在指定的milli_seconds
中不被分配时间片。
实现方式,手册中给了提示
1、修改PROCESS
结构体,增加一个wake_tick
字段,指示该进程醒来的时间片。
2、修改schedule
函数,在进程调度时需要增加对wake_tick
的判断,可以参与进程调度的应该是wake_tick<=current_tick
的进程。
模拟读者写者
注意点
一个读进程被选中开始读一个时间片之后,另一个读进程被调入开始读的这一个时间中,前一个读进程应该仍然保持读状态,直至读结束。
需要实现三种策略:读者优先、写者优先、读写公平(防止饿死)
允许同时读的进程数需要能够改变(
n=1,2,3
)每个进程读写结束之后休息的时间片可以随意修改(\(t \ge 0\))
(具体的PV操作以及三种策略对应的读写函数见代码)
1、三种策略
使用表驱动的实现方式,建立函数数组,依据不同的策略去调用数组中对应的读写函数即可。
1 |
|
2、同时读的进程数
使用一个信号量控制读者进程数量r_mutex
,其value
初值为READER_MAX
3、读写之后的休息时间
在PROCESS
结构体中增加一个sleep_time
字段,在完成读写后调用sleep
函数即可
4、PROCESS
结构体
1 |
|
5、schedule
函数
注意:要调度所有的睡醒的进程、调度所有的结束进程释放资源、调度所有的waiting进程。因为逻辑上他们就是要同步进行的。
1 |
|
注意:调度了刚睡醒的进程后,如果其可以成功执行,要在开始执行后停止再次执行调度函数,因为需要将结束的进程的资源返回,同时尝试开启其他进程。(
run_after_sleep
字段就是为了执行这个功能)
6、一个问题
reader_max=2 && sleep_time=2
会有一个问题:B进程结束开始睡觉,C进程睡醒,D进程在睡觉,此时rw_mutex
被写者进程抢走。
解决:先调度刚睡醒的进程,然后调度要结束的进程,保证刚睡醒的进程可以和其他进程一样在结束的进程返回资源之后一起竞争。
实验
中断返回
schedule调度策略采用顺序调度,如下结果可以发现,当再次调度到A的时候是先打印的是a.
,可见再次调度之后确实是从中断处继续执行的。
进程调度
同样的sleep函数,一个是普通函数实现,一个是系统调用实现,结果是只有系统调用才能正确实现进程调度。
因为系统调用返回后回到p_proc_ready
所指的进程中执行,而普通函数返回后仍然在原来函数中执行,即使p_proc_ready
已经改变。
奇怪的问题:
右侧只是做了一些输出,结果就和左边不一样!
原因:在进程中直接调用了schedule
函数改变了p_proc_ready
,但是由于schedule
函数在用户态执行完毕后返回原进程,就没有起到预想中的调度的作用。但是增加了一些输出就做到了,是因为输出方法是系统调用,会进入内核态,然后从内核态返回之后,就进入了p_proc_ready
所指定的进程中执行,起到了调度的作用!
在图中红框部分,0
和t
本应该是schedule
函数中相邻的两次输出,但是中间却夹着一部分字符,这一部分字符就是从第一个打印系统调用中返回后进入新的p_proc_ready
指定的进程中执行的输出,出现乱码也应该是因为之前不正当使用schedule
函数引起的。
解决:用一个系统调用封装schedule
函数供进程调用,修改后发现程序行为正常且一致。
sleep和milli_delay的对比
对A任务进程执行 milli_delay(100)
对A任务进程执行sleep(100)
两者的区别就在于:对A执行sleep(100)
后就不参与进程调度了,所以只有B和C进程参与调度;但是对A执行miili_delay(100)
,A只是在这100ms
中不执行任何操作而已,仍然参与进程的调度。