【版权所有,转载请注明出处。出处:http://www.cnblogs.com/joey-hua/p/5596746.html

首先看main.c里的初始化函数main函数里面有个函数是对进程调度的初始化,sched_init()函数,次函数在sched.c中实现:

// 调度程序的初始化子程序。
void sched_init (void)
{
int i;
struct desc_struct *p; // 描述符表结构指针。 if (sizeof (struct sigaction) != 16) // sigaction 是存放有关信号状态的结构。
panic ("Struct sigaction MUST be 16 bytes");
// 设置初始任务(任务0)的任务状态段描述符和局部数据表描述符(include/asm/system.h,65)。
set_tss_desc (gdt + FIRST_TSS_ENTRY, &(init_task.task.tss));
set_ldt_desc (gdt + FIRST_LDT_ENTRY, &(init_task.task.ldt));
// 清任务数组和描述符表项(注意i=1 开始,所以初始任务的描述符还在)。
p = gdt + 2 + FIRST_TSS_ENTRY;
for (i = 1; i < NR_TASKS; i++)
{
task[i] = NULL;
p->a = p->b = 0;
p++;
p->a = p->b = 0;
p++;
}
/* Clear NT, so that we won't have troubles with that later on */
/* 清除标志寄存器中的位NT,这样以后就不会有麻烦 */
// NT 标志用于控制程序的递归调用(Nested Task)。当NT 置位时,那么当前中断任务执行
// iret 指令时就会引起任务切换。NT 指出TSS 中的back_link 字段是否有效。
__asm__ ("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); // 复位NT 标志。
ltr (0); // 将任务0 的TSS 加载到任务寄存器tr。
lldt (0); // 将局部描述符表加载到局部描述符表寄存器。
// 注意!!是将GDT 中相应LDT 描述符的选择符加载到ldtr。只明确加载这一次,以后新任务
// LDT 的加载,是CPU 根据TSS 中的LDT 项自动加载。
// 下面代码用于初始化8253 定时器。
outb_p (0x36, 0x43); /* binary, mode 3, LSB/MSB, ch 0 */
outb_p (LATCH & 0xff, 0x40); /* LSB */// 定时值低字节。
outb (LATCH >> 8, 0x40); /* MSB */// 定时值高字节。
// 设置时钟中断处理程序句柄(设置时钟中断门)。
set_intr_gate (0x20, &timer_interrupt);
// 修改中断控制器屏蔽码,允许时钟中断。
outb (inb_p (0x21) & ~0x01, 0x21);
// 设置系统调用中断门。
set_system_gate (0x80, &system_call);
}

首先初始化任务0的TTS,FIRST_TSS_ENTRY为4,表示在描述符表的索引是4。因为gdt是desc_struct类型为8个字节,刚好是一个描述符的长度,所以这里的gdt+4可以理解为gdt[4]。刚好对应的是TSS0。

描述符表的内容如下:

0-没有用nul,1-代码段cs,2-数据段ds,3-系统段syscall,4-任务状态段TSS0,5-局部表LTD0,6-任务状态段TSS1,等。

//// 在全局表中设置任务状态段/局部表描述符。
// 参数:n - 在全局表中描述符项n 所对应的地址;addr - 状态段/局部表所在内存的基地址。
// type - 描述符中的标志类型字节。
// %0 - eax(地址addr);%1 - (描述符项n 的地址);%2 - (描述符项n 的地址偏移2 处);
// %3 - (描述符项n 的地址偏移4 处);%4 - (描述符项n 的地址偏移5 处);
// %5 - (描述符项n 的地址偏移6 处);%6 - (描述符项n 的地址偏移7 处);
#define _set_tssldt_desc(n,addr,type) \
__asm__ ( "movw $104,%1\n\t" \ // 将TSS 长度放入描述符长度域(第0-1 字节)。
"movw %%ax,%2\n\t" \ // 将基地址的低字放入描述符第2-3 字节。
"rorl $16,%%eax\n\t" \ // 将基地址高字移入ax 中。
"movb %%al,%3\n\t" \ // 将基地址高字中低字节移入描述符第4 字节。
"movb $" type ",%4\n\t" \ // 将标志类型字节移入描述符的第5 字节。
"movb $0x00,%5\n\t" \ // 描述符的第6 字节置0。
"movb %%ah,%6\n\t" \ // 将基地址高字中高字节移入描述符第7 字节。
"rorl $16,%%eax" \ // eax 清零。
::"a" (addr), "m" (*(n)), "m" (*(n + 2)), "m" (*(n + 4)),
"m" (*(n + 5)), "m" (*(n + 6)), "m" (*(n + 7)))
//// 在全局表中设置任务状态段描述符。
// n - 是该描述符的指针(向量);addr - 是描述符中的基地址值。任务状态段描述符的类型是0x89。
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr, "0x89")
//// 在全局表中设置局部表描述符。
// n - 是该描述符的指针(向量);addr - 是描述符中的基地址值。局部表描述符的类型是0x82。
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr, "0x82")

