Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9489
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 50
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-08 18:47
文章分类
文章存档

2015年(5)

我的朋友
最近访客

分类: LINUX

2015-03-15 21:30:59

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


阅读(437) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~