标准linux休眠和唤醒机制分析
linux进程的睡眠和唤醒

LKD中的讲解休眠(被阻塞)的进程处于一个特殊的不可执行状态。
这点非常重要,否则,没有这种特殊状态的话,调度程序就可能选出一个本不愿意被执行的进程,更糟糕的是,休眠就必须以轮询的方式实现了。
进程休眠有各种原因,但肯定都是为了等待一些事件。
事件可能是一段时间、从文件I/O读更多数据,或者是某个硬件事件。
一个进程还有可能在尝试获得一个已经占用的内核信号量时被迫进入休眠。
休眠的一个常见原因就是文件I/O --如进程对一个文件执行了read()操作,而这需要从磁盘里读取。
还有,进程在获取键盘输入的时候也需要等待。
无论哪种情况,内核的操作都相同:进程把它自己标记成休眠状态,把自己从可执行队列移出,放入等待队列,然后调用schedule()选择和执行一个其他进程。
唤醒的进程刚好相反:进程被设置为可执行状态,然后再从等待队列中移到可执行队列。
休眠有两种相关的进程状态:TASK_INTERRUPTIBLE andTASK_UNINTERRUPTIBLE。
它们的惟一区别是处于TASK_UNINTERRUPTIBLE状态的进程会忽略信号,而处于TASK_INTERRUPTIBLE状态的进程如果收到信号会被唤醒并处理信号(然后再次进入等待睡眠状态)。
两种状态的进程位于同一个等待队列上,等待某些事件,不能够运行。
休眠通过等待队列进行处理。
等待队列是由等待某些事件发生的进程组成的简单链表。
内核用wake_queue_head_t来代表等待队列。
等待队列可以通过DECLARE_W AITQUEUE()静态创建,也可以有init_waitqueue_head()动态创建。
进程把自己放入等待队列中并设置成不可执行状态。
等与等待队列相关的事件发生的时候,队列上的进程会被唤醒。
为了避免产生竞争条件,休眠和唤醒的实现不能有纰漏。
针对休眠,以前曾经使用过一些简单的接口。
但那些接口会带来竞争条件;有可能导致在判断条件变为真后进程却开始了休眠,那样就会使进程无限期地休眠下去。
linux sleep函数 实现原理

在Linux系统中,`sleep`函数用于让当前进程挂起一段指定的时间,其实现原理涉及到
操作系统的调度和定时器机制。
实现原理大致如下:
1. 系统调用:当用户程序调用`sleep`函数时,会触发一个系统调用(System Call),将控制权转交给操作系统内核。
2. 设置定时器:内核收到`sleep`函数的调用后,会创建一个定时器(Timer)并将其加
入内核的定时器队列中。
这个定时器会在未来的某个时刻触发。
3. 进程挂起:接着,内核会将当前进程标记为休眠状态,并从可调度进程队列中移除,使得该进程不会被调度到CPU上执行。
4. 等待触发:在定时器设定的时间到达后,内核会将之前休眠的进程重新标记为可运
行状态,然后放回可调度进程队列中,从而使得该进程再次有机会被调度执行。
需要注意的是,Linux中的`sleep`函数以秒为单位指定休眠时间,另外还有一个名为
`usleep`的函数可以以微秒为单位进行休眠。
总的来说,`sleep`函数的实现利用了操作系统内核的定时器机制,通过设置定时器来实现进程的挂起和唤醒,从而实现了让当前进程休眠一定时间的功能。
android休眠与唤醒驱动流程分析

android休眠与唤醒驱动流程分析标准linux休眠过程:●power management notifiers are executed with PM_SUSPEND_PREPARE●tasks are frozen●target system sleep state is announced to the platform-handling code●devices are suspended●platform-specific global suspend preparation methods are executed●non-boot CPUs are taken off-line●interrupts are disabled on the remaining (main) CPU●late suspend of devices is carried out (一般有一些BUS driver的动作进行)●platform-specific global methods are invoked to put the system to sleep标准linux唤醒过程:●t he main CPU is switched to the appropriate mode, if necessary●early resume of devices is carried out (一般有一些BUS driver的动作进行)●interrupts are enabled on the main CPU●non-boot CPUs are enabled●platform-specific global resume preparation methods are invoked●devices are woken up●tasks are thawed●power management notifiers are executed with PM_POST_SUSPEND用户可以通过sys文件系统控制系统进入休眠:查看系统支持的休眠方式:#cat /sys/power/state常见有standby(suspend to RAM)、mem(suspend to RAM)和disk(suspend to disk),只是standby耗电更多,返回到正常工作状态的时间更短。
Kernel休眠与唤醒综述

