前言

既然引进了多进程,其实也就是在进程之间来回切换,那么就会有进程之间的调度问题。实则是在可运行进程之间分配有限的处理器时间资源的内核子系统。

几个简单的CPU调度算法

  • First Come, First Served(FCFS)

其实就是一个先进先出队列了,也就是说先申请的进程,先执行。当CPU空闲时,它会分配给位于队列头部的进程,并且这个运行进程从队列中移去。FCFS调度代码编写简单并且理解容易。

但是对于一个需要和用户进行交互的进程,这种调度算法就会造成体验非常不好,因为周转时间需要完成一整个队列的任务,非常的长

但FCFS调度算法是非抢占的。一旦 CPU 分配给了一个进程,该进程就会使用 CPU 直到释放 CPU 为止,即程序终止或是请求I/O。

  • Shortest Job First(SJF)

SJF调度算法就指对短作业或者短进程优先调度的算法,将每个进程与其估计运行时间进行关联选取估计计算时间最短的作业投入运行。这样就可以缩短周转时间

最短作业优先(SJF)调度算法将每个进程与其下次CPU执行的长度关联起来。当 CPU 变为空闲时,它会被赋给具有最短 CPU 执行的进程。如果两个进程具有同样长度的 CPU 执行那么可以由FCFS来处理。

  • RR

该算法中,将一个较小时间单元定义为时间量或时间片。时间片的大小通常为 10~100ms。就绪队列作为循环队列。CPU调度程序循环整个就绪队列,为每个进程分配不超过一个时间片的CPU。

为了实现RR调度,我们再次将就绪队列视为进程的 FIFO 队列。新进程添加到就绪队列的尾部。CPU 调度程序从就绪队列中选择第一个进程,将定时器设置在一个时间片后中断,最后分派这个进程。

调度算法的折中

在运行的许多进程里,有的进程更关心响应时间,有的进程更关心周转时间,所以调度算法就需要折中,但是如何折中又是一个问题。

Linux0.11 调度算法

schedule

schedule是Linux0.11里最主要的调度算法,但是十分简洁

  • task_struct是用来描述一个进程的结构体

    task_struct的counter是调度算法实现折中的一个关键,它既用来表示分配的时间片,又用来表示进程的优先级

  • 首先从任务数组中最后一个任务开始循环检测一些域

    如果设置过任务的定时值alarm,并且已经过期(alarm<jiffies),则在信号位图中置SIGALRM信号,即向任务发送SIGALARM信号。然后清alarm。还有一些信号量相关的会在后面再提

  • 找到数值最大的一个couter,也就是运行时间最少的一个进程,切换到它的进程

  • 当执行完一回的轮转就会重新分配一次时间片,这时候对于已经轮转过的进程,时间片将会被设置为初值,但是对于那些阻塞的进程,时间片将会增加,也就是进行了一次折中的调度。

void schedule(void)
{int i,next,c;struct task_struct ** p;/* check alarm, wake up any interruptible tasks that have got a signal */for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)if (*p) {if ((*p)->alarm && (*p)->alarm < jiffies) {(*p)->signal |= (1<<(SIGALRM-1));(*p)->alarm = 0;}if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&(*p)->state==TASK_INTERRUPTIBLE)(*p)->state=TASK_RUNNING;}/* this is the scheduler proper: */while (1) {c = -1;next = 0;i = NR_TASKS;p = &task[NR_TASKS];while (--i) {if (!*--p)continue;if ((*p)->state == TASK_RUNNING && (*p)->counter > c)c = (*p)->counter, next = i;}if (c) break;for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)if (*p)(*p)->counter = ((*p)->counter >> 1) +(*p)->priority;}switch_to(next);
}

init

我们在顺便来看一下sched_init,这个函数内核调度程序的初始化子程序,就是初始化一些中断和描述符

  • 首先调用set_tss_desc和set_ldt_desc设置进程0的tss和ldt

  • 清任务数组和描述符表项

  • 之后初始化8253定时器

  • 最后是设置时钟中断和系统调用中断

void sched_init(void)
{int i;struct desc_struct * p;if (sizeof(struct sigaction) != 16)panic("Struct sigaction MUST be 16 bytes");set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));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 */__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");ltr(0);lldt(0);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);
}

设置描述符

_set_tssldt_desc其实就是根据描述符各个位的作用来进行设置

#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x89")
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x82")#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \"movw %%ax,%2\n\t" \"rorl $16,%%eax\n\t" \"movb %%al,%3\n\t" \"movb $" type ",%4\n\t" \"movb $0x00,%5\n\t" \"movb %%ah,%6\n\t" \"rorl $16,%%eax" \::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \"m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \)

设置中断

#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \"movw %0,%%dx\n\t" \"movl %%eax,%1\n\t" \"movl %%edx,%2" \: \: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \"o" (*((char *) (gate_addr))), \"o" (*(4+(char *) (gate_addr))), \"d" ((char *) (addr)),"a" (0x00080000))#define set_intr_gate(n,addr) \_set_gate(&idt[n],14,0,addr)#define set_system_gate(n,addr) \_set_gate(&idt[n],15,3,addr)    

小结

这一篇主要看了一下Linux0.11里的调度算法,十分的简洁,但是又照顾了响应时间,又照顾了周转时间。

然后提了一下内核调度程序的初始化子程序,其实就是根据之前说的设置一些描述符和中断处理

