linux 内核任务调度,《linux内核完全剖析》笔记04-任务调度

问题:

任务调度在何时发生

任务调度的基本策略是什么

任务切换时怎么做到的

1. 隐含的睡眠队列

建立睡眠等待队列的原因,是因为有先后顺序等待某项资源,然后要按顺序唤醒进程,就要依照这里隐含的队列顺序进行

sched.c第171行

static inline void __sleep_on(struct task_struct **p, int state)

{

struct task_struct *tmp;

if (!p)

return;

if (current == &(init_task.task))

panic("task[0] trying to sleep");

tmp = *p;

*p = current;

current->state = state;

repeat: schedule();

//这里的*p的内容,有可能不是上面的*p的内容即不是当前任务,也有可能是

//不是当前任务是因为在其它进程执行本函数时__sleep_on设置的

if (*p && *p != current) {

(**p).state = 0;

current->state = TASK_UNINTERRUPTIBLE;

goto repeat;

}

if (!*p)

printk("Warning: *P = NULL\n\r");

if (*p = tmp)

tmp->state=0;

}

以下这张图展示了这个队列的大概样子,上面方块代表__sleep_on函数块,需要了解这个机制,是因为很多其它的子系统也用到类似的方法,这也是了解睡眠机制的关键

0818b9ca8b590ca3270a3433284dd417.png

2. 任务调度在何时发生

任务状态发生改变的时候都会要重新调度,比如睡眠了一个进程(任务),自然就要重新选一个进程继续运行,在者要是没有任务发生改变时,时间中断回调函数,会在每10ms到来时运行一次

sched.c第324行

void do_timer(long cpl)

{

...

//current->counter > 0意思是当前任务还有分配给他的时间片

if ((--current->counter)>0) return;

current->counter=0;

if (!cpl) return;

//重点:重新调度

schedule();

}

3. 任务调度的基本策略

sched.c第120行

void schedule(void)

{

...

/* 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);

}

备注:

选取超时时间最少也就是分配的时间片最多的那个进程进行切换

调度性能与进程的数目成线性关系,进程越多性能越差

4. 切换任务的代码分析

先来看看TSS段描述符的格式:

0818b9ca8b590ca3270a3433284dd417.png

然后再来看看_TSS宏,它是寻找GDT表中本进程的tss描述符的选择符号值,每个任务包含一个ldt选择符和tss选择符

//每个任务有一个8字节的tss选择符和8字节的ldt选择子一共16字节

//任务为n偏移2^4字节

#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))

切换代码分析

#define switch_to(n) {\

struct {long a,b;} __tmp; \

/*是不是当前任务,是就不用切换直接跳到下面标号为的地方*/

__asm__("cmpl %%ecx,_current\n\t" \

"je 1f\n\t" \

/* %dx装载下面的_TSS(n),也就是tss段描述符的值 放到%1,%1代表__tmp.b处 */

"movw %%dx,%1\n\t" \

/*%ecx为保存为切换出来的任务*/

"xchgl %%ecx,_current\n\t" \

/*长跳到__tmp.a处的任务,也就是上面tss保存到__tmp.b的进程*/

"ljmp %0\n\t" \

"cmpl %%ecx,_last_task_used_math\n\t" \

"jne 1f\n\t" \

"clts\n" \

"1:" \

::"m" (*&__tmp.a),"m" (*&__tmp.b), \

"d" (_TSS(n)),"c" ((long) task[n])); \

}

结束语:

0.12版本的进程调度策略比较简单,但给了我们理解进程调度的核心意思,最新的linux代码仍然使用switch_to宏,只是已经做了非常的优化工作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值