Linux Kernel and Android 休眠与唤醒(中文版) (转)简介休眠/唤醒在嵌入式Linux中是非常重要的部分,嵌入式设备尽可能的进入休眠状态来延长电池的续航时间.这篇文章就详细介绍一下Linux中休眠/唤醒是如何工作的, 还有Android中如何把这部分和Linux的机制联系起来的.版本信息∙Linux Kernel: v2.6.28∙Android: v2.0对于休眠(suspend)的简单介绍在Linux中,休眠主要分三个主要的步骤:1.冻结用户态进程和内核态任务2.调用注册的设备的suspend的回调函数o顺序是按照注册顺序3.休眠核心设备和使CPU进入休眠态冻结进程是内核把进程列表中所有的进程的状态都设置为停止,并且保存下所有进程的上下文. 当这些进程被解冻的时候,他们是不知道自己被冻结过的,只是简单的继续执行.如何让Linux进入休眠呢?用户可以通过读写sys文件/sys /power/state 是实现控制系统进入休眠. 比如命令系统进入休眠. 也可以使用来得到内核支持哪几种休眠方式.Linux Suspend 的流程相关的文件:你可以通过访问Linux内核网站来得到源代码,下面是文件的路径: ∙linux_soruce/kernel/power/main.c∙linux_source/kernel/arch/xxx/mach-xxx/pm.c∙linux_source/driver/base/power/main.c接下来让我们详细的看一下Linux是怎么休眠/唤醒的. Let 's going to see how these happens.用户对于/sys/power/state 的读写会调用到 main.c中的state_store(), 用户可以写入 const char * const pm_state[] 中定义的字符串, 比如"mem", "standby".然后state_store()会调用enter_state(), 它首先会检查一些状态参数,然后同步文件系统. 下面是代码:准备, 冻结进程当进入到suspend_prepare()中以后, 它会给suspend分配一个虚拟终端来输出信息, 然后广播一个系统要进入suspend的Notify, 关闭掉用户态的helper 进程, 然后一次调用suspend_freeze_processes()冻结所有的进程, 这里会保存所有进程当前的状态, 也许有一些进程会拒绝进入冻结状态, 当有这样的进程存在的时候, 会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程.让外设进入休眠现在, 所有的进程(也包括workqueue/kthread) 都已经停止了, 内核态人物有可能在停止的时候握有一些信号量, 所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁, 所以在外设的suspend()函数里面作lock/unlock 锁要非常小心,这里建议设计的时候就不要在suspend()里面等待锁. 而且因为suspend的时候,有一些Log是无法输出的,所以一旦出现问题,非常难调试.然后kernel在这里会尝试释放一些内存.最后会调用suspend_devices_and_enter()来把所有的外设休眠, 在这个函数中, 如果平台注册了suspend_pos(通常是在板级定义中定义和注册), 这里就会调用 suspend_ops->begin(), 然后driver/base/power/main.c 中的device_suspend()->dpm_suspend() 会被调用,他们会依次调用驱动的suspend() 回调来休眠掉所有的设备.当所有的设备休眠以后, suspend_ops->prepare()会被调用, 这个函数通常会作一些准备工作来让板机进入休眠. 接下来Linux,在多核的CPU中的非启动CPU会被关掉, 通过注释看到是避免这些其他的CPU造成race condion,接下来的以后只有一个CPU在运行了.suspend_ops 是板级的电源管理操作, 通常注册在文件arch/xxx/mach-xxx/pm.c 中.接下来, suspend_enter()会被调用, 这个函数会关闭arch irq, 调用device_power_down(), 它会调用suspend_late()函数, 这个函数是系统真正进入休眠最后调用的函数, 通常会在这个函数中作最后的检查. 如果检查没问题, 接下来休眠所有的系统设备和总线, 并且调用 suspend_pos->enter() 来使CPU进入省电状态. 这时候,就已经休眠了.代码的执行也就停在这里了.Resume如果在休眠中系统被中断或者其他事件唤醒, 接下来的代码就会开始执行, 这个唤醒的顺序是和休眠的循序相反的,所以系统设备和总线会首先唤醒,使能系统中断, 使能休眠时候停止掉的非启动CPU, 以及调用suspend_ops->finish(), 而且在suspend_devices_and_enter()函数中也会继续唤醒每个设备,使能虚拟终端, 最后调用 suspend_ops->end().在返回到enter_state()函数中的, 当 suspend_devices_and_enter() 返回以后, 外设已经唤醒了, 但是进程和任务都还是冻结状态, 这里会调用suspend_finish()来解冻这些进程和任务, 而且发出Notify来表示系统已经从suspend状态退出, 唤醒终端.到这里, 所有的休眠和唤醒就已经完毕了, 系统继续运行了.Android 休眠(suspend)在一个打过android补丁的内核中, state_store()函数会走另外一条路,会进入到request_suspend_state()中, 这个文件在earlysuspend.c中. 这些功能都是android系统加的, 后面会对earlysuspend和late resume 进行介绍.涉及到的文件:∙linux_source/kernel/power/main.c∙linux_source/kernel/power/earlysuspend.c∙linux_source/kernel/power/wakelock.c特性介绍Early SuspendEarly suspend 是android 引进的一种机制, 这种机制在上游备受争议,这里不做评论. 这个机制作用在关闭显示的时候, 在这个时候, 一些和显示有关的设备, 比如LCD背光, 比如重力感应器, 触摸屏, 这些设备都会关掉, 但是系统可能还是在运行状态(这时候还有wake lock)进行任务的处理, 例如在扫描SD卡上的文件等. 在嵌入式设备中, 背光是一个很大的电源消耗,所以android会加入这样一种机制.Late ResumeLate Resume 是和suspend 配套的一种机制, 是在内核唤醒完毕开始执行的. 主要就是唤醒在Early Suspend的时候休眠的设备.Wake LockWake Lock 在Android的电源管理系统中扮演一个核心的角色. Wake Lock是一种锁的机制, 只要有人拿着这个锁, 系统就无法进入休眠, 可以被用户态程序和内核获得. 这个锁可以是有超时的或者是没有超时的, 超时的锁会在时间过去以后自动解锁. 如果没有锁了或者超时了, 内核就会启动休眠的那套机制来进入休眠.Android Suspend当用户写入mem 或者 standby到 /sys/power/state中的时候, state_store()会被调用, 然后Android会在这里调用 request_suspend_state() 而标准的Linux会在这里进入enter_state()这个函数. 如果请求的是休眠, 那么early_suspend这个workqueue就会被调用,并且进入early_suspend状态.Early Suspend在early_suspend()函数中, 首先会检查现在请求的状态还是否是suspend, 来防止suspend的请求会在这个时候取消掉(因为这个时候用户进程还在运行),如果需要退出, 就简单的退出了. 如果没有, 这个函数就会把early suspend中注册的一系列的回调都调用一次, 然后同步文件系统, 然后放弃掉main_wake_lock, 这个wake lock是一个没有超时的锁,如果这个锁不释放, 那么系统就无法进入休眠.Late Resume当所有的唤醒已经结束以后, 用户进程都已经开始运行了, 唤醒通常会是以下的几种原因:∙来电如果是来电, 那么Modem会通过发送命令给rild来让rild通知WindowManager 有来电响应,这样就会远程调用PowerManagerService来写"on" 到/sys/power/state 来执行late resume的设备, 比如点亮屏幕等.∙用户按键用户按键事件会送到WindowManager中, WindowManager会处理这些按键事件,按键分为几种情况, 如果案件不是唤醒键(能够唤醒系统的按键) 那么WindowManager会主动放弃wakeLock来使系统进入再次休眠, 如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行 Late Resume.∙Late Resume 会依次唤醒前面调用了Early Suspend的设备.Wake Lock我们接下来看一看wake lock的机制是怎么运行和起作用的, 主要关注wakelock.c文件就可以了.wake lock 有加锁和解锁两种状态, 加锁的方式有两种, 一种是永久的锁住, 这样的锁除非显示的放开, 是不会解锁的, 所以这种锁的使用是非常小心的. 第二种是超时锁, 这种锁会锁定系统唤醒一段时间, 如果这个时间过去了, 这个锁会自动解除.锁有两种类型:1.WAKE_LOCK_SUSPEND 这种锁会防止系统进入睡眠2.WAKE_LOCK_IDLE 这种锁不会影响系统的休眠, 作用我不是很清楚.在wake lock中, 会有3个地方让系统直接开始suspend(), 分别是:1.在wake_unlock()中, 如果发现解锁以后没有任何其他的wake lock了,就开始休眠2.在定时器都到时间以后, 定时器的回调函数会查看是否有其他的wakelock, 如果没有, 就在这里让系统进入睡眠.3.在wake_lock() 中, 对一个wake lock加锁以后, 会再次检查一下有没有锁, 我想这里的检查是没有必要的, 更好的方法是使加锁的这个操作原子化, 而不是繁冗的检查. 而且这样的检查也有可能漏掉. Suspend当wake_lock 运行 suspend()以后, 在wakelock.c的suspend()函数会被调用,这个函数首先sync文件系统,然后调用pm_suspend(request_suspend_state),接下来pm_suspend()就会调用enter_state()来进入Linux的休眠流程..Android于标准Linux休眠的区别pm_suspend() 虽然会调用enter_state()来进入标准的Linux休眠流程,但是还是有一些区别:∙当进入冻结进程的时候, android首先会检查有没有wake lock,如果没有, 才会停止这些进程, 因为在开始suspend和冻结进程期间有可能有人申请了 wake lock,如果是这样, 冻结进程会被中断.∙在 suspend_late()中, 会最后检查一次有没有wake lock, 这有可能是某种快速申请wake lock,并且快速释放这个锁的进程导致的,如果有这种情况, 这里会返回错误, 整个suspend就会全部放弃.如果pm_suspend()成功了,LOG的输出可以通过在kernel cmd里面增加"no_console_suspend" 来看到suspend和resume过程中的log输出。
Linux休眠

