第一关 版本1.2内核的进程调度过程分析
(一)答案
1.mygdb
侧调试命令:
b task1
c
c
b schedule
c
disas 0x6973
p sys_call_table[0x1b]
c
disas 0x69ae
p sys_call_table[0x1d]
c
c
c
c
c
2.第三关.txt
:
(二)解题思路
1.概述:0号进程和1号进程间进程调度过程
(1)从system_call
函数进入相应的系统调用函数
在两个进程运行alarm
和pause
函数时,到执行alarm
和pause
系统调用时,都会进入到system_call函数,在下图第一处划线位置根据系统调用号,调用相应的sys_alarm
和sys_pause
函数。
(2)执行sys_alarm
和sys_pause
系统调用函数
两个函数的共同点在于都是系统调用的函数,因此在执行这两个函数之前会进入上面说的system_call
函数,在执行完上一一张图提到的划线部分的call sys_call_table
指令后,也均会执行reschedule
(除了第一次的system_call(alarm)
,但这次例外出现在题目中要求的 “第二次输出1
” 之前,因此本文不做考虑(水平有限,没想明白)),reschedule
会调用一次schedule
函数。
而不同点在于,sys_alarm
函数本身内部并没有调用schedule
函数,而sys_pause
函数内部调用了一次schedule
函数,因此在main
函数调用alarm
系统调用时,alarm
系统调用共执行了一次schedule
,而main
函数的1
号进程和0
号进程调用pause
系统调用时,pause
系统调用共执行了两次schedule
。
sys_alarm
:
sys_pause
reschedule
main
(3)进程调度与函数调度的关系
在执行schedule
函数时,会发生进程的调度,在第一关中,每一次的进程调度都会导致0
号进程与1
号进程间的切换,由于进程在切换时,老进程会保留当前退出内存时的状态,,比如从0
号进程执行到main
函数for循环
中,task0
函数下一行的pause
函数,这时pause
系统调用会进入system_call
函数,通过下图划线位置的call sys_call_table
和pause
的系统调用号调用sys_pause
函数,执行到sys_call
函数的schedule
时,先保存这时的0号进程的执行状态,再切换到1
号进程,下次从1
号进程再切换到0
号进程时,保存的0
号进程的状态会使得0
号进程从call schedule
的下一条指令(大概是这个意思,可能不准确)开始执行,随后,一次退出sys_pause
、sys_call_table
,回到system_call
函数,直至执行到call reschedule
中的schedule
函数,再保存0
号进程的状态,重复上述操作。
2.从第二次输出1的开始到第三次输出1之前,进程调度与函数调用关系示意图
第二关:两个进程的 严格交替输出 (从第一次循环开始两个进程严格交替输出)
(一)答案
在1.2/linux/kernel/sched.c
文件中的schedule
函数添加如下图圈内内容。
虽然最后结果里的前几个0/1
没有严格交替输出,但是评测通过。
(二)思路
1.分析原schedule
函数导致0
、1
不严格交替输出的原因
在上一关中的思路中提到:
alarm
系统调用共执行了一次schedule
,而main
函数的1
号进程和0
号进程调用pause
系统调用时,pause
系统调用共执行了两次schedule
。
所以一次for循环
中,打印一个0
,0
号进程一共要调用两次schedule
函数(pause
的两次),而1
号进程要调用三次schedule
函数(pause
的两次和alarm
的一次),正是这样不为1:1
的schedule
的调用次数比(即进程调度、切换的次数,因为原schedule
函数使得只要该函数被调用,就会进行进程的切换),导致了从输出第二个1
开始的10100
这样的输出循环中,0
,1
并非严格交替输出,且输出的0
:输出的1
= 3
:2
(这是因为1
号进程调用了三次0
号进程。而0
号进程调用了两次1
号进程)。
2.设计新schedule
函数
那么只要使得1
号进程中与0
号进程中通过调度切换进程的次数为1:1
即可解决该问题,而思路就是使得1
号进程中的alarm
函数在调用schedule
函数的时候不进行进程的调度(切换)。
再看原schedule
函数,其基本逻辑为:如果当前进程为0
号进程,就让下一个进程为1
号进程,如果当前进程为1
号进程,就让下一个进程为0
号进程。
需要将其逻辑改为:如果当前进程为0
号进程,就让下一个进程为1
号进程,如果当前进程为1
号进程,且当前schedule
函数,不是alarm
函数调用的system_call
中的reschedule
调用schedule
函数,就让下一个进程为0号进程,否则下一个进程依然为1
号进程。
如何判断上述思路中当前schedule
函数是否为alarm
函数调用的system_call
中的reschedule
调用的schedule
函数:
在schedule
函数中的task[next]->counter = 15;
语句之前添加printk
调试信息如下:
printk("Current pid = %d", current->pid);
printk("Current alarm = %d", current->alarm);
(为什么打印current->alarm
:因为在调用alarm
函数中的sys_alarm
中修改了alarm
的值,判断current->alarm
为区别alarm
和pause
的特征参数。)
发现0
号进程的current->alarm
的值总为0
、1
号进程的current->alarm
的值一直为正值且在增加,于是构造答案所示的schedule
函数。