20169203《Linux内核原理与分析》第四周作业
通过本周对Linux的学习,我对Linux的进程管理有了更加深入的了解大体来讲进程有五种状态,在五状态进程模型中,进程状态被分成下列五种状态。进程在运行过程中主要是在就绪、运行和阻塞三种状态间进行转换。创建状态和退出状态描述进程创建的过程和进程退出的过程。
1)运行状态(Running):进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
2)就绪状态(Ready):进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排人低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
3)阻塞状态(Blocked):当进程由于等待I/O操作或进程同步等条件而暂停运行时,它处于阻塞状态。
4)创建状态(New):进程正在创建过程中,还不能运行。操作系统在创建状态要进行的工作包括分配和建立进程控制块表项、建立资源表格(如打开文件表)并分配资源、加载程序并建立地址空间表等。
5)退出状态(Exit):进程已结束运行,回收除进程控制块之外的其他资源,并让其他进程从进程控制块中收集有关信息(如记帐和将退出代码传递给父进程)。
对于所有的进程都有一个进程描述符对其进程所有的信息进行描述,但是需要说明的是进程的生命周期与进程描述符的生命周期是不一样的.
对于进程调用中的函数堆栈框架的构建其在程序开始主要是将原来的ebp保存即pushl %ebp,再设置调用函数的ebd即movl %esp %ebp,这样相对于调用程序来说自己的堆栈就构建完成,当调用函数结束时要把栈指针设置到调用初始位置即movl %ebp %esp,再讲存放的ebp弹出即popl %ebp.
对于本次实验楼的实验,这个实验主要涉及到两个c源程序,分别是mymain.c和myinterrupt.c。通过编写这两个源程序来实现一个简单的时间片轮转多道程序内核代码。下边,分别给出这两个源程序的代码:
mymain.c
void _init my_start_kernel(void)
{
int i=0 ;
while(1)
{
i++ ;
if(i%100000 == 0)
printk(KERN_NOTICE "my_start_kernel here %d \n", i) ;
}
}
myinterrupt.c
void my_timer_handler(void)
{
printk(KERN_NOTICE "\n>>>>>>>>>>>>>>>>>>>");
}
其编译执行的结果如下:
在内核初始阶段my_start_kernel函数中先初始化一个进程0,第32行为定义进程0的入口为my_process,因为一开始系统里只有进程0,所以第34行代码表示的是pid的next还是指向自己。接下来就创建更多其他的进程,在初始化这些进程的时候可以直接把0号进程的代码拷贝过来,第41行表示每个进程都有自己的堆栈,然后把创建好的新进程放到进程列表的尾部,这样就完成了创建。
创建好了之后就可以开始执行0号进程,第48行处到之后的几行代码表示的是一段嵌入式汇编代码。其中的%0表示的是参数thread.ip,%1表示的是参数thread.sp。第49行表示的就是把参数thread.sp放到esp中;接下来push %1,又因为当前栈为空,esp=ebp,所以等价于push ebp;然后push thread.ip;ret等价于pop thread.ip;最后pop ebp。
ret之后0号进程就正式启动了。
函数my_process定义所有进程的工作。这个函数里面定义了一个循环,if语句表示循环1000万次才有机会判断是否需要调度。这是一个主动调度的机制。
下面看一下myinterrupt.c的代码:
对于if(time_count%1000==0)其中time_count%1000是把time_count对1000取余 time_count从0开始加到1000余数才为零进入if语句
对于函数my_timer_handler,用于设置时间片的大小,时间片用完时设置一下调度标志。当时钟中断发生1000次,并且my_need_sched!=1时,把my_need_sched赋为1。因为当进程发现my_need_sched=1时,就会执行my_schedule。
对于my_scheduel的工作,先把当前进程的下一个进程赋给next,当前进程为prev,如果下一个进程的状态是正在执行的话,就运用if语句中的代码表示的方法来切换进程,这些代码为嵌入式汇编代码,与mymain.c代码中的相似。
%0表示prev->thread.sp,%1表示prev->thread.ip,%2表示next->thread.sp,%3表示next->thread.ip。push ebp为保存当前进程的ebp;然后保存当前进程的esp;把下一个进程的sp放到esp中;接下来保存eip,$1表示后面的标号1:的位置;然后把下一个进程的eip push到栈里面。
ret之后下一个进程就开始执行了。这是进程切换的关键代码。
对于时间片轮转的多道程序来说其中最关键的是在程序轮转的调度,程序的轮转要用的中断程序,在中断程序中,对于中断服务程序的进入,中断现场的保存与恢复,都是十分重要的其中对于函数栈的操作是其中的关键,我有一个问题就是会不会有一种极端情况计算机产生中断之后在执行中断服务程序时又发生中断,然后若进入这个中断在执行这个中断服务时又发生中断以此类堆那么一个中断程序中就会嵌套非常多的中断,这显然是不合理的那么计算机是怎么避免这种情况的呢?