没有合适的资源?快使用搜索试试~ 我知道了~
经典Linux任务调度学习资料《鼠眼看Linux调度器.doc》。值得下载!
资源推荐
资源详情
资源评论









一、耗子vs Linux ?
“鼠目寸光”,应该是个暴光率挺高的成语了,常用来说某人看事情没有深度,看不透本质。毫无疑
问,这是一个贬义 100%的词。但不管是认识什么未知事物,都一定会有个“寸光”的过程,如果有进而持
续不断地努力,才可能做到对之了如直掌。
Linux 内核是个复杂的软件,作为一个成熟的操作系统核心部件,最可贵的就是它的开放源码。我
想,有着“忧国忧民”抱负的程序员恐怕每天都会有“我要读懂它”的冲动。正是在这股“贼心”的驱使下,我开
始了对 Linux 内核的学习。一路走来,我还远不能说是已经 AtoZ 了 Linux 内核,甚至不敢确定自己是不
是已摆脱了“啮齿动物”的行列。但在领略其中的独特风景后,踏踏实实地体会到真是有些欲罢不能了。例
如,内核中表面上看似稀松平常的五六行代码,有时却隐藏了许多秘密,反复思考之后,有时依然不得要
领,在请教 LKML 上的牛人之后,才恍然大悟。每逢此时都禁不住感叹,“同是程序员,咋差距凑这么大
哩?”。这种探索的乐趣是研究学习一般软件所不能获得的,我想这也是 Linux 内核的魅力所在吧。
我猜想,有不少朋友都曾经尝试过阅读 Linux 内核源代码,但没有坚持下来。所幸自己“贼胆”不小,
终于还算成功地迈出了第一小步。现在有了些小小的斩获,不甘独享,拿出来“显摆”一下,真诚地期望有
更多的“小鼠”朋友能够加入学习 Linux 内核的行列。
即便是解释了这么半天,以《鼠眼看 XX》作为文章题目是依旧是需要些勇气的,澄清一下,我的本
意其实是,文章中不会有太深的技术内容,难度水平是以一般 Linux 应用程序开发者能够看懂为限,当然,
我会尽所能避免“寸光”,尽量使“知其所以然/阅读难度”的比值大些。
最后,本文讨论 Linux 2.6.13 内核的调度功能,包括调度器的工作机制、时间片、优先级的计算等
内容,也简要地讨论了一点 staircase 调度。闲话结束,开“看”!
二、“看上去很美"的调度器。
讨论调度,最直接就是从它们功能入口谈起了。不同的调度器暴露其功能的方法不尽相同:
1、系统调用。这是最重要的方法。甚至于已经成为了一部分 POSIX 标准,但其数量并不太多,即
使如此,这里也不可能一一展开讨论,我们关心是最常用的两个:nice()和 sched_setscheduler(),尤
其是前者。
2、sysctl 参数。某些调度器的配置项可以用 sysctl 命令调整。比如可以提高交互性的 staircase
调度补丁,它的几个版本(比如 CK8 中的)就带有 compute 等三个 sysctl 参数可以配置调度的行为。
3、编译时的 C 常量。这是配置调度器功能的最底层手段。
显然,(3)适合系统程序员、(2)更多是管理员的任务。只有(1)方法才是我们应用程序开发
者最常用的调度功能接口。
先来简单回顾一下 Linux 上这两个系统调用的功能吧(下面可不是 POSIX 标准的原文哦):
int nice(int inc);
调整当前进程的运行优先级。inc 越小,当前进程的运行优先级会变的越高,反之优先级越低。
inc 可以是任意整数(尤其是可以小于 0)。
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *p);
配置进程号为 pid 的进程的调度参数,包括策略 policy,和参数 p,目前 p 只是运行优先级。
policy 可以是软实时调度(SCHED_RR)、先来先服务调度(SCHED_FIFO)和正常的时间片轮转调度
和优先级调度混合的调度策略(SCHED_OTHER)。

