转自:https://blog.csdn.net/dog250/article/details/5303442

linux的电源管理发展非常迅速,比如在挂起到内存的时候,系统会冻结住所有的进程,也就是所有的进程都不再运行,它们被冻结之前,最后的状态被保存,等到解冻的时候,所有进程恢复运行,linux对此的实现非常巧妙,它没有用特殊的机制来实现这一点,而是用它的freeze框架加上信号处理来实现的,在freeze所有进程的时候并没有将之挂起或者说使其不再运行,而是在信号处理的开始挂了一个类似于钩子的东西,把挂起进程交给信号处理来做,freeze框架要做的就是设置一些标志位来指示信号处理要冻结它了,然后设置此进程的信号附着位,这样该进程在返回用户空间的时候就乖乖进入到冻结状态了,这一点下面的代码分析会详述。这么做有几个好处,一来从设计上讲这又是一个为了低耦合而将一个框架分离成几个模块让各个模块分别承担一部分职责的做法,这样的话,将来如要升级会非常灵活,这看似复杂,但是一旦你理解了精要,还是觉得蛮好的;二来从效果上说,冻结进程其实是一个很危险的操作,2.6内核有了内核抢占,如果一个任务在内核中正执行或者正在内核中睡眠,那么它很有可能持有一把锁或者在等待一把锁,这个时候如果使其无条件的冻结,那么等待解冻的时候,解冻的顺序就要求很高了,要不然很容易死锁,因此把冻结操作安排在进程返回用户空间的时候,这样可以保证进程将不在内核了,既然已经到了返回用户空间的前夕,那么可以保证它肯定不会和内核的其它进程或中断引起竞态,这么做简直好的没法说。
int freeze_processes(void)
{
         error = try_to_freeze_tasks(true); //低强度冻结
...//错误处理以及信息打印
         error = try_to_freeze_tasks(false); //高强度冻结
...//错误以及善后处理
}
static int try_to_freeze_tasks(bool sig_only)
{
         struct task_struct *g, *p;
         unsigned int todo;
...//这些变量和主要问题无关
         do {
                 todo = 0;
                 read_lock(&tasklist_lock);
                 do_each_thread(g, p) {
                         if (frozen(p) || !freezeable(p))
                                 continue;
                         if (!freeze_task(p, sig_only))  //这个函数本质上造成了进程的冻结,但是在它里面你却找不到任何“冻结”的操作。
                                 continue;
                         if (!task_is_stopped_or_traced(p) && !freezer_should_skip(p))
                                
todo++; 
//如果有个进程我们目前无法设置它的冻结位,也就是对它无可奈何的时候,我们将todo递增,以表示要再进行一轮,直到将之冻结或者超过预定期限或者别的什么更加重要的事情发生。

                 } while_each_thread(g, p);
                 read_unlock(&tasklist_lock);
                
yield();
//刚才在freeze_task中不是可能给要冻结的进程发送信号了吗(其实就是设置了一个信号位小小欺骗一下,并没有发送真正的信号)?那么此时就要给这个进程机会使之运行,然后在信号处理中真正冻结,注意yield并不改变当前操作进程的任何标志,仅仅让出cpu而已,理想情况,等到这一轮的进程p被真正冻结以后,这个当前调用try_to_freeze_tasks的进程将继续运行,以便使下一个进程冻结

                 if (time_after(jiffies, end_time))
                         break;
         } while (todo);
...//这下面的我们不必关心
         }
         return todo ? -EBUSY : 0;
}
bool freeze_task(struct task_struct *p, bool sig_only)
{
         if (!freezing(p)) {
                 rmb();
                 if (frozen(p))
                         return false;
                 if (!sig_only || should_send_signal(p))
                         set_freeze_flag(p);
                 else
                         return false;
         }
         if (should_send_signal(p)) {
                 if (!signal_pending(p))
                        
fake_signal_wake_up(p); 
//对进程p设置上_TIF_SIGPENDING标志,然后唤醒它,这其实是一场欺骗,根本没有什么信号被发往p,这么做是因为在进程被唤醒后,检查是否有_TIF_SIGPENDING置位,如有的话,在执行get_signal_to_deliver的时候有个死亡之神在等着它,就是get_signal_to_deliver中马上调用的try_to_freeze,以便进程p上当受骗。

         } else if (sig_only) {
                 return false;
         } else {
                 wake_up_state(p, TASK_INTERRUPTIBLE);
         }
         return true;
}
在get_signal_to_deliver中会调用try_to_freeze,然后这个函数将进程真正冻结,非常安全,因为信号处理在返回用户空间时执行,此时该进程已经退出了内核的执行路径,不会被搅进内核的管理竞态中,是的,此时的事情十分安全:
static inline int try_to_freeze(void)
{
         if (freezing(current)) {
                 refrigerator();
...//别的处理
}
以下这个refrigerator函数彻底冻结了一个进程,即当前进程,它其实就是让当前进程在当前的状态上定格,等到被唤醒以后马上恢复到当前情况,这个当前进程其实睡眠在TASK_UNINTERRUPTIBLE的状态撒谎能够
void refrigerator(void)
{
         long save;
         task_lock(current);
         if (freezing(current)) {
                 frozen_process(); //freezing已经完成,将该进程设置为frozen
                 task_unlock(current);
...
         save = current->state;
         spin_lock_irq(&current->sighand->siglock);
         recalc_sigpending();  //刚才为了欺骗这个进程才使得人家跑到这里准备被绑,现在绑架任务已经完成,清除掉为了引诱目的而设置的标志位
         spin_unlock_irq(&current->sighand->siglock);
         for (;;) {
                 set_current_state(TASK_UNINTERRUPTIBLE);
                 if (!frozen(current))
                         break;
                 schedule();
         }
         __set_current_state(save);  //被唤醒,继续执行
}
唤醒的过程比这简单多了,就是一个一个的调用wake_up_process而已,这实在太简单了以至于不说了。这里就有了一个问题,在2.6.25以来新内核中,增加了对cgroup的支持,那么某种意义上,linux开始支持只有商用unix上才有“容器”的概念,linux内核陆续增加了cgroup对内存,文件等的控制,用户可以对单个group进行资源限制了,在linux内核上,以资源作为区分相当于运行着好几个虚拟机,每台虚拟机都是资源分配的一个单位,如果联系前面刚说的freeze框架的话就会发现,如果把一个cgroup的进程作为一个组进行冻结的话会很有用,比如这样的话我们可以将全组的进程“热迁移”到别的处理器,其实不是真正的热迁移,毕竟那些进程已经不再运行了,呵呵。其实freeze框架本身就可以对热插拔cpu进行支持,cgroup的freeze框架仅仅使得冻结的粒度可以切割了,不再是要么一下子冻结全部然后唤醒全部,要么就一个也不冻结,这就需要一个内核补丁,在最新的2.6.29内核中已经有了这样的机制,其实就是在冻结的时候加上了一个croup作为参数,然后把这个cgroup的进程应用上面的机制将之冻结,linux总是这样,一开始设计时就很灵活,然后后面修改加补丁的时候才不受罪。

另外在2.6.29内核中还有了文件系统的冻结,其实和上述的进程冻结一样,如果说进程冻结是将进程冻结到内存从而为了cpu而做一点事的话,那么文件系统就是将文件系统冻结到磁盘,然后让备份系统做一点事,以使得备份时是一个稳定又一致的文件系统,其实很简单,所谓冻结就是不让挂载不让写,其实用信号量就可以搞定,冻结一个文件系统的时候要得到其bdev的一个相关的信号量,而且挂载和写这个文件系统的时候也要得到这个信号量,如此就完结了。

版权声明:本文为博主原创,无版权,未经博主允许可以随意转载,无需注明出处,随意修改或保持可作为原创! https://blog.csdn.net/dog250/article/details/5303442

linux新内核的freeze框架以及意义【转】的更多相关文章

  1. linux新内核中关闭硬盘的DMA

    vortex86 SIS550 Minit-5250E瘦客户机,使用CF卡启动,显示不支持DMA. 搜索得新内核已基本不再使用ide=nodma参数了,查到这篇文章:“Debian下关闭CF卡的DMA ...

  2. linux新内核的时钟机制代码

    http://blog.chinaunix.net/uid-22810130-id-384173.html 如果说cfs是linux的一个很有创意的机制的话,那么linux中另一个创意就是nohz,我 ...

  3. Linux新内核:提升系统性能 --Linux运维的博客

    http://blog.csdn.net/linuxnews/article/details/52864182

  4. linux如何编译安装新内核支持NTFS文件系统?(以redhat7.2x64为例)

    内核,是一个操作系统的核心.它负责管理系统的进程.内存.设备驱动程序.文件和网络系统,决定着系统的性能和稳定性.Linux作为一个自由软件,在广大爱好者的支持下,内核版本不断更新.新的内核修订了旧内核 ...

  5. Virtio:针对 Linux 的 I/O 虚拟化框架

    Virtio:针对 Linux 的 I/O 虚拟化框架 --http://www.ibm.com/developerworks/cn/linux/l-virtio/#ibm-pcon 使用 KVM 和 ...

  6. linux设备驱动程序--串行通信驱动框架分析

    linux 串行通信接口驱动框架 在学习linux内核驱动时,不论是看linux相关的书籍,又或者是直接看linux的源码,总是能在linux中看到各种各样的框架,linux内核极其庞杂,linux各 ...

  7. 深入linux kernel内核配置选项

    ============================================================================== 深入linux kernel内核配置选项 ...

  8. (转)linux IO 内核参数调优 之 参数调节和场景分析

    1. pdflush刷新脏数据条件 (linux IO 内核参数调优 之 原理和参数介绍)上一章节讲述了IO内核调优介个重要参数参数. 总结可知cached中的脏数据满足如下几个条件中一个或者多个的时 ...

  9. [转]Linux中文件权限目录权限的意义及权限对文件目录的意义

    转自:http://www.jb51.net/article/77458.htm linux中目录与文件权限的意义 一.文件权限的意义 r:可以读这个文件的具体内容: w:可以编辑这个文件的内容,包括 ...

随机推荐

  1. 沉迷Link-Cut tree无法自拔之:[BZOJ2049]洞穴勘探(蒟蒻的LCT板子)

    来自蒟蒻 \(Hero \_of \_Someone\) 的 \(LCT\) 学习笔记 最近学了一波 \(LCT\) , 于是怒刷 \(LCT\) 合集...... $ $ 学的时候借鉴了 Clove ...

  2. How to Add Trust Sites into IE before IE10 through Group Policy

    Due to IE10 published, I'll conclude the methods that how to add trust sites in to IE of the version ...

  3. 【codeforces 103E】 Buying Sets

    http://codeforces.com/problemset/problem/103/E (题目链接) 题意 给出$n$个数,每个数与一个集合相关联.从其中选出最小的若干个数,选出的数的个数与这些 ...

  4. Python文件和异常

    程序和运行时数据是在内存中驻留的,涉及到数据交换的地方,通常是磁盘.网络等,因此需要IO接口. IO编程中,Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单 ...

  5. Gradle 从svn 中检出的父项目后处理配置【我】

    前提: 一个用gradle配置的 类似maven的聚合项目的项目,然后它在svn上就是一个父工程的目录. 检出方式: 在eclipse中,直接用svn资源库检出 父项目 的目录. 然后,在父项目下面的 ...

  6. java.lang.OutOfMemoryError: unable to create new native thread 居然是MQ问题

    问题: 开发环境,之前一直正常,某天突然用tomcat启动项目后时不时报如下错误: java.lang.OutOfMemoryError: unable to create new native th ...

  7. 浅谈python函数签名

    函数签名对象,表示调用函数的方式,即定义了函数的输入和输出. 在Python中,可以使用标准库inspect的一些方法或类,来操作或创建函数签名. 获取函数签名及参数 使用标准库的signature方 ...

  8. 拒绝了对对象 'Proc_LHDashBoard' (数据库 'jy',架构 'dbo')的 EXECUTE 权限。”

    没有权限,在数据库里面开启权限.找到你那个访问的用户名,然后:

  9. jQuery 选择城市,显示对应的即时时区时间

    因客户需要,我们CRM系统中,jQuery 弄个时区插件 如图: HTML: <div id="cityDate"> <i class="P_arrow ...

  10. JacobMathType

    JACOB是一个 Java到微软的COM接口的桥梁.使用JACOB允许任何JVM访问COM对象,从而使JAVA应用程序能够调用COM对象,;MathType 是由美国Design Science公司开 ...