转自: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. bzoj1691/luogu2869 [USACO07DEC]挑剔的美食家 (STL::set)

    给牛和草都按价格排序,然后贪心地把草给牛(就是尽量给满足价格的.要求的美味度最高但不超过这个草的美味度的牛) 这个可以用一个平衡树来维护,偷懒直接用multiset了 #include<bits ...

  2. jieba分词器

    始终觉得官方文档是最好的学习途径.嗯,我只是一个大自然的搬运工.传送门https://github.com/fxsjy/jieba 1.分词 jieba.cut 方法接受三个输入参数: 需要分词的字符 ...

  3. 洛谷P4843 清理雪道

    题意:给你DAG,求最小路径边覆盖.路径可重. 解:首先可以想到边转点,发现有n²条边,果断超时. 有源汇有上下界最小流. 建图:每条边都建立一条边,流量限制为[1, 1]. 源点向每个点连边,因为都 ...

  4. JAVA8给我带了什么——Optional和CompletableFuture

    不管是JAVA,还是.NET.我们常常会看到空异常(NullPointerException).这种异常都是在运行的过程中出现.往往是变量是一个null值.但是你引用这个变量的后继字段或是方法.所以我 ...

  5. JAVA8给我带了什么——流(入门)

    JAVA8有一个新功能——流.笔者简单的看一下流.然后默默的闭上眼睛.感叹一声:这不是.NET里面的Linq吗?如果你们当中有谁做过.NET程序员的话,对于流的学习其实帮助是很大的.但是要明白你现在是 ...

  6. 【LOJ#10002】喷水装置

    题目大意:给定一段区间 [l,r] ,N 条线段,求至少用多少条线段能够覆盖整个区间,不能覆盖输出-1. 题解:每次在起点小于当前位置的线段集合中选择有端点最大的位置作为下一个位置,并更新答案,如果当 ...

  7. C++11并发——多线程std::thread (一)

    https://www.cnblogs.com/haippy/p/3284540.html 与 C++11 多线程相关的头文件 C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是< ...

  8. os模块(二十)

    os模块是与操作系统交互的一个接口 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作目录:相 ...

  9. 第一章:认识Ajax

    第一节:Ajax 简介 1,Ajax 是一种网页开发技术,(Asynchronous Javascript + XML)异步JavaScript 和XML:2,Ajax 是异步交互,局部刷新:3,Aj ...

  10. pyautogui_pdf内容提取到excel内_3

    sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...