本文只关心默认的最常用的策略 SCHED_OTHER,SCHED_RR 和 SCHED_FIFO 也不是想像的那
么复杂,事实上,它们的实现比 SCHED_OTHER 简单的多,只是作为 SCHED_OTHER 的特殊情况对待
罢了,体现在代码上,就是在正常处理逻辑上多了几个条件语句。下面的介绍中我们会绕过它们。有兴趣
的读者可以过后直接研习代码,难度不大。
需要指出的是,上面介绍的有些地方是不太准确的,特别是术语“进程”和“运行优先级”比较含糊,下
面我们就逐个揭开它们的面纱。
三、先瞧两眼 Linux 上的进程与线程。
我想大家对“进程"和"线程"这两个概念的含义已经烂熟于心了,小生就不班门弄斧了。不知是哪本
牛书的结论了,好像是《现代操作系统》,有个很精辟的结论,回忆如下:进程主要作为资源分配的单元,
而线程更大程度上是作为任务调度单元使用的。
众所周知,UNIX 进程模型是基于进程复制的,这个复制过程集中体现于 fork()系统调用。系统启动
后,首先在用户空间建立一个 PID 为 1 叫作 init 的进程。然后,整个系统中的所有用户空间上的进程都是
由这个 init 进程直接或间接复制出来的,只要系统在正常运转,这个 init 进程就可以说是“永不落进程”。
Linux 也采用了 UNIX 进程模型,甚至更彻底:Linux 上创建线程也是通过复制方法的得到的。新的
NTPL 线程库采用的是 1 对 1 的线程模型,即 1 个用户空间线程对应 1 个内核线程。虽然表层的 API 仍然
是依照 POSIX 标准的,但已经与前任线程实现有了很大不同。
说到底,创建进程最终会用到 fork()系统调用,而创建线程则最终使用 clone()系统调用。让我们看
一眼这两个系统调用的直接实现,不要慌张,它们很简单:
asmlinkageintsys_fork(structpt_regs regs)
{
returndo_fork(SIGCHLD,regs.esp,®s,0,NULL,NULL);
}
asmlinkageintsys_clone(structpt_regs regs)
{
unsignedlongclone_@ags;
unsignedlongnewsp;
int__user*parent_tidptr,*child_tidptr;
/* 我们在这里省略几行代码*/
returndo_fork(clone_@ags,newsp,®s,0,parent_tidptr,child_tidptr);
}
很明显,fork()和 clone()系统调用使用的是同一个函数 do_fork(),区别只有参数不同,至此,我
们可以大胆的推测,Linux 在对待进程和线程的问题上,是将两者看作基本相同的概念。事实上,在内核
里它们都使用 task_struct 结构体描述,传统的进程号保存在该结构的 tgid 成员(线程组 ID)中,而线
程号则保存在 pid 成员中。所以,在下文中我们统称“进程”和“线程”两者为“任务”。在后面介绍 nice()和

sched_setscheduler()两个系统调用的时,所用的术语“进程”或“线程”,如果不加特殊声明,都可以换成
“任务”。
有 Linux 开发经验读者可能知道,Linux 上还有一个系统调用可以创建新任务,这就是 vfork()。你可
能对它的实现方式有兴趣,下面是就是它的直接实现:
asmlinkageintsys_vfork(structpt_regs regs)
{
returndo_fork(CLONE_VFORK|CLONE_VM|SIGCHLD,regs.esp,®s,0,NULL,N
ULL);
}
嗯,看来 vfork()依旧没有摆脱 do_fork()的如来掌心。
探讨对 Linux 上进程和线程的实现是十分有意思的挑战,但对它们作深入讨论已经超出本文的范围,如果
有时间,下次我们再用“鼠眼“仔细瞧瞧 Linux 任务。
三、nice()系统调用。
限于这篇文章的写作目标,这里不可能完整解释与调度有关的每一行代码,但绝不放过每一个有价
值的地方。从现在开始,我会以简化代码的方式引用 Linux 内核的代码,简化的标准是只保留与主功能直
接相关的部分,例如内核中的各种同步细节、次要的错误检查,甚至安全方面的代码,我都会省略掉,但
是我只删代码,绝不会修改原有代码,这样既有利于抓住核心环节,缩减篇幅,又不妨碍有兴趣的读者顺
着这些线索亲自“咀嚼”代码。
下面就是与 nice()系统调用有关的简化代码:
asmlinkagelongsys_nice(intincrement)
{
intretval;
longnice;
1>if(increment<-40)
increment=-40;
if(increment>40)
increment=40;
2>nice=PRIO_TO_NICE(current->static_prio)+increment;
3>if(nice<-20)
nice=-20;
if(nice>19)
nice=19;
剩余14页未读,继续阅读
资源评论


gqb666
- 粉丝: 438
- 资源: 122
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助


安全验证
文档复制为VIP权益,开通VIP直接复制