打过android补丁的内核中,state_store()函数会走另外一条路,会进入到request_suspend_state()中,这个文件在earlysuspend.c中.这些功能都是android系统加的,后面会对earlysuspend和lateresume进行介绍。
EXPORT_SYMBOL(wake_unlock);//释放lock
EXPORT_SYMBOL_GPL(device_power_up);//打开特殊的设备
EXPORT_SYMBOL_GPL(device_power_down);//关闭特殊设备
EXPORT_SYMBOL_GPL(device_resume);//重新存储设备的状态;
EXPORT_SYMBOL_GPL(device_suspend);:保存系统状态,并结束掉系统中的设备;
EXPORT_SYMBOL(register_early_suspend);//注册earlysuspend的驱动
EXPORT_SYMBOL(unregister_early_suspend);//取消已经注册的earlysuspend的驱动
2)LateResume
Late Resume是和suspend配套的一种机制,是在内核唤醒完毕开始执行的,主要就是唤醒在EarlySuspend的时候休眠的设备.
当所有的唤醒已经结束以后,用户进程都已经开始运行了,唤醒通常会是以下的几种原因:
来电
如果是来电,那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写"on"到/sys/power/state来执行lateresume的设备,比如点亮屏幕等.
linux休眠流程

linux休眠流程Linux休眠流程休眠是Linux操作系统中的一种省电模式,它允许计算机在不使用时进入低功耗状态,以节省能源并延长电池寿命。
本文将介绍Linux系统中的休眠流程,以便读者对其有一个清晰的了解。
一、什么是休眠模式休眠模式是计算机进入低功耗状态的一种方式。
在休眠模式下,计算机会将所有正在运行的程序和数据保存在硬盘上,并关闭主要硬件设备,如显示器和硬盘驱动器。
这样可以将计算机的功耗降到最低,同时保留所有的工作进度。
二、进入休眠模式的触发条件当计算机处于空闲状态一段时间后,操作系统会自动触发休眠模式。
同时,用户也可以手动将计算机设置为休眠模式,例如通过关闭显示器或选择休眠选项。
三、休眠模式的启动流程1. 操作系统接收到休眠请求后,会先检查当前所有正在运行的程序和数据,将其保存到硬盘上的临时文件中。
这一过程称为"冻结"。
2. 一旦所有程序和数据都被保存,操作系统会关闭主要硬件设备,例如显示器、硬盘驱动器和外部设备。
3. 操作系统将计算机的状态保存到BIOS芯片中,以便在唤醒时能够恢复到原来的状态。
4. 最后,操作系统向计算机发送指令,使其进入休眠模式。
四、从休眠模式中唤醒当用户需要使用计算机时,可以通过以下几种方式唤醒它:1. 按下计算机的电源按钮。
2. 移动鼠标或按下键盘上的任意键。
3. 通过设置唤醒定时器,在指定时间后自动唤醒计算机。
五、休眠模式的优势和劣势休眠模式的主要优势是可以节省能源,延长电池寿命,并且在唤醒后可以立即恢复到之前的状态,不会丢失任何数据。
然而,休眠模式也有一些劣势,例如休眠状态下计算机仍然消耗一定的电量,并且在唤醒时需要一定的时间来恢复。
六、总结通过本文,我们了解了Linux系统中休眠模式的流程。
休眠模式是一种省电的方式,它可以将计算机的功耗降到最低,并在唤醒时恢复到之前的状态。
尽管休眠模式有一些劣势,但它仍然是一种非常实用的功能,可以帮助我们更好地管理和节省能源。
46.Linux电源管理-休眠与唤醒

