`
xitong
  • 浏览: 6196641 次
文章分类
社区版块
存档分类
最新评论

关于邮件列表一个问题的解释

 
阅读更多

问题:在 exit_mm() 中为什么要 atomic_inc(&mm->mm_count) 呢? 并没有对应的dec。

解答:

首先要明白,对于一个可以被释放内存的进程也就是说一个拥有mm_struct的进程来说,它的task_struct中的mm字段和active_mm字段是一样的,这个在fork时,copy_mm中就决定了,接下来解释这个问题:

在exit_mm中:

atomic_inc(&mm->mm_count); //这里递增了引用计数,你说对了,没有dec但是inc了,多了一次inc,那么就要多一次dec

BUG_ON(mm != tsk->active_mm); //保证mm和tsk->active_mm一样

task_lock(tsk);

tsk->mm = NULL; //注意这里将tsk->mm设置为NULL

up_read(&mm->mmap_sem);

enter_lazy_tlb(mm, current);

clear_freeze_flag(tsk);

task_unlock(tsk); //第一次dec

然后注意在do_exit调用exit_mm之后,最终会调用schedule将这个退出进程切出去,因此最终释放task_struct将在新进程切到运行之后再进行,那么看context_switch中:

oldmm = prev->active_mm; //这里的prev就是那个退出进程,其active_mm就是上面的mm,由此可见上面的atomic_inc就是为了这里

...

if (unlikely(!prev->mm)) { //这里if为真,因为上面tsk->mm = NULL;

prev->active_mm = NULL;

rq->prev_mm = oldmm; //rq->prev_mm就是还差一个计数就释放的prev->mm,要不早就释放了,幸亏你说的那个atomic_inc;

}

最后安全切换了新进程以后,一切都可以释放了,看finish_task_switch:

static void finish_task_switch(struct rq *rq, struct task_struct *prev)

{

struct mm_struct *mm = rq->prev_mm; //这里的mm就是上面那个退出的mm

long prev_state;

rq->prev_mm = NULL;

prev_state = prev->state;

...

if (mm)

mmdrop(mm); //引用计数彻底为0,最后被释放

...

其实mm也好,task_struct也好,进程切换时可能会用到,因此都在安全切到新进程之后也就是finish_task_switch中被释放,为了不让mm在exit_mm就被释放,那么只有增加它的引用计数了,不知道这里情景分析你懂了没有。

上述的解答如果大致理解的话那么就够了,但是如果真的较起真来的话,还真的有点让人发蒙,如果说linux在退出进程的时候不释放task_struct是因为schedule中要用到这个task_struct的话,那么在退出时保留其mm_struct是为什么呢?其实为题还有一大堆,比如为何linux在schedule时还要用到退出进程的task_struct呢?为何要这样呢?因为linux为了效率而没有另立一个调度器,进程切换必须由进程自己进行,也就是说切换前在调度器在切出进程的上下文运行,而切换后在换入进程的上下文运行。那么到底为何不能释放其mm_struct呢?因为当前退出进程所用的正是当前的页目录,也就是当前的页目录绝对不能释放,注意这里的页目录的768项之前的并无所谓,因为现在在内核,只会访问768项以后,这768项以后的每一项和swapper_pg_dir的768项以后一一对应,其实就是直接指向,即便如此,也不能释放pgd,毕竟mmu访问从pgd指向,因为在exit_mm中,该mm_struct的users引用计数已经成为1了,然后最后的mmput中的dec就会使其成为0,那么就要调用mmdrop了,一旦mm的引用计数递减后为0,在后者中会调用__mmdrop释放掉pgd,为了防止这一件事,但是还必须使得users顺利成为0,那么只要防止__mmdrop就可以了,于是就递增了mm的引用计数。

另外一个作用就要仔细研究一下context_switch函数:

static inline void context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next)

{

struct mm_struct *mm, *oldmm;

prepare_task_switch(rq, prev, next);

trace_sched_switch(rq, prev, next);

mm = next->mm;

oldmm = prev->active_mm;

arch_enter_lazy_cpu_mode();

if (unlikely(!mm)) { //如果下一个进程的mm为空就说明下一个进程是内核线程

next->active_mm = oldmm;

atomic_inc(&oldmm->mm_count); //又递增了一个引用计数

enter_lazy_tlb(oldmm, next); //进入懒惰模式

} else

switch_mm(oldmm, mm, next); //如果下一个不是内核线程,那么就切换mm_struct,也就是切换页表

if (unlikely(!prev->mm)) { //这里就是关键,标记为*

prev->active_mm = NULL;

rq->prev_mm = oldmm;

}

...

}

上面的标记为*的if判断很重要,prev->mm什么时候为假呢?有两种情况,第一就是前一个进程是内核线程,第二就是前一个进程是用户进程但是已经退出,我们这里的情况是第二种情况,只有在这种情况下rq->prev_mm被赋值为退出进程的active_mm,而这个和退出进程的mm一样,记住此时mm的引用计数在exit_mm由于被inc了变成了1,那么安全切换了以后在finish_task_switch中判断rq->prev_mm不为空从而被释放。这个解释很合理,但是和tlb刷新的懒惰模式没有关系,而只是保证了退出进程的pgd在mm切换之前不被释放,那么第二种情况就是要说懒惰模式相关的,如果下一个进程是内核线程,那么进入:

if (unlikely(!mm)) { //如果下一个进程的mm为空就说明下一个进程是内核线程

next->active_mm = oldmm;

atomic_inc(&oldmm->mm_count); //又递增了一个引用计数

enter_lazy_tlb(oldmm, next); //进入懒惰模式

}

可以看到,不但释放不了了mm,还又一次递增了它的引用计数,此时引用计数为2,可是不要急,不但进入了上面的if,连后面的if (unlikely(!prev->mm))也会进入,那么在切换以后还是会在mmdorp中递减一次mm的引用计数,这样这个内核线程保持了引用计数为1的mm,当内核线程被切换出去时,if (unlikely(!prev->mm))为真,因为内核线程没有mm(也可以有),那么在切换以后还是会调用mmdrop,此时mm被释放,pgd被释放,一切安然!

因此可以看出,linux对引用计数的设置是多么巧妙啊,一个mm_struct不但有users计数还有count计数,如果我设计的话,这俩肯定就合二为一了,linux的设计者考虑的真是细致,users为0了count可以不为0,为何呢?users为0说明没有进程使用了,可是count不为0说明调度器还要使用,前面说过,调度器在被调度的进程的上下文运行,调度器借用页目录,那么就要保留页目录到调度器不用为止,另外,不但调度器要借用页目录,内核线程还可以借用之,这是为了提高效率,可以少一次切换开销,内核认为少一次切换开销比释放一个mm_struct带来的收益要更有意义。

分享到:
评论

相关推荐

    为Linux内核打补丁

    在Linux内核邮件列表中一个经常被问到的问题就是怎样为Linux内核打一个补丁,或者更具体一点说, 存在这么多的主干/分支,一个补丁到底要打在哪个版本的基础内核上。希望这篇文档能够为你解释明白这 一点。 除了解释...

    Visual C++ 2005入门经典--源代码及课后练习答案

    7.4.4 在构造函数中使用初始化列表 320 7.5 类的私有成员 320 7.5.1 访问私有类成员 323 7.5.2 类的友元函数 324 7.5.3 默认复制构造函数 326 7.6 this指针 328 7.7 类的const对象 331 7.7.1 类的...

    php5手册.chm

    "counter" 扩展 - 一个连续的实例 PHP 5 构建系统 扩展的结构 PDO 驱动 扩展相关 FAQ Zend Engine 2 API 参考 Zend Engine 2 操作码列表 Zend Engine 1 FAQ — FAQ:常见问题 一般信息 邮件列表 获取 PHP 数据库问题...

    PHP官方正版中文帮助手册

     "counter" 扩展 - 一个连续的实例  PHP 5 构建系统  扩展的结构  内存管理  变量的使用  函数的编写  类和对象的使用  资源的使用  INI 设置的使用  流的使用  PDO 驱动  扩展相关 FAQ  Zend Engine 2...

    php5中文手册20111124

    "counter" 扩展 - 一个连续的实例 PHP 5 构建系统 扩展的结构 内存管理 变量的使用 函数的编写 类和对象的使用 资源的使用 INI 设置的使用 流的使用 PDO 驱动 扩展相关 FAQ Zend Engine 2 API 参考 Zend Engine 2 ...

    RFC974_邮件路由与域名系统 .doc

    目录 域服务器了解什么 2 路由原则概述 2 确定消息要发往哪儿 3 发出查询 3 解释MX资源记录列表 3 次要的特殊问题 4 例子 5

    php_manual_zh-PHP中文手册(2010-08-16).part2.rar

    "counter" 扩展 - 一个连续的实例 PHP 5 构建系统 扩展的结构 内存管理 变量的使用 函数的编写 类和对象的使用 资源的使用 INI 设置的使用 流的使用 PDO Driver How-To 扩展相关 FAQ Zend Engine 2 API 参考 Zend ...

    php_manual_zh-PHP中文手册(2010-08-16).part1.rar

    "counter" 扩展 - 一个连续的实例 PHP 5 构建系统 扩展的结构 内存管理 变量的使用 函数的编写 类和对象的使用 资源的使用 INI 设置的使用 流的使用 PDO Driver How-To 扩展相关 FAQ Zend Engine 2 API 参考 Zend ...

    php中文手册

    "counter" 扩展 - 一个连续的实例 PHP 5 构建系统 扩展的结构 内存管理 变量的使用 函数的编写 类和对象的使用 资源的使用 INI 设置的使用 流的使用 PDO 驱动 扩展相关 FAQ Zend Engine 2 API 参考 Zend Engine 2 ...

    php_manual_zh最新中文版20090215

    邮件列表 获取 PHP 数据库问题 安装 — 安装常见问题 编译问题 使用 PHP PHP 和 HTML PHP 和 COM PHP 和其它语言 从 PHP 4 移植到 PHP 5 杂类问题 附录 PHP 及其相关工程的历史 Migrating from ...

    PHP5最新中文手册CHM(2013年10月更新)

    ■"counter" 扩展 - 一个连续的实例 ■PHP 5 构建系统 ■扩展的结构 ■PDO 驱动 ■扩展相关 FAQ ■Zend Engine 2 API 参考 ■Zend Engine 2 操作码列表 ■Zend Engine 1 ■FAQ — FAQ:常见问题 ■一般信息 ■邮件...

    php官方中文帮助手册

    ■"counter" 扩展 - 一个连续的实例 ■PHP 5 构建系统 ■扩展的结构 ■内存管理 ■变量的使用 ■函数的编写 ■类和对象的使用 ■资源的使用 ■INI 设置的使用 ■流的使用 ■PDO 驱动 ■扩展相关 FAQ ■Zend Engine 2...

    PHP基础教程 是一个比较有价值的PHP新手教程!

    但是PHP的邮件列表很是有用而且除非你正在运行像Yahoo!或者Amazon.com这样的极受欢迎的站点,你不会感觉出PHP的速度与其他的有什么不同。最起码我就没有感觉出来!好了,让我们来看看PHP有那些优点: - 学习过程 ...

    php5.5.10中文手册下载[官方版][2014-02-20最后编译]

    ◦"counter" 扩展 - 一个连续的实例 ◦PHP 5 构建系统 ◦扩展的结构 ◦PDO 驱动 ◦扩展相关 FAQ ◦Zend Engine 2 API 参考 ◦Zend Engine 2 操作码列表 ◦Zend Engine 1 •FAQ — FAQ:常见问题◦一般信息 ◦邮件...

    php5.5.10手册官方版【2014-02-20编译】

    ◦"counter" 扩展 - 一个连续的实例 ◦PHP 5 构建系统 ◦扩展的结构 ◦PDO 驱动 ◦扩展相关 FAQ ◦Zend Engine 2 API 参考 ◦Zend Engine 2 操作码列表 ◦Zend Engine 1 •FAQ — FAQ:常见问题◦一般信息 ◦邮件...

    php_manual_zh.chm

    ■"counter" 扩展 - 一个连续的实例 ■PHP 5 构建系统 ■扩展的结构 ■内存管理 ■变量的使用 ■函数的编写 ■类和对象的使用 ■资源的使用 ■INI 设置的使用 ■流的使用 ■PDO 驱动 ■扩展相关 FAQ ■Zend Engine 2...

    测试培训教材

    一个好的测试管理工具应该能把以上几个阶段都管理起来。 测试人员每时每刻都在度量别人的工作成果,而测试人员的工作成果又由谁来度量呢?度量的标准和依据是什么呢?软件测试的度量是测试管理必须仔细思考的问题。...

    PHP和MySQL WEB开发(第4版)

    4.1 创建一个示例应用程序:智能表单邮件 4.2 字符串的格式化 4.2.1 字符串的整理:chop()、ltrim()和trim() 4.2.2 格式化字符串以便显示 4.2.3 格式化字符串以便存储:addslashes()和stripslashes() 4.3 用字符串...

    PHP和MySQL Web开发第4版pdf以及源码

    4.1 创建一个示例应用程序:智能表单邮件 4.2 字符串的格式化 4.2.1 字符串的整理:chop()、ltrim()和trim() 4.2.2 格式化字符串以便显示 4.2.3 格式化字符串以便存储:addslashes()和stripslashes() 4.3 用...

    PHP 5.4.40 Released 中文手册

    •"counter" 扩展 - 一个连续的实例 •PHP 5 构建系统 •扩展的结构 •PDO 驱动 •扩展相关 FAQ •Zend Engine 2 API 参考 •Zend Engine 2 操作码列表 •Zend Engine 1 •FAQ — FAQ:常见问题•一般信息 •邮件...

Global site tag (gtag.js) - Google Analytics