转载于:https://www.cnblogs.com/secoding/p/11422525.html

我是如何学习写一个操作系统(六):进程的调度相关推荐

  1. 我是如何学习写一个操作系统(完结):总结和系列索引

    前言 从一开始的引导程序到现在的文件系统已经有十篇了,算是自己对操作系统的学习的一个笔记,原本是想结合自己之前写的玩具操作系统FragileOS,但是之后就转到了结合Linux 0.11的代码去写这个 ...

  2. 我是如何学习写一个操作系统(五):故事的高潮之进程和线程1

    前言 为什么取这个标题呢?一是进程和线程是作为操作系统里最重要最核心的一部分.二是确实吃冰棍拉冰棍,没话,强行凑标题和之前的标题差不多字数. 前一章写了系统调用的过程,算是一个小插曲,这个部分不管在哪 ...

  3. 我是如何学习写一个操作系统(七):进程的同步与信号量

    前言 在多进程的运行环境下,进程是并发执行的,不同进程间存在着不同的相互制约关系.为了协调进程之间的相互制约关系,达到资源共享和进程协作,避免进程之间的冲突,引入了进程同步的概念. 临界资源 多个进程 ...

  4. 我是如何学习写一个操作系统(二):操作系统的启动之Bootloader

    前言 今天本来的任务看书和把之前写的FragileOS整理一下,但是到现在还在摸鱼,书也只看一点.后来整理了一下写这个系列的思路,原本的目的是对操作系统原理性的学习和对之前写的一个玩具型操作系统的回顾 ...

  5. 我是如何学习写一个操作系统(九):文件系统

    前言 这个应该是这个系列的尾声了,一个完整的操作系统可能最主要的也就是分成这几大模块:进程管理.内存管理和文件系统.计算机以进程为基本单位进行资源的调度和分配:而与用户的交互,基本单位则是文件 生磁盘 ...

  6. 我是如何学习写一个操作系统(八):内存管理和段页机制

    前言 多进程和内存管理是紧密相连的两个模块,因为运行进程也就是从内存中取指执行,创建进程首先要将程序和数据装入内存.将用户原程序变成可在内存中执行的程序,而这就涉及到了内存管理. 内存的装入 绝对装入 ...

  7. 我是如何学习写一个操作系统(四):操作系统之系统调用

    前言 最近有点事情,马上要开学了,所以学习的脚步就慢下来了.这一篇主要是来说操作系统的系统调用的,像C语言的printf深入到内部就是一个有关屏幕输出的系统调用 什么是系统调用 之前提过操作系统是对硬 ...

  8. 我是如何学习写一个操作系统(一):开篇

    前言 有人说程序员的三大浪漫分别是编译原理.操作系统和计算机网络 在之前完成了一两个小软件和网站后遇见了编译原理,也不知道为什么就傻傻的学,还写了一个从零写编译器的系列学习笔记,不过都是小打小闹,只能 ...

  9. 我是如何学习写一个操作系统(三):操作系统的启动之保护模式

    前言 上一篇其实已经说完了boot的大致工作,但是Linux在最后进入操作系统之前还有一些操作,比如进入保护模式.在我自己的FragileOS里进入保护模式是在引导程序结束后完成的. 实模式到保护模式 ...

最新文章

  1. python—操作MySQL增加数据
  2. 研究一个新的功能的时候,如何获取该资源的文档
  3. Java开发中数据类型之间的转换
  4. html文本域 高度自适应,textarea高度自适应,textarea随着内容高度变化
  5. 免费开源低代码拖拽开发_资料来源:面向开源开发人员的免费代码搜索工具
  6. Python学习day07 - Python进阶(1) 内置方法
  7. 关于JavaScript系列的自学,该怎么学比较好?
  8. 25 条 SSH 命令和技巧
  9. HDU 2017 一系列统计数据
  10. css表格做日历,CSS 制作有弹性的日历表
  11. 微波雷达感应模块,人体存在感应雷达技术,广告屏智能感应显示
  12. 微信开放平台与微信公众平台的支付关系
  13. android系统密码设置功能,手机锁屏设置!安卓手机锁屏密码设置技巧?
  14. 小米盒子显示连不上服务器,小米盒子连不上WiFi?这七个技巧绝对管用
  15. win7安装.Net Framework 4,出现错误码(1603,0x80070643)
  16. 滴滴章文嵩:一个人的20年开源热情和国内互联网开源运动
  17. js 无害化_道德第一无害
  18. java pdf 判断横版、竖版
  19. 以太坊ETH源码分析(1):地址生成过程
  20. R语言使用glmnet包拟合lasso-cox回归模型(生存时间和结果标签)、lasso-cox模型进行特征筛选、plot函数可视化cv.glmnet模型获得的最佳lambda曲线位置及其1个标准差线

热门文章

  1. 在android studio中创建Hello-JNI工程
  2. 关于NSKeyedArchiver的编码格式
  3. debian安装搜狗
  4. mongodb 入门 启动mongodb 无法启动 问题 非正常关闭
  5. 极路由2 root及校园802.1x拨号经验帖(西南校园)
  6. ORACLE一致读与ORA-01555
  7. HP DL380G4服务器前面板指示灯的含义
  8. loc和iloc的用法汇总(完成)
  9. mvnrepository总是出现烦人的one more step验证
  10. flink的savepoints和checkpoints以及state Query(暂时无法全部完成)