46.Linux电源管理-休眠与唤醒1.休眠⽅式在内核中,休眠⽅式有很多种,可以通过下⾯命令查看# cat /sys/power/state//来得到内核⽀持哪⼏种休眠⽅式.常⽤的休眠⽅式有freeze,standby, mem, diskfreeze:冻结I/O设备,将它们置于低功耗状态,使处理器进⼊空闲状态,唤醒最快,耗电⽐其它standby, mem, disk⽅式⾼standby:除了冻结I/O设备外,还会暂停系统,唤醒较快,耗电⽐其它 mem, disk⽅式⾼mem: 将运⾏状态数据存到内存,并关闭外设,进⼊等待模式,唤醒较慢,耗电⽐disk⽅式⾼disk: 将运⾏状态数据存到硬盘,然后关机,唤醒最慢⽰例:# echo standby > /sys/power/state// 命令系统进⼊standby休眠.2.唤醒⽅式当我们休眠时,如果想唤醒,则需要添加中断唤醒源,使得在休眠时,这些中断是设为开启的,当有中断来,则会退出唤醒,常见的中断源有按键,USB等.3.以按键驱动为例(基于内核3.10.14)在内核中,有个input按键⼦系统"gpio-keys"(位于driver/input/keyboard/gpio.keys.c),该平台驱动platform_driver已经在内核中写好了(后⾯会简单分析)我们只需要在内核启动时,注册"gpio-keys"平台设备platform_device,即可实现⼀个按键驱动.3.1⾸先使板卡⽀持input按键⼦系统(基于mips君正X1000的板卡)查看Makefile,找到driver/input/keyboard/gpio.keys.c需要CONFIG_KEYBOARD_GPIO宏⽅式1-修改对应板卡的defconfig⽂件,添加宏:CONFIG_INPUT=y //⽀持input⼦系统(加载driver/input⽂件)CONFIG_INPUT_KEYBOARD=y //⽀持input->keyboards(加载driver/input/keyboard⽂件)CONFIG_KEYBOARD_GPIO=y //⽀持input->keyboards->gpio按键(加载gpio.keys.c)⽅式2-进⼊make menuconfig-> Device Drivers-> Input device support-> [*]Keyboards[*] GPIO Buttons3.2修改好后,接下来写my_button.c⽂件,来注册platform_device#include <linux/platform_device.h>#include <linux/gpio_keys.h>#include <linux/input.h>struct gpio_keys_button __attribute__((weak)) board_buttons[] = {{.gpio = GPIO_PB(31), //按键引脚.code = KEY_POWER, //⽤来定义按键产⽣事件时,要上传什么按键值.desc = "power key", //描述信息,不填的话会默认设置为"gpio-keys".wakeup =1, //设置为唤醒源. debounce_interval =10, //设置按键防抖动时间,也可以不设置.type = EV_KEY,.active_low = 1, //低电平有效},};static struct gpio_keys_platform_data board_button_data = {.buttons = board_buttons,.nbuttons = ARRAY_SIZE(board_buttons),};struct platform_device my_button_device = {.name = "gpio-keys",.id = -1,.num_resources = 0,.dev = {.platform_data = &board_button_data,}};static int __init button_base_init(void){platform_device_register(&my_button_device);return0;}arch_initcall(button_base_init);上⾯的arch_initcall()表⽰:会将button_base_init函数放在内核链接脚本.initcall3.init段中,然后在内核启动时,会去读链接脚本,然后找到button_base_init()函数,并执⾏它.通常,在内核中,platform 设备的初始化(注册)⽤arch_initcall()调⽤⽽驱动的注册则⽤module_init()调⽤,因为module_init()在arch_initcall()之后才调⽤因为在init.h中定义:#define pure_initcall(fn) __define_initcall(fn, 0)#define core_initcall(fn) __define_initcall(fn, 1)#define core_initcall_sync(fn) __define_initcall(fn, 1s)#define postcore_initcall(fn) __define_initcall(fn, 2)#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)#define arch_initcall(fn) __define_initcall(fn, 3) // arch_initcall()优先级为3,⽐module_init()先执⾏#define arch_initcall_sync(fn) __define_initcall(fn, 3s)#define subsys_initcall(fn) __define_initcall(fn, 4)#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)#define fs_initcall(fn) __define_initcall(fn, 5)#define fs_initcall_sync(fn) __define_initcall(fn, 5s)#define rootfs_initcall(fn) __define_initcall(fn, rootfs)#define device_initcall(fn) __define_initcall(fn, 6) //module_init()优先级为6#define device_initcall_sync(fn) __define_initcall(fn, 6s)#define late_initcall(fn) __define_initcall(fn, 7)#define late_initcall_sync(fn) __define_initcall(fn, 7s)... ...#define __initcall(fn) device_initcall(fn)#define module_init(x) __initcall(fn) //module_init 等于 device_initcall3.3然后将my_button.c⽂件添加到Makefile中编译内核后,便实现⼀个简单的按键唤醒休眠了.接下来开始分析platform_driver(位于driver/input/keyboard/gpio.keys.c),看看是如何注册按键和实现唤醒的.4.分析driver/input/keyboard/gpio.keys.c4.1该⽂件⾥有常⽤的函数有static int gpio_keys_probe(struct platform_device *pdev);设置按键和input_dev,注册input-key⼦系统static int gpio_keys_setup_key(struct platform_device *pdev,struct input_dev *input, struct gpio_button_data *bdata,const struct gpio_keys_button *button);设置GPIO,设置input结构体⽀持的按键值,设置中断,设置防抖动机制static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id);按键中断函数,如果是中断源,则通过pm_stay_awake()通知pm⼦系统唤醒,如果有防抖动,则延时并退出,否则通过schedule_work()来调⽤gpio_keys_gpio_work_func()⼀次static void gpio_keys_gpio_timer(unsigned long _data);定时器超时处理函数,⽤来实现防抖动,⾥⾯会通过schedule_work()来调⽤⼀次gpio_keys_gpio_work_func();static void gpio_keys_gpio_work_func(struct work_struct *work);处理gpio事件函数,⽤来上报input事件,并判断按键中断源,如果是的话,则调⽤pm_relax(),通知pm⼦系统唤醒⼯作结束void pm_wakeup_event(struct device *dev, unsigned int msec);通知pm(power manager), 唤醒休眠static int gpio_keys_suspend(struct device *dev);休眠函数,休眠之前会被调⽤static int gpio_keys_resume(struct device *dev);唤醒函数,唤醒之前被调⽤static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);SIMPLE_DEV_PM_OPS宏位于pm.h,它将会定义⼀个dev_pm_ops结构体,⽤来被pm⼦系统调⽤,实现休眠唤醒4.2 ⾸先来看probe函数如下图所⽰,probe函数为gpio_keys_probe()gpio_keys_probe()函数定义如下所⽰:static int gpio_keys_probe(struct platform_device *pdev){struct device *dev = &pdev->dev; //获取平台设备的.devconst struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); //获取my_button.c⽂件的board_button_data成员struct gpio_keys_drvdata *ddata; //按键驱动数据const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); //获取平台总线设备数据if (!pdata) {pdata = gpio_keys_get_devtree_pdata(dev);if (IS_ERR(pdata))return PTR_ERR(pdata);}ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +pdata->nbuttons * sizeof(struct gpio_button_data),GFP_KERNEL); //给平台设备数据分配空间input = input_allocate_device(); //分配input 按键⼦系统if (!ddata || !input) {dev_err(dev, "failed to allocate state\n");error = -ENOMEM;goto fail1;}ddata->pdata = pdata;ddata->input = input;mutex_init(&ddata->disable_lock);platform_set_drvdata(pdev, ddata);//将ddata保存到平台总线设备的私有数据。
linux的睡眠