因为TSS最小尺寸是104字节,所以第一句是把长度104赋值给TTS0描述符的第0-1字节,描述符的格式如 描述符格式 ;第二句是把ax也就是addr也就是任务联合的第一个任务的tss地址赋值给*(n+2)处,因为是movw字,所以也就是描述符的第2-3字节处。接下来填充第4字节,然后把类型type填充到第5字节,最后把剩余的字节填充。

初始化任务0的ldt的方法也是类似,好了,这里初始化完成任务0的TSS和LDT。

sched_init接下来是清空除了任务0的所有任务的数组和对应的描述符,这个好理解。

下面是加载任务0的TSS到任务寄存器tr,加载ldt到局部描述符表寄存器ldtr,sched.h:

/*
* 寻找第1 个TSS 在全局表中的入口。0-没有用nul,1-代码段cs,2-数据段ds,3-系统段syscall
* 4-任务状态段TSS0,5-局部表LTD0,6-任务状态段TSS1,等。见head.s
*/
// 全局表中第1 个任务状态段(TSS)描述符的选择符索引号。
#define FIRST_TSS_ENTRY 4
// 全局表中第1 个局部描述符表(LDT)描述符的选择符索引号。
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
// 宏定义,计算在全局表中第n 个任务的TSS 描述符的索引号(选择符)。
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
// 宏定义,计算在全局表中第n 个任务的LDT 描述符的索引号。
#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
// 宏定义,加载第n 个任务的任务寄存器tr。
#define ltr(n) __asm__( "ltr %%ax":: "a" (_TSS(n)))
// 宏定义,加载第n 个任务的局部描述符表寄存器ldtr。
#define lldt(n) __asm__( "lldt %%ax":: "a" (_LDT(n)))

LDTR局部描述符寄存器:16位,高13为存放LDT在GDT中的索引值。

所以FIRST_LDT_ENTRY要左移3位,(((unsigned long) n)<<4)不太好理解,因为先要去掉左移的3位,所以实际值是n<<1,也就是2n。最终的值相当于FIRST_LDT_ENTRY+2n。这样就好理解了,因为每个任务都有两个描述符项。

这里要注意:只明确加载这一次,以后新任务LDT 的加载,是CPU 根据TSS 中的LDT 项自动加载。

接下来是初始化定时器,没什么好说的。

接下来两句最关键了,进程调度的引发的诱因就是在下面初始化的:

  // 设置时钟中断处理程序句柄(设置时钟中断门)。
set_intr_gate (0x20, &timer_interrupt);
// 修改中断控制器屏蔽码,允许时钟中断。
outb (inb_p (0x21) & ~0x01, 0x21);

第一句在系统调用机制分析中有讲到,是设置中断门的,所以这里就是把system_call.s中的函数timer_interrupt和中断号0x20关联起来,下面一句代码参考 时钟中断 开启了时钟中断也就是0x20号中断,也就是说时钟每滴答(10ms)一下就会调用timer_interrupt函数。

到这里,进程调度的初始化就结束了。

Linux0.11内核--进程调度分析之1.初始化的更多相关文章

  1. Linux0.11内核--进程调度分析之2.调度

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5596830.html ] 上一篇说到进程调度归根结底是调用timer_interrupt函数, ...

  2. Linux-0.11内核源代码分析系列:内存管理get_free_page()函数分析

    Linux-0.11内存管理模块是源码中比較难以理解的部分,如今把笔者个人的理解发表 先发Linux-0.11内核内存管理get_free_page()函数分析 有时间再写其它函数或者文件的:) /* ...

  3. Linux0.11内核--内存管理之1.初始化

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5597705.html ] Linux内核因为使用了内存分页机制,所以相对来说好理解些.因为内存 ...

  4. Linux0.11内核--引导程序分析

    1.简介 本文主要介绍三个文件bootsect.s.setup.s.head.s,主要是做了些从软盘加载内核和设置32位保护模式的操作. 2.程序分析 当PC电源打开后,BIOS自检后将bootsec ...

  5. 第3阶段——内核启动分析之start_kernel初始化函数(5)

    内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只是浅尝辄止的描述一下函数的功能,很多函数 ...

  6. linux0.11内核源码剖析:第一篇 内存管理、memory.c【转】

    转自:http://www.cnblogs.com/v-July-v/archive/2011/01/06/1983695.html linux0.11内核源码剖析第一篇:memory.c July  ...

  7. linux0.11内核源码——进程各状态切换的跟踪

    准备工作 1.进程的状态有五种:新建(N),就绪或等待(J),睡眠或阻塞(W),运行(R),退出(E),其实还有个僵尸进程,这里先忽略 2.编写一个样本程序process.c,里面实现了一个函数 /* ...

  8. Linux0.11内核剖析--内核体系结构

    一个完整可用的操作系统主要由 4 部分组成:硬件.操作系统内核.操作系统服务和用户应用程序,如下图所示: 用户应用程序是指那些字处理程序. Internet 浏览器程序或用户自行编制的各种应用程序: ...

  9. Linux0.11内核源码——内核态线程(进程)切换的实现

    以fork()函数为例,分析内核态进程切换的实现 首先在用户态的某个进程中执行了fork()函数 fork引发中断,切入内核,内核栈绑定用户栈 首先分析五段论中的第一段: 中断入口:先把相关寄存器压栈 ...

随机推荐

  1. ASP.NET Core的配置(2):配置模型详解

    在上面一章我们以实例演示的方式介绍了几种读取配置的几种方式,其中涉及到三个重要的对象,它们分别是承载结构化配置信息的Configuration,提供原始配置源数据的ConfigurationProvi ...

  2. Vim 快速上手

    1.vi的基本概念 基本上vi可以分为三种状态,分别是 命令模式(command mode) 插入模式(Insert mode) 底行模式(last line mode) 1) 命令行模式comman ...

  3. webapp应用---cordova.js 3.7.0插件安装总结

    今天是2014年的最后一天,年终总结什么的就不写了.记录一下今天的工作内容.如果不知道phoneGap,那么就不需要往下看了,phoneGap现在已经叫cordova了,叫什么不重要,重要的是它对we ...

  4. CSS3实现图形曲线阴形和翘边阴影

    首先,来看看完成之后的效果图: 实现原理 ①曲线阴影实现: 多个阴影重叠,就是正常阴影+曲线阴影 正常情况下,有个矩形有正常的阴影,作为主投影,这时候再定义一个有一定弧度圆角的圆角矩形,然后放在正常矩 ...

  5. 主机巡检脚本:OSWatcher.sh

    主机巡检脚本:OSWatcher.sh 2016-09-26更新,目前该脚本只支持Linux操作系统,后续有需求可以继续完善. 注意: 经测试,普通用户执行脚本可以顺利执行前9项检查: 第10项,普通 ...

  6. 1Z0-053 争议题目解析705

    1Z0-053 争议题目解析705 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 705.View Exhibit1 to examine the DATA disk group ...

  7. WebGIS中GeoHash编码的研究和扩展

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 1.1普通地理编码流程 将采集的POI入库后,数据库里保存有 ...

  8. 使用GIT@OSChina 实现协同工作的方法。

    由于我新建了一个团队,团队里的人对于GIT都不太熟悉,所以才有了这篇文章.我用的是git-1.9.4的版本,所以我建议团队里面的成员也使用这个版本.首先是下载git,这个自己去网上找吧,一大堆,记得是 ...

  9. 【知识积累】SBT+Scala+MySQL的Demo

    一.背景 由于项目需要,需要在Sbt+Scala项目中连接MySQL数据库.由于之前使用Maven+Java进行依赖管理偏多,在Sbt+Scala方面也在不断进行摸索,特此记录,作为小模块知识的积累. ...

  10. JSP自定义tag

    前端需要调用后端的配置,想起velocity-tools.然而jsp的话,目前只能想到tag和EL表达式了. Tag相当好写,jsp2.0提供了简化写法: 编写一个java类: public clas ...