当女作家们越来越多的使用下半身来告诉我什么是文学的时候,当模特们越来越多的使用裸体来告诉我什么是人体艺术的时候,我开始对这个社会困惑了,当行为艺术家们越来越多的使用垃圾堆砌来告诉我什么是波谱的时候,当地下音乐者们越来越多的使用烦躁不安的敲打来告诉我什么是原创的时候,我开始对这个时代迷茫了,当我们系的教授们越来越多的使用旧得不能再旧的教材来告诉我什么是微电子前沿技术的时候,当我们区(开源社区)的兄弟们越来越多的使用复杂得不能再复杂的函数来告诉我什么是编程艺术的时候,我开始对这些电源管理代码晕菜了.

可是没办法,谁叫我不幸生在中国呢,所以我觉得应该抽点时间,找点空闲,抛开代码看看理论.没有一点理论基础,这代码肯定是看不懂的.

电源管理其实发展挺多年了,也算是比较成熟了,主流的技术有两种,APM和ACPI,APM,即Advanced Power Management,高级电源管理,ACPI,即Advanced Configuration and Power Interface,高级配置和电源接口,相比之下,APM容易实现,但是,APM属于一个BIOS的spec,也就是说需要BIOS的支持,这种情况过去在笔记本电脑中比较普遍,不过自从ACPI横空出世之后,APM就将走向没落了,并被行家认为将在不久的将来从市场中消失,毕竟一种特性依赖于BIOS不是什么好事,这个时代强调的是独立.APM是盖茨他们家和Intel一起于1992年提出的,而ACPI则是多了一家小日本的企业,东芝+Intel+Microsoft,这三家于1996年提出了ACPI 1.0,Microsoft的Windows2000也是第一个支持ACPI的操作系统.很显然,ACPI是更为先进的技术,它提供了更为灵活的接口,功能也更为强大,它最有型的地方大概就在于它基本不需要BIOS插手,基本可以通过OS来搞定.它的出现就是为了替代APM,或者说为了克服APM的不足.在如今的Linux发行版里,基本上你可以自己选择使用这两种电源管理的方式,默认应该是ACPI.选择了其中一种就得关掉另一种,不可能说两种方法同时使用,道理很简单,古训有云:一山不能容二虎,除非一公和一母.(顺便友情提醒一下,电源管理只是ACPI的功能中的一部分,除此以外,ACPI还有很多别的功能,干了不少和BIOS抢饭碗的事情.)

比如Red Hat中我们进入setup就可以看到系统服务里面有一个叫做acpid的,还有一个叫做apmd的,就是与这两种电源管理方式对应的服务进程.打开了一个就不要再打开另一个.

Ok,第一个概念,首先必须知道,Linux中的电源管理作为一个子系统(subsystem),它是一个系统工程,这个工程规模大,波及范围广,技术复杂,建设周期长,材料设备消耗大,施工生产流动性强,受自然和社会环境因素影响大,如果说内存管理相当于我国的南水北调工程,那么电源管理就相当于我国的西电东送工程,这两者都可谓任务重,要求高,而且还被全区(开源社区)人民寄予厚望.谁也不希望劳民伤财之后造出一个像三峡大坝这样的垃圾工程来.别以为我这样说很夸张,我们都知道Linux 2.6内核一个最伟大的变化就是建立了一个新的统一的设备模型,可是你知道当年区领导决定建立这个设备模型的初衷是什么吗?就是为了让电源管理工作变得更加容易.(“The device model was originally intended to make power management tasks easier through the maintenance of a representation of the host system's hardware structure.”)只不过后来失去了党的英明指引,写代码的兄弟们走火入魔,踏上了一条不归路,把这个设备模型做得偏离了最初的方向,然而不曾想做出来的东西意义更大了.不仅解决了电源管理的复杂性,而且把所有的设备管理任务都给集中起来了.

那么我们先抛出几个问题,第一,电源管理的含义是什么?一个字,省电.具体来说,电源管理意味着让世界充满爱的同时,让所有的设备处于尽可能低的耗电状态.如果你计算机中某些部分没有被使用,那么就把它关闭(比如显示器)或者让它进入省电的睡眠模式(比如硬盘).

其次,什么情况设备要挂起呢?比如,合上笔记本,又比如用户自己定义了一个系统电源管理策略(像30分钟没有console活动的话就挂起),再比如设备自身有它的电源管理游戏规则(像一个设备五分钟没有活动的话就挂起).于是这就意味着有几种可能,一种是系统级的,即当你合上笔记本的时候,OS负责通知所有的驱动程序,告诉它,需要suspend,即驱动程序提供的suspend函数会被调用,这种情况在江湖上被人叫做system pm,也叫System Sleep Model,另一种情况,不是系统级的,设备级的,就是说我虽然没有合上笔记本,但就单个设备而言,如果我用户希望这个设备处于低耗电的状态,那么驱动程序也应该能够支持,这种情况被道上的兄弟称之为runtime pm,即Runtime Power Management Model.换句话说,我不管别的设备死还是活,总之我这个设备自己想睡就睡,睡到自然醒,谁也别烦我.

因此,第三,从设备驱动的角度来说,我们应该如何作出自己应有的贡献呢?众所周知,电源管理最重要的两个概念就是suspend和resume,即挂起和恢复.而设备驱动被要求保存好设备的上下文,即在挂起的时候,你作为设备驱动,你得保存好设备的一切状态信息,而在resume的时候,你要能够负责恢复这些信息.所以,你必须申请相关的buffer,把东东存在里面,关键的时候拿出来恢复.总的来说,suspend这个过程就是,上级下命令通知driver,driver保存状态,然后执行命令.即notify before save state;save state before power down.而resume这个过程则是,power on and restore state.

第四,刚才说了,建立统一设备模型的初衷是为了打造更佳的电源管理模型,这究竟是如何体现的呢?我们说过,2.6的内核,不管你是usb还是pci还是scsi,你最终会有一棵树,会通过父子关系,兄弟关系把所有的设备连接起来,这样做不是为了体现亲情,而是电源管理的基础,因为操作系统需要以一种合理的顺序去唤醒或者催眠一堆的设备,即,比如,一个PCI总线上的设备必须在它的父设备睡眠之前先进入睡眠,反过来,又必须在它的父设备醒来以后才能醒来.开源战士范仲淹有一句话把这电源管理机制下的子设备诠释得淋漓尽致:先天下之睡而睡,后天下之醒而醒.

第五,从微观经济学来看,设备挂起意味着什么?意味着没有任何进出口贸易的发生,用英语说这叫,quiesce all I/O,即四个坚持,坚持不进行任何DMA操作,坚持不发送任何IRQ,坚持不读写任何数据,坚持不接受上层驱动发过来的任何请求.

最后提两个术语.STR和STD,这是两种Suspend的状态.STR即Suspend to RAM,挂起到内存,STD就是Suspend to Disk,挂起到磁盘.STR就是把系统进入STR前的工作状态数据都存放在内存中去.在STR状态下,电源仍然继续为内存和主板芯片组供电,以确保数据不丢失,而其它设备均处于关闭状态,系统的耗电量极低.一旦我们按下Power按钮,系统就被唤醒,马上从内存中读取数据并恢复到STR之前的工作状态,STR的优点是休眠快唤醒也快,因为数据本来就在内存中.而STD则是把数据保存在磁盘中,很显然,保存在磁盘中要比保存在内存中慢.不过STD最酷的是因为它写到了磁盘中,所以即使电源完全断了,数据也不会丢失.STD就是我们在Windows里面看到的那个Hibernate,即冬眠,或曰休眠,而STR就是我们在Windows里面看到的那个Standby.STR和STD是计算机休眠的两种主要方式.关于STD,多说两句,我想你永远不会忘记,第一次装Linux的时候,有人要你分区的时候分一个swap分区吧?这里就体现了swap分区的一个作用,如果你安装了Suse操作系统,看你的grub里面,一定有一项类似于resume=/dev/sda4吧,就是断电以后重起了之后,从这个分区里把东西读出来的意思.再补一句,ACPI的状态一共有五种,分别是S1,S2,S3,S4,S5,实际上S4就是STD,而STR就是S3,只不过S1,S2,S3差别不大,不过,在Linux中,S1被叫做Standby,而S3被叫做STR.而S5就是Shutdown.在Linux中说挂起,主要说的就是S1,S3和S4.关于S1,S3,S4这三种状态,在/sys/power/state文件里可以有所体现,cat /sys/power/state,你会看到”standby”,”mem”,”disk”的字样.

我们来做一个实验.你打开一个网络连接,比如你去某FTP站点下一部武藤兰的A片,大小500M的那种,然后正在下的时候(即现在进行时的下载),比如下到100M左右,或者说刚开始下,下了3,5M了,这时候你另外开一个终端,执行下面两个命令:

# echo shutdown > /sys/power/disk

# echo disk > /sys/power/state

你的机器将进入传说中的Hibernate状态,你如果觉得不够刺激,可以把电源线都拔掉,然后隔一会儿再按动电源开关,开机,你会发现,你又得重新面对grub,重新选择启动选项,但是再次启动好了之后,那部A片仍然在继续下载.

以上这个实验做的就是STD.当然,你自己做的话失败了别怪我,我早就说过,电源管理这部分是这几年里开源社区最hot的话题之一,问题其实很多,被你遇上了也不必惊讶.不过我需要提醒一下,做之前得确认grub里面kernel那行有那个resume的定义.基本上Suse Linux有这个,Redhat默认好像没有设置这一项.另外,在第一个命令中,你还可以把shutdown换成reboot,这样的话在你执行了第二条命令以后,系统会立刻重起,而不是直接进入power off的状态.重起之后你看到的效果也是一样的,原来什么样,起来之后就还是什么样.

而进行STR的实验就稍微复杂一点,我们这里不多说了,从设备驱动来说,没有必要知道这次挂起是STR还是STD,总之,以上这个STD的实验自从第二个命令执行之后,各个驱动提供的suspend函数就会相继被执行,而之后重起的时候,就会相继调用各驱动提供的resume函数.PM core那边知道如何遍历设备树,所以从usb这边来说,我们甚至不需要做太多递归的事情.

Ok,现在让我们来介绍一下PM Core那边给出的接口.我早就说过,Linux中基本上就是这么几招.首先是把整个内核分成很多个子系统,或者美其名曰Subsystem,然后很多子系统又分为一个核心部分和其他部分,比如我们的usb,就是usbcore和其它部分,usb core这部分负责提供整个usb子系统的初始化,主机控制器的驱动,(当然主机控制器的驱动由于host controller的种类越来越多,host controller driver也便单独构成了一个目录,所以我们有时候说usb core实际上指的是drivers/usb/core和drivers/usb/host目录,但这是相对外围设备而言的,实际上usb core本身是一个模块,而主机控制器的驱动又是单独成为一个模块.)而外围设备的驱动,比如usb-storage,就是直接使用usb core提供出来的接口即可.这种伎俩在Linux内核中比比皆是.而电源管理也是如此,在电源管理子系统中,有一个叫做PM core的,它所包含的是一些核心的代码.而其它各大子系统里要加入power management的功能,就必须使用PM core提供的接口函数.因此,现在是时候让我们来看一看了.

首先,既然我们说电源管理是一个系统工程,那么必然是自上而下的一次大规模改革,不仅仅是设备驱动需要支持它,总线驱动也必须提供相应的支持.还记得在讲八大函数的时候,我们贴出来的结构体变量struct bus_type usb_bus_type么?其中我们把.suspend赋值为usb_suspend,而把.resume赋值为usb_resume.也就是说这两个函数是各类总线不相同的,每类总线都需要实现自己特定的函数,比如PCI总线,我们也能看到类似的定义,来自drivers/pci/pci-driver.c:

542 struct bus_type pci_bus_type = {

543         .name           = "pci",

544         .match          = pci_bus_match,

545         .uevent         = pci_uevent,

546         .probe          = pci_device_probe,

547         .remove         = pci_device_remove,

548         .suspend        = pci_device_suspend,

549         .suspend_late   = pci_device_suspend_late,

550         .resume_early   = pci_device_resume_early,

551         .resume         = pci_device_resume,

552         .shutdown       = pci_device_shutdown,

553         .dev_attrs      = pci_dev_attrs,

它的.suspend就被赋值为pci_device_suspend,而.resume被赋值为pci_device_resume.

而设备驱动呢?提供自己的suspend/resume函数,于是最终总线的那个suspend/resume函数就会去调用设备自己的suspend/resume,比如我们回归usb,hub驱动提供了两个函数,hub_suspend/hub_resume,于是usb_suspend/usb_resume最终就会调用hub_suspend/hub_resume,换句话说,总线的suspend/resume是包装,是面子工程,而设备自己的suspend/resume是实质.

所以,接下来我们有两种选择,第一,直接讲hub_suspend/hub_resume,第二,从usb_suspend/usb_resume开始讲.很显然,前者相对简单,选择前者意味着我们的故事在讲完这两个函数之后就可以结束.我也轻松你也轻松.而选择后者意味着我们选择了一条更加艰难的道路.选择是一个崭新的开端,选择高耸入云的峭崖便需有”路漫漫其修远兮,吾将上下而求索”的信念;选择波涌浪滚的大海便需有”直挂云帆济沧海”的壮志豪情;选择寒风劲厉的荒漠便需有”醉卧沙场君莫笑,古来征战几人回”的博大胸怀.我很无奈,但是人生是一定要选择的,你不可能什么都抓在手上,如果这样,你什么都会失去!经过谨慎思考,我还是决定铤而走险,去探索一下这个复杂的乱世.

但在真正开始讲代码之前,我还是想强调一下,首先,基本上如果你看完了此前的hub_events和那八大函数,你就算是了解hub驱动了.下面的代码属于usb core中电源管理的部分,对于大多数人来说是可以不用再看的,除非:

1.      你和我一样,很无聊,很孤独,对人生很失望.

2.      或者,你欲投身于开源社区的开发事业,想了解最新的开发进展,你想和Alan Stern,想和Oliver Neukum,想和David Brownell同流合污,为Linux内核中usb子系统做出自己的贡献.

Linux那些事儿之我是Hub(25)不说代码说理论相关推荐

  1. Linux那些事儿之我是Hub(9)While You Were Sleeping(二)

    老实说,从函数一个开始的598行直到627行都没有什么可说的.其中需要一提的是,606行,调用usb_buffer_alloc()申请内存,赋给hub->buffer.614行,调用kmallo ...

  2. 【转】Linux那些事儿之我是Hub(7)蝴蝶效应

    朋友,你相信,一只蝴蝶在北京拍拍翅膀,将使得纽约几个月后出现比狂风还厉害的龙卷风吗?看过那部经典的影片蝴蝶效应的朋友们一定会说,这不就是蝴蝶效应吗.没错.蝴蝶效应其实是混沌学理论中的一个概念.它是指对 ...

  3. Linux那些事儿之我是Hub(4)

    这一节我们讲队列. 从前在乡下的时候是不用排队的,村里的人们都很谦让,而且人本来又不多.后来到了县城里,县城里不大,大家去走亲戚去串门去逛街不用坐车不用排队,除了街上的游戏厅人多一点以外,别的地方人都 ...

  4. Linux那些事儿之我是Hub(1)跟我走吧,现在就出发

    最早知道hub是在大学里,复旦的4人间宿舍,条件真好,每个人一张书桌,书桌下面一个网口,但是有时候网口坏了,那可急死人了,要知道当初我们买电脑初衷虽说是为了学习C语言,可是买了之后,C倒是没学,先学了 ...

  5. Linux那些事儿之我是Hub(2)

    莎士比亚曾经说过,不懂hub是怎么工作的就等于不知道usb设备驱动是怎么工作的.这句话一点没错,因为usb设备的初始化都是hub这边发起的,通常我们写usb设备驱动程序都是在已经得到了一个struct ...

  6. Linux那些事儿之我是Hub(19)八大重量级函数闪亮登场(三)

    在开始第三个函数前,2492行至2494行还有三行代码,对udev中的speed,bus_mA,level进行赋值. 先说一下,bus_mA,struct usb_device中的成员,unsigne ...

  7. Linux系统USB驱动目录,Linux那些事儿之我是USB 目录

    目录 第1篇  Linux那些事儿之我是USB Core 1.引子 2 2.它从哪里来 2 3.PK 3 4.漫漫辛酸路 3 5.我型我秀 4 6.我是一棵树 5 7.我是谁 9 8.好戏开始了 11 ...

  8. Linux设备之我是usb,linux那些事儿之我是usb

    linux那些事儿之我是usb,复旦大学教授肖林甫先生给学生们解说的linux操作系统的一些硬件驱动开发的事儿. 内核说明: 我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内 ...

  9. 读书笔记《Linux那些事儿之我是USB》

    第一篇:Linux那些事儿之我是USB Core USB诞生于inel 产生是为了解决前期计算机并口串口的问题,实现一种解决速度,扩展性,易用性的通信方式. 速度:usb2.0高速模式,480MB/s ...

最新文章

  1. tasklist 妙用
  2. create view必须是批处理中仅有的语句_sqlserver 脚本和批处理指令小结
  3. Docker镜像与容器命令 专题
  4. java VM argument_java vm args
  5. custompage.width 不能小数吗_基金净值暴涨暴跌,背后的原因你清楚吗?
  6. php 502状态码,Nginx502状态码处理
  7. 开课吧Java课堂之如何使用FilenameFilter
  8. “从0开始的FreeRTOS”系列教程第一讲
  9. Python基础知识资料收集库
  10. c语言互质欧拉函数,互质与欧拉函数
  11. AGU13-Save The Princess
  12. 视频vv播放量是指什么?怎样提升视频vv播放量?
  13. 43款设计师必备英文设计字体【书法类字体】
  14. Win7 x64 Vad遍历模块
  15. Java如何将URL读取的内容存入本地文件中
  16. android obb在哪,在Android中使用加密的OBB文件
  17. 堆漏洞挖掘——fastbin attack漏洞
  18. RBM受限玻尔兹曼机的一点理解
  19. 淘宝 美团 评论标签效果
  20. 计算机学院军训特色标语,各具学院特色的军训口号

热门文章

  1. null、undefined在ajax请求中的区别
  2. 无动力游乐设施的优势
  3. 开源在线客服系统源码 支持PC官网+微网站+h5页面+小程序+公众号等多端客服 含搭建教程
  4. 渗透测试-菜刀冰蝎蚁剑哥斯拉等webshell工具及特征分析
  5. Android Studio修改标题栏颜色和APP图标
  6. 通过Charles抓取抖音无水印视频
  7. Adaptive Color Attributes for Real-Time Visual Tracking几句话总结
  8. 头歌-信息安全技术-安全审计
  9. by which, in which, from which 语法区别
  10. 您会给这篇高考最牛作文多少分?