这篇报告旨在弄清楚在x86平台下,睡眠的过程中,设备的行为是什么,特别时pcie设备,以及与ACPI有什么关系。
Linux内核提供了三种Suspend: Freeze、Standby和STR(Suspend to RAM),在用户空间向”/sys/power/state”文件分别写入”freeze”、”standby”和”mem”,即可触发它们。
这个报告涉及到的主要是STR(Suspend to RAM)。
在用户空间执行如下操作:echo "mem"> /sys/power/state然后内核中会进入在kernel/power/suspend.c定义的pm_suspend,处理所有的pci_pm_suspend,drivers/pci/pci-driver.c,这部分代码会根据具体的pcieradeon_suspend_kms,drivers/gpu/drm/radeon/radeon_device.c其中amd显卡radeon驱动中,涉及到的电源管理相关的代码,主要在drivers/gpu/drm/radeon/radeon_pm.c,然后acpi相关的在drivers/gpu/drm/radeon/radeon_acpi.c中。
然后我在这两个文件中的所有函数都加了打印后,查看这些函数的调用情况,发现在睡眠动作的时候,adeon_acpi.c中的函数都不会被执行。
在内核的初始化的时候,在显卡初始化过程中,会调用drivers/gpu/drm/radeon/radeon_acpi.c中的radeon_acpi_init函数。
其中pci_set_power_state这个函数涉及到的是PCI的电源管理。
那在睡眠过程中pci电源管理和acpi的关系是什么呢,设备是否会借助pci来使用acpi呢?pci中acpi相关代码在drivers/pci/pci-acpi.c中。
在drivers/pci/pci-acpi.c中所有函数加入标记后,在睡眠过程中发现,pci设备就返回0,我所实验的显卡时返回0,表示不支持。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
标准linux休眠和唤醒机制分析说明:1. Based on linux2.6.32, only for mem(SDR)2. 有兴趣请先参考阅读:电源管理方案APM和ACPI比较.docLinux系统的休眠与唤醒简介.doc3. 本文先研究标准linux的休眠与唤醒,android对这部分的增改在另一篇文章中讨论4. 基于手上的一个项目来讨论,这里只讨论共性的地方虽然linux支持三种省电模式:standby、suspend to ram、suspend to disk,但是在使用电池供电的手持设备上,几乎所有的方案都只支持STR模式(也有同时支持standby模式的),因为STD模式需要有交换分区的支持,但是像手机类的嵌入式设备,他们普遍使用nand 来存储数据和代码,而且其上使用的文件系统yaffs一般都没有划分交换分区,所以手机类设备上的linux都没有支持STD省电模式。
一、项目power相关的配置目前我手上的项目的linux电源管理方案配置如下,.config文件的截图,当然也可以通过make menuconfig使用图形化来配置:## CPU Power Management## CONFIG_CPU_IDLE is not set## Power management options#CONFIG_PM=y# CONFIG_PM_DEBUG is not setCONFIG_PM_SLEEP=yCONFIG_SUSPEND=yCONFIG_SUSPEND_FREEZER=yCONFIG_HAS_WAKELOCK=yCONFIG_HAS_EARLYSUSPEND=yCONFIG_WAKELOCK=yCONFIG_WAKELOCK_STAT=yCONFIG_USER_WAKELOCK=yCONFIG_EARLYSUSPEND=y# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set# CONFIG_CONSOLE_EARLYSUSPEND is not setCONFIG_FB_EARLYSUSPEND=y# CONFIG_APM_EMULATION is not set# CONFIG_PM_RUNTIME is not setCONFIG_ARCH_SUSPEND_POSSIBLE=yCONFIG_NET=y上面的配置对应下图中的下半部分图形化配置。
,看来是直接在Kconfig文件中删除了配置STD模式的选项。
使用上面的配置编译出来的系统,跑起来之后,进入sys目录可以看到相关的接口:# pwd/sys/power# lsstate wake_lock wake_unlock wait_for_fb_sleep wait_for_fb_wake# cat statemem如果配置了宏CONFIG_PM_DEBUG,那么在power目录下会多出一个pm_test文件,cat pm_test后,列出的测试选项有:[none] core processors platform devices freezer。
关于这个test模式的使用,可以参考kernel文档:/kernel/documentation/power/Basic-pm-debugging.txt这个文档我也有详细的阅读和分析。
二、sys/power和相关属性文件创建系统bootup时候在sys下新建power和相关属性文件,相关源码位置:kernel/kernel/power/main.cstatic int __init pm_init(void){int error = pm_start_workqueue();// CONFIG_PM_RUNTIME not set, so this fun is nullif (error)return error;power_kobj = kobject_create_and_add("power", NULL);// 建立power对应的kobject和sysfs_dirent对象,同时建立联系:kobject.sd =// &sysfs_dirent,sysfs_dirent.s_dir->kobj = &kobject。
if (!power_kobj)return -ENOMEM;return sysfs_create_group(power_kobj, &attr_group);// 建立一组属性文件,可以在power下建立一个子目录来存放这些属性文件,// 不过需要在结构体attr_group中指定name,否则直接将这些属性文件放在// power_kobj对应的目录下。
}core_initcall(pm_init); // 看的出来,该函数是很早就被调用,initcall等级为1static struct attribute_group attr_group = {.attrs = g,};struct attribute_group {const char *name;mode_t (*is_visible)(struct kobject *,struct attribute *, int);struct attribute **attrs;};// 属性文件都是以最基本得属性结构struct attribute来建立的static struct attribute * g[] = {&state_attr.attr,#ifdef CONFIG_PM_TRACE // not set&pm_trace_attr.attr,#endif#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG) // not set&pm_test_attr.attr,#endif#ifdef CONFIG_USER_WAKELOCK // set &wake_lock_attr.attr,&wake_unlock_attr.attr,#endifNULL,};#ifdef CONFIG_PM_SLEEP#ifdef CONFIG_PM_DEBUGpower_attr(pm_test);#endif#endifpower_attr(state);#ifdef CONFIG_PM_TRACEpower_attr(pm_trace);#endif#ifdef CONFIG_USER_WAKELOCK power_attr(wake_lock);power_attr(wake_unlock);#endif#define power_attr(_name) \static struct kobj_attribute _name##_attr = { \.attr = { \.name = __stringify(_name), \.mode = 0644, \}, \.show = _name##_show, \.store = _name##_store, \}// 而这些被封装过的属性结构体,将来会使用kobject的ktype.sysfs_ops->show(store)这两个通用函数通过container_of()宏找到实际的属性结构体中的show和store函数来调用。
关于更多sysfs的内容,请查看其他关于这部分内容的详细解析文档。
三、pm_test属性文件读写int pm_test_level = TEST_NONE;static const char * const pm_tests[__TEST_AFTER_LAST] = {[TEST_NONE] = "none",[TEST_CORE] = "core",[TEST_CPUS] = "processors",[TEST_PLATFORM] = "platform",[TEST_DEVICES] = "devices",[TEST_FREEZER] = "freezer",// core >> processors >> platform >> devices >> freezer,控制范围示意cat pm_test的时候最终会调用函数pm_test_show(),在终端上打印出上面数组中的字符串,当前的模式用[]表示出来。
echo devices > pm_test的时候会最终调用到函数pm_test_store()中去,该函数中设置全局变量pm_test_level的值,可以是0-5,分别代表上none ~ freezer。
该全局变量会在后面的suspend和resume中被引用到。
memchr函数说明:原型:extern void *memchr(void *buf, char ch, unsigned int count);用法:#include <string.h>功能:从buf所指内存区域的前count个字节查找字符ch。
说明:当第一次遇到字符ch时停止查找。
如果成功,返回指向字符ch的指针;否则返回NULL。
四、state属性文件power_attr(state)宏定义了一个struct kobj_attribute结构体state_attr:static struct kobj_attribute state_attr = {.attr = {.name = __stringify(state),.mode = 0644,},.show = state_show,.store = state_store,kobj_attribute结构体封装了struct attribute结构体,新建属性文件是依据struct attribute结构体。
最终通过函数kobj_attr_show和kobj_attr_store回调到实际的show和store函数(kobject.c)。
state_show()函数主要是显示当前系统支持哪几种省电模式。
static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){char *s = buf;#ifdef CONFIG_SUSPEND //defint i;for (i = 0; i < PM_SUSPEND_MAX; i++) {if (pm_states[i] && valid_state(i))s += sprintf(s,"%s ", pm_states[i]);}#endif#ifdef CONFIG_HIBERNATION // undef, don't support STD modes += sprintf(s, "%s\n", "disk");#elseif (s != buf)/* convert the last space to a newline */*(s-1) = '\n';#endifreturn (s - buf);}@ kernel/include/linux/suspend.h#define PM_SUSPEND_ON ((__force suspend_state_t) 0)#define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1)#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)#define PM_SUSPEND_DISK ((__force suspend_state_t) 4)#define PM_SUSPEND_MAX ((__force suspend_state_t) 5)@ kernel/kernel/power/suspend.cconst char *const pm_states[PM_SUSPEND_MAX] = {#ifdef CONFIG_EARLYSUSPEND // android修改了标准linux的休眠唤醒机制,增加了eraly suspend和late resume机制,如果是android内核,则这个宏是需要定义的。