【版权所有,转载请注明出处。出处: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函数。

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

转载于:https://www.cnblogs.com/joey-hua/p/5596746.html

Linux0.11内核--进程调度分析之1.初始化相关推荐

  1. LINUX0.11内核阅读笔记

    我是通过阅读赵炯老师编的厚厚的linux内核完全剖析看完LINUX0.11的代码,不得不发自内心的说Linus真的是个天才.虽然我觉得很多OS设计的思想他是从UNIX学来的,但是他自己很周全很漂亮很巧 ...

  2. 一站式linux0.11内核head.s代码段图表详解

    阅读本文章需要的基础: 计算机组成原理:针对8086,80386CPU架构的计算机硬件体系要有清楚的认知,我们都知道操作系统是用来管理硬件的,那我们就要对本版本的操作系统所依赖的硬件体系有系统的了解, ...

  3. Linux2.6 内核进程调度分析

    Linux2.6 内核进程调度分析    进程的调度时机与引起进程调度的原因和进程调度的方式有关.在 2.6 中,除核心应用     主动调用调度器之外, 核心还在应用不完全感知的情况下在以下三种时机 ...

  4. Linux-0.11内核学习-添加系统调用

    1.参考资料 赵炯博士的网站oldlinux Linux内核完全注释 Linux0.11 源码 2.概要 操作系统作为软件应用层和底层硬件之间的部分,向下提供服务,向上提供接口.系统调用便是操作系统向 ...

  5. Linux0.11内核源码解析-setup.s

    学习资料: Linux内核完全注释 操作系统真像还原 极客时间-Linux内核源码趣读 Linux0.11内核源码 ->setup程序将system模块从0x10000~0x8ffff整块向下移 ...

  6. Linux0.11内核源码解析-bootsect.s

    学习资料: Linux内核完全注释 操作系统真像还原 极客时间-Linux内核源码趣读 Linux0.11内核源码 ->上电 ->80x86架构CPU会自动进入实模式 ->从地址0x ...

  7. Ubuntu14.04下搭建Bochs仿真平台,同时用该平台安装Linux0.11内核

    因为Linux0.11内核需要在80X86硬件平台上运行,现在已经没有该硬件系统了,所以需要搭建Bochs这个仿真平台.Bochs是一个X86硬件平台的开源模拟器. 安装步骤参考的是如下一篇文章:ht ...

  8. 编译linux0.11内核

    编译linux0.11内核 一.实验环境 二.下载文件 三.配置Linux0.11所需环境 四.编译内核 五.运行linux0.11 六.说明 1.setup.sh脚本里进行了什么操作? 2.最后弹出 ...

  9. Linux0.11内核引导启动过程概述

    Linux0.11仅支持x86架构.它的内核引导启动程序在文件夹boot内,共有三个汇编代码文件.按照启动流程依次是: (1)bootsect.s.boot是启动引导的意思,sect即sector,是 ...

  10. Linux0.11内核--进程的调度schedule和switch_to解析

    前言 在我学习进程调度的时候,是通过这三篇文章把这个问题搞明白的.所以这里贴 在一起,方便查看.第一篇主要是将进程的调度,可认为是个总体讲述,里面共 涉及两个函数schedule()和switch_t ...

最新文章

  1. Java中几个常见的关键字
  2. i2s传输中左右声道数据格式
  3. openlayers基础(一)——Map
  4. 程序怎么启动vasp_构建可扩展的GPU加速应用程序(NVIDIA HPC)
  5. JSONUtils.toJSONString的一个坑
  6. 聊聊高并发(四)Java对象的表示模型和运行时内存表示
  7. Java顺序IO性能
  8. 百度云盘照片导入华为相册里_必须知道的相册管理工具
  9. SpringBoot开源项目(企业信息化基础平台)
  10. 几何常用算法与判断线段相交【转】
  11. 9.5---所有字符串的排列组合(CC150)
  12. 显示器U2518怎么连服务器,工作游戏两不误,戴尔 U2518D显示器太划算-双显示器设置...
  13. EndNote文献输出引用格式自定义修改与编辑界面解读
  14. yolo批量检测图片
  15. android 的User-Agen
  16. 多址接入技术 FDMA TDMA CDMA NOMA
  17. 厦门大学计算机科学学院,厦门大学张俊松
  18. Java练手项目实战——五子棋游戏实现思路及源码
  19. 决策树之C4.5实现(离散属性与连续,属性并存)
  20. 如何用自己电脑架设服务器!自助建站将不再是梦想

热门文章

  1. python闭包函数的必要条件_Python闭包函数
  2. 2019蓝桥:2019拆分为平方和问题
  3. SLAM--李群李代数左扰动模型
  4. c语言情书大赛图片,校园情书大赛,一等奖是“真”情书
  5. 智能体挑战赛 - “奥林匹克 跑步运动”
  6. 极客大学架构师训练营-架构师技术图谱-大作业二
  7. 写jsx_使用Vue 3.0做JSX(TSX)风格的组件开发
  8. python字节码解析_简单入门python字节码混淆
  9. 2021-09-08因子分解机
  10. header js修改form_IE9 文件上传表单如何设置自定义header