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(¤t->sighand->siglock);

recalc_sigpending();  //刚才为了欺骗这个进程才使得人家跑到这里准备被绑,现在绑架任务已经完成,清除掉为了引诱目的而设置的标志位

spin_unlock_irq(¤t->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.51cto.com/512bit/1671121

linux新内核的freeze框架以及意义相关推荐

  1. 如何安装新linux内核,详解Debian系统中安装Linux新内核的流程

    一直对Linux内核很有兴趣,但苦于入门不易,认真看了ldd前5章突然就来感觉了,光看不练不顶用,首先就需要环境搭建. 使用的是Debian 5.0,内核2.6.26,欲安装的新内核为2.6.28,这 ...

  2. linux内核 3.1,快更新:Linux新内核发布 支持USB3.1!

    距离Linux Kernel 4.5.4发布刚刚过去了4天,Linux Kernel 4.6就进入了我们的视线. 昨天,Linus Torvald在内核邮件列表上突然发布Linux Kernel 4. ...

  3. Linux新内核修复14年古老bug

    前往巴西参加了LinuxCon大会之后,Linus Torvalds立即投入工作,近日又放出了Linux Kernel系统内核的2.6.36-rc5预览版. 该版本依然是修复bug和其他问题为主,其中 ...

  4. linux ubuntu内核安装,ubuntu安装linux新内核4.15.7

    1.安装完成后不能挂载exfat的移动硬盘,使用命令 sudo apt-get install exfat-fuse exfat-utils 即可挂载: 2.用fdisk重建分区后(fdisk /de ...

  5. Linux内核进阶----整体框架及子系统概览

    目录 1.概述 2.核心抽象及设计选型 2.1. 对进程和内核的抽象 2.2. 对进程地址空间的抽象 2.3. 支持可重入可抢占的内核 2.4. 放松管控与努力回收 2.5. 单块结构内核+动态加载模 ...

  6. Linux内核 LCD 驱动程序框架

    Linux 内核 LCD 驱动程序框架 1. framebuffer 简介 1.1 什么是 framebuffer 1.2 framebuffer的作用 2. framebuffer 驱动的框架 3. ...

  7. 树莓派Linux内核编译、文件系统、Linux内核驱动基础框架、驱动测试步骤、总线地址

    树莓派高阶开发课程 1. ubuntu18.04版本安装          让程序猿搭建环境太搞笑了,轻松easy! ========================================= ...

  8. linux内核编译与新内核启用

    1.  准备工作 (1)       整理出系统需要支持的硬件.文件系统类型以及网络协议等内容. (2)       建议用命令uname –r 查看一下系统的版本号,如果你的系统版本与将要编译的内核 ...

  9. 树莓派Linux内核源码配置、编译、挂载(boot/kernal/根文件)、开启新内核

    目录 一.树莓派Linux源码配置(适合树莓派) 总体概述 配置的三种方式 1.照搬厂家的配置(使用这种方式) 2.参考厂家的配置(感受一下) 3.完全自主配置(需要一定工作经验) 二.树莓派Linu ...

最新文章

  1. 作为一名后端开发者,你需要学习和掌握的技术栈都有哪些呢?
  2. SpringCloud-Eureka
  3. javascript设计模式系列 - LukeLin - 博客园
  4. java 检索ldap,从LDAP(Java)检索信息
  5. 轻量级锁_一句话撸完重量级锁、自旋锁、轻量级锁、偏向锁、悲观、乐观锁等各种锁 不看后悔系列...
  6. Aspose.Cells基础使用方法整理
  7. Flash 3D 基础
  8. 基于Axis1.4的webservice接口开发(环境搭建)
  9. XRD 数据处理:使用 Origin 进行多谱图对比
  10. SCI论文 Introduction 部分没有思路,快来看看这个写作模板
  11. PHP strpos
  12. 如何在百度地图上标注宾馆饭店(矢量点标注)并导出为图片
  13. 软件项目经理应具备的素质和条件_软件项目经理素质能力的必备要求
  14. 基于人工鱼群优化可倒摆法(QIP)控制器附matlab代码
  15. uni-app 汉字转拼音 搜索和按首字母排序页面
  16. SQL Server 详细安装教程
  17. YDOOK:PyDraw 所见即所得 Python GUI 绘制框架 编程源自 JY Lin
  18. visio流程图工具安装包的下载和安装教程
  19. 请求数据应该放在Created还是Mounted
  20. 中国手机行业发展现状及趋势,行业市场集中度上升「图」

热门文章

  1. Linux入门-7 Linux管道、重定向以及文本处理
  2. shell脚本执行及配置文件
  3. Lambda 表达式有何用处?如何使用?
  4. [Sdoi2017]硬币游戏 [高斯消元 KMP]
  5. 微服务架构的优势与不足
  6. Golang访问Redis初体验
  7. 极客Web前端开发资源大荟萃#007
  8. 【scala初学】scala symbol 符号 -3
  9. 使用UTL_MAIL包实现存储过程邮件发送(转)
  10. 17秋 软件工程 第六次作业 Beta冲刺 总结博客