0 前言
什么是操作系统内核?
操作系统内核是指大多数操作系统的核心部分。它由操作系统中用于管理存储器、文件、外设和系统资源的那些部分组成。操作系统内核通常运行进程,并提供进程间的通信。
内核具有主要下述功能:
进程管理、内存管理、文件系统、设备控制、网络。
什么是进程管理:
内核负责创建和销毁进程, 并处理它们与外部世界的联系(输入和输出). 不同进程间通讯(通过信号, 管道,
或者进程间通讯原语)对整个系统功能来说是基本的, 也由内核处理. 另外, 调度器, 控制进程如何共享 CPU, 是进程管理的一部分. 更通常地,
内核的进程管理活动实现了多个进程在一个单个或者几个 CPU 之上的抽象.
但对于本周实验,我们所模拟的内核极为“微型”,无论是进程控制块PCB,还是调度机制都极为简单,具体内容见源码与分析部分。
1 源码
由于本周涉及源码较多,仅粘贴首个进程初始化并执行,以及进程调度相关代码。
进程控制块:
进程初始化:
进程调度
2 实验
使用讲义中提供的源码
/ 替换实验楼中的mykernel源文件。
重新编译生成内核版本文件。
使用qemu模拟器加载新内核后执行的结果。
(呃,这个mykernel-1.1 貌似还加入了进程调度优先级,不是上课那一版。。看似是用错代码了。不过已经快到deadline了。。。就这样吧,不影响理解。)
3 分析
从入口函数 my_start_kernel 开始分析 (注:分析的也是mykernel-1.1中的文件)
my_start_kernel:
1) 先初始化进程0,填充进程0的控制块信息
共填写了 PID(进程id)、进程状态(runnable/unrunnable)、进程入口位置(task_entry)、进程现场寄存器信息(ip与sp)、进程控制块链表指针(指向下一个进程控制块)
2) 再初始化其他待运行进程
注意进程状态设置为unrunnable
3) 运行进程0
使用my_current_task变量来记录当前CPU上正在运行的进程。
asm volatile(
"movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */
"pushl %1\n\t" /* push ebp */
"pushl %0\n\t" /* push task[pid].thread.ip */
"ret\n\t" /* pop task[pid].thread.ip to eip */
"popl %%ebp\n\t"
:
: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);
而这段代码涉及直接操作堆栈,所以使用了嵌入式汇编。其作用就是将之前设置好的进程相关的寄存器(sp与ip)压入到堆栈中,然后赋值给对应寄存器,准备进入运行状态。
my_process:
这个函数是进程的实际执行函数,在
task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;中赋值给ip,并在进程启动时压入了eip得到了执行。
在此函数中,此函数实际意义就是判断my_need_sched全局变量的值,然后决定是否主动调用my_schedule来进行进程调度。
其中my_need_sched的值由时钟中断触发,在中断服务函数中进行修改。
my_schedule:(重点)
这里重点分析,进程调度这一部分。
if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
{//save current scene
/* switch to next process */
asm volatile(
"pushl %%ebp\n\t" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
"movl %2,%%esp\n\t" /* restore esp */
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
"1:\t" /* next process start here */
"popl %%ebp\n\t"
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
my_current_task = next;//switch to the next task
printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n",prev->pid,next->pid,next->pid);
}
前文已描述,进程控制块PCB以链式结构进行组织。
当下一个进程可运行时,需要进行进程切换工作,步骤为,
1) 当前进程的状态的保存
当前ebp压入内核堆栈来保存,esp保存回到PCB中(
prev->thread.sp),
2) 准备下一个进程的运行现场,从PCB中找到下一个进程的堆栈顶信息和执行的指令信息。
"movl %2,%%esp\n\t" /* restore esp */
"pushl %3\n\t" "ret\n\t" /* restore eip */
更新my_current_task变量,完成进程切换。
4 总结
通过上述分析我们理解到,
1 进程通过PCB来描述,PCB可以以链式结构进行组织。
2 进程切换时,需要保存当前进程状态到PCB或内核堆栈,然后从PCB中恢复下一个待执行进程的现场并准备进行执行。
作者:胡川
原创作品转载请注明出处
《Linux内核分析》MOOC课程
参考:
《内核功能的划分》http://blog.chinaunix.net/uid-25533439-id-225371.html
阅读(452) | 评论(0) | 转发(0) |