标准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,表示不支持。
你知道Linux进程的睡眠和唤醒操作?
你知道Linux进程的睡眠和唤醒操作?1 Linux进程的睡眠和唤醒在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状态标志位为TASK_RUNNING。
一旦一个运行中的进程时间片用完,Linux 内核的调度器会剥夺这个进程对CPU的控制权,并且从运行队列中选择一个合适的进程投入运行。
当然,一个进程也可以主动释放CPU的控制权。
函数schedule()是一个调度函数,它可以被一个进程主动调用,从而调度其它进程占用CPU。
一旦这个主动放弃CPU的进程被重新调度占用CPU,那么它将从上次停止执行的位置开始执行,也就是说它将从调用schedule()的下一行代码处开始执行。
有时候,进程需要等待直到某个特定的事件发生,例如设备初始化完成、I/O 操作完成或定时器到时等。
在这种情况下,进程则必须从运行队列移出,加入到一个等待队列中,这个时候进程就进入了睡眠状态。
Linux 中的进程睡眠状态有两种:一种是可中断的睡眠状态,其状态标志位TASK_INTERRUPTIBLE;另一种是不可中断的睡眠状态,其状态标志位为TASK_UNINTERRUPTIBLE。
可中断的睡眠状态的进程会睡眠直到某个条件变为真,比如说产生一个硬件中断、释放进程正在等待的系统资源或是传递一个信号都可以是唤醒进程的条件。
不可中断睡眠状态与可中断睡眠状态类似,但是它有一个例外,那就是把信号传递到这种睡眠状态的进程不能改变它的状态,也就是说它不响应信号的唤醒。
不可中断睡眠状态一般较少用到,但在一些特定情况下这种状态还是很有用的,比如说:进程必须等待,不能被中断,直到某个特定的事件发生。
在现代的Linux操作系统中,进程一般都是用调用schedule()的方法进入睡眠状态的,下面的代码演示了如何让正在运行的进程进入睡眠状态。
sleeping_task = current;set_current_state(TASK_INTERRUPTIBLE);schedule();func1();/* Rest of the code ... */在第一个语句中,程序存储了一份进程结构指针sleeping_task,current 是一个宏,它指向正在执行的进程结构。
Android Linux的休眠和唤醒
Android Linux的休眠和唤醒Android下有三种省电方式:Early_suspend::主要是屏幕省电需要。
进入early_suspend后,注册了early_suspend的外设进入休眠,大部分进程都还在运行,CPU处于正常工作状态。
外设可以通过register_early_suspend注册为early_suspend设备。
LPAUDION_MODE:系统进入early_suspend模式之后,通过has_audio_wake_lock判断是否进入LPAUDION_MODE模式,进入LPAUDION_MODE后,音乐正常播放,CPU进入省电模式。
Suspend:系统运行状态保存到内存,CPU进入省电模式,所有外设电源关闭,只有内存保持供电和工作。
跟休眠唤醒相关的文件:/kernel/power/main.c/kernel/power/earlysuspend.c/kernel/power/wakelock.c/kernel/power/suspend.c/drivers/base/power/main.c/arch/arm/plat-samsung/pm.c/arch/arm/mach-s5pv210/arch/arm/plat-s5p/sleep.s/arch/arm/mach-s5pv210/cpuidle.cAndroid linux休眠流程:休眠的入口在/kernel/power/main.c的state_store函数。
State_store函数通过power_attr(state)宏,在sysfs中定义了一个属性。
用户空间可以通过向state写入MEM,就可以使系统进入休眠状态。
也可以通过cat state查看支持哪些休眠方式在state_store函数中,调用request_suspend_state函数请求休眠。
在request_suspend_state 中,如果判断到系统不处于休眠状态,则调用early_suspend。
标准linux休眠和唤醒机制分析
标准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上面的配置对应下图中的下半部分图形化配置。
Androidlinux的休眠和唤醒
Androidlinux的休眠和唤醒写了很多内容,突然发布文章的时候保存失败。
郁闷凭借记忆只重写如下总结:1. Early suspend/ late resume 是android 添加的机制,用户关闭LCD, TS, Sensors等为了省电。
可称之为浅度休眠2. Linux的Suspend/resume 仍然有效,可称之为深度休眠3. echo mem > sys/power/state 会触发early suspend,echo on > sys/power/state 会触发late resume,cat sys/power/state 会显示当前支持的休眠状态,有mem, on, standby选项,但针对android 则只有mem, 同时无条件支持on4. LCD, TS, Sensors等设备会注册early suspend 和late resume 回调函数,用于early suspend/late resume时逐个执行。
5. User space 通过操作sys/power/wake_lock, sys/power/wake_unlock 可以向kernel申请一个wake lock;cat sys/power/wake_lock 或wake_unlock 只是显示User Space 申请的wake lock, kernel 申请的wake lock显示不出来6. cat proc/wakelocks 可以显示所有的wake lock的信息7. Early suspend/Late resume中有一个关键wake lock是main_wake_lock, 执行early suspend后会unlock main_wake_lock;执行late resume之前会lock main_wake_lock.8. 当任何一个wake lock被unlock时,会检测是否有其他wake lock处于locked状态?如果没有则系统进入linux 常规suspend9. 一般而言android系统是否休眠,是由user space发起的,而kernel space 是不能主动进入休眠的;当android系统需要进入休眠时,user space 执行echo mem > sys/power/state;强迫kernel 进入early suspend状态,如果还没有任何wake lock被locked,则进入linux 常规suspend.10. 如果android系统没有进入深度休眠,则user space发起echo on> sys/power/state, 则系统马上执行late resume, 唤醒LCD,TS,sensor等;如果已经进入深度休眠,则另外处理11. kernel会依次同步文件系统,执行各设备注册的suspend, 执行system core 的suspend disble bus和irq等,然后进入深度休眠后会在suspend_enter@kernel/kernel/power/suspend.c中的 suspend_ops->enter(state) 系统挂起,直到有硬件的操作唤醒;一旦被唤醒则依次执行system core resume, device resume 等,最后还会执行late resume.12. User space 向kernel申请wake lock时, 写入sys/power/wake_lock 的buf格式为<wakelock名称> [延时的纳秒数];但是User space 没有这么使用,android在java层自己实现了计数wake lock和记次wake lock.13. Early suspend/late resume 针对LCD,TS等设备来说,所起的作用与suspend/resume是一样的。
Linux休眠原理与实现
Linux休眠原理与实现Linux 休眠原理与实现 (2010-03-05 17:04)1.介绍Linux 休眠提供了一种类似于Windows 的休眠方式,使用户能够通过休眠操作,保存系统当前的内存数据到硬盘,即s w a p 分区中。
当计算机重新启动后,系统重新装载保存的内存数据,包括进程数据,寄存器数值等,并恢复到关机前的状态。
由于不需要重新装载文档,应用程序也不用重新打开,因此休眠启动方式要比正常的启动过程快得多。
2.Linux 休眠原理要实现操作系统的休眠,首先要理解linux 的内存管理机制。
标准L i n u x 的分页是三级页表结构:页目录、中间页目录和页。
i 3 8 6 采用的是两级页表结构:页目录和页,不支持中间页目录。
4 G 的线性地址空间,只有一个页目录,它最多有1024 个目录项,每个目录项又含有1024 个页面项,每个页面有4 K 字节。
分页机制通过把线性地址空间中的页,重新定位到物理地址空间来进行管理,因为每个页面的整个4K 字节作为一个单位进行映射,并且每个页面都对齐4K 字节的边界,因此,线性地址的低12 位经过分页机制直接地作为物理地址的低1 2 位使用。
下图所示是x86 下线性地址映射为物理地址的过程:休眠过程可以分为两个阶段, 一是SUSPEND 阶段, 二是R E S U M E 阶段, R E S U M E 过程是S U S P E N D 的逆过程。
S U S P E N D 阶段保存进程数据到硬盘中, 并关机;RESUME 阶段, 从硬盘中读取保存的进程数据, 并恢复到关机前的原始状态。
休眠需要解决的问题中,最重要的部分是内存数据的保存和如何恢复保存的内存数据。
我们可以很容易获取内存页面数据,SUSPEND 的过程中,主要任务就是要保存这些需要保存的页面,但是,作为存储页面地址的页表也需要保存下来,因为页表仅仅是一个中间转换作用的链表, 所以,可以在S U S P E N D 的过程中, 临时建立, 然后将内存页面地址记录在页表中。
linux 睡眠函数
linux 睡眠函数
Linux睡眠函数是操作系统提供的一种机制,可以让系统进入低功耗状态,从而节省能源和延长电池续航时间。
在 Linux 系统中,有多种睡眠模式可供选择,如 Suspend、Hibernate 等。
Suspend 模式是一种较为常见的睡眠模式,它会将所有正在运行的程序挂起,并将系统进入睡眠状态。
在这种模式下,系统的内存和CPU 都会被关闭,但是 RAM 中的数据仍然会被保存。
当用户再次唤醒系统时,系统会从 RAM 中恢复数据,并重新运行之前挂起的程序。
Hibernate 模式是一种更加节能的睡眠模式,它会将 RAM 中的数据保存到硬盘上,并将系统完全关闭。
当用户再次启动系统时,系统会从硬盘上读取之前保存的数据,并恢复上次的状态。
除了 Suspend 和 Hibernate 模式,还有一些较为特殊的睡眠模式,如 Standby、Deep Sleep 等。
这些模式都有各自的特点和适用场景,用户可以根据需要灵活选择。
总的来说,睡眠功能是 Linux 系统中非常重要的一个功能,可以帮助用户节省能源,延长电池续航时间,提高系统性能和稳定性。
因此,学习和了解 Linux 睡眠函数是非常有益的。
- 1 -。
对 Linux 系统休眠的理解
原文出处: tekkamanninja今天看了一个关于等待队列头w ait_queue_head_t 一般是定义在模块或内核代码中的全局变量一般是定义在模块或内核代码中的全局变量,而其中链接的元素 wait_queue_t 的定义被包含在了休眠宏中。
休眠和唤醒的过程如下图所示:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748/*** wait_event - 休眠,直到 condition 为真* @wq: 所休眠的等待队列* @condition: 所等待事件的一个C 表达式** 进程被置为等待状态 (TASK_UNINTERRUPTIBLE) 直到* @condition 评估为真. @condition 在每次等待队列<a href="/members/wangchao8098">@wq</a> 被唤醒时* 都被检查。
** wake_up() 必须在改变任何可能影响等待条件结果* 的变量之后被调用。
*/#define wait_event(wq, condition)do {if (condition)break;//先测试条件,看看是否真的需要休眠__wait_event(wq, condition);} while (0)#define __wait_event(wq, condition)do {DEFINE_WAIT(__wait); //定义一个插入到等待队列中的等待队列结构体,注意.private = current,(即当前进程)#define DEFINE_WAIT_FUNC(name, function)wait_queue_t name = {.private = current,.func = function,.task_list = LIST_HEAD_INIT((name).task_list),}#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function) for (;;) {prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); //将上面定义的结构体__wait 放入wq 等待队列中,并设置当前进程状态为TASK_UNINTERRUPTIBLE if (condition)break;//测试条件状态,看看是否真的需要休眠调度schedule();//开始调度,程序停于此处,直到有其他进程唤醒本进程,就从此处继续......}finish_wait(&wq, &__wait); //由于测试条件状态为假,跳出以上循环后执行休眠后的扫尾工作://设置当前进程状态为TASK_RUNNING //将上面定义的__wait 从等待队列链表中删除。
基于wakeup_source的linux内核睡眠机制
基于wakeup_source的linux内核睡眠机制基于wakeup_source的linux内核睡眠机制2014-11-24 19:38一:wakeup_source简介:linux 3.4内核PM使用了wakeup_source来保持唤醒状态,也就是keep awake。
之前android一直是基于Linux加入了wake_lock 机制来阻止系统休眠,后来Linux 3.4内核加入了wakeup_source来管理,安卓4.4跟着升级内核也就摒弃了自己的繁杂的wake_lock机制,在对上层接口并不改变,在内核wake_lock实现直接基于wakeup_source来实现的。
当然也会带来debug上的一些问题,比如以前的wake_lock自身带有强大的debug信息,那么我们在调试的时候可以自己看见dmesg中默认打印active wake lock XXX,很直观来辨别需要休眠的时候那个wake lock有问题阻止了休眠。
这个需要我们自己来完善。
个人认为改进很大,现在使用了autosleep机制,只要不存在任何active wakeup_source了,系统自动休眠,当有active wake_source自动block住,个人认为休眠更及时,非休眠时间在减少,同时不会消耗额外的资源。
使用基于queue work与进程block来管理suspend。
还有这里的wakeup_source个人觉得应该叫keepawake_source或者stayawake_souce,毕竟系统的唤醒也就是cpu的再次运行是由中断唤醒的而不是wakeup_source。
同时安卓4.4还有一个重大改变就是去除了early suspend机制改为fb event 通知机制。
那么现在就只有suspend与resume,runtime suspend与runtime resume了。
/*** struct wakeup_source - Representation of wakeup sources ** @total_time: Total time this wakeup source has been active.* @max_time: Maximum time this wakeup source has been continuously active.* @last_time: Monotonic clock when the wakeup source's was touched last time.* @prevent_sleep_time: Total time this source has been preventing autosleep.* @event_count: Number of signaled wakeup events.* @active_count: Number of times the wakeup sorce was activated.* @relax_count: Number of times the wakeup sorce was deactivated.* @expire_count: Number of times the wakeup source's timeout has expired.* @wakeup_count: Number of times the wakeup source might abort suspend.* @active: Status of the wakeup source.* @has_timeout: The wakeup source has been activated with a timeout.*/struct wakeup_source {const char *name;struct list_head entry;struct list_head list;spinlock_t lock;struct timer_list timer;unsigned long timer_expires; //超时时间,也就是wake_lock_timeout()里面的时间参数,超时后会执行deactivate函数ktime_t total_time;ktime_t max_time;ktime_t last_time;ktime_t start_prevent_time;ktime_t prevent_sleep_time;unsigned long event_count; //event计数unsigned long active_count;//active计数unsigned long relax_count;unsigned long expire_count;unsigned long wakeup_count;bool active:1; //用于判断是否是active状态bool autosleep_enabled:1;//这个变量是来标记active等时间的};//active任何wakeup_source都会执行该函数,标记active为true/*** wakup_source_activate - Mark given wakeup source as active.* @ws: Wakeup source to handle.** Update the @ws' statistics and, if @ws has just been activated, notify the PM* core of the event by incrementing the counter of of wakeup events being* processed.*/static void wakeup_source_activate(struct wakeup_source *ws){unsigned int cec;ws->active = true;ws->active_count++;ws->last_time = ktime_get();if (ws->autosleep_enabled)ws->start_prevent_time = ws->last_time;/* Increment the counter of events in progress. */cec = atomic_inc_return(&combined_event_count);trace_wakeup_source_activate(ws->name, cec);}//deactivate任何wakeup_source都会执行该函数,标记active 为false/*** wakup_source_deactivate - Mark given wakeup source as inactive.* @ws: Wakeup source to handle.** Update the @ws' statistics and notify the PM core that the wakeup source has* become inactive by decrementing the counter of wakeup events being processed* and incrementing the counter of registered wakeup events.*/static void wakeup_source_deactivate(struct wakeup_source *ws){unsigned int cnt, inpr, cec;ktime_t duration;ktime_t now;ws->relax_count++;/** __pm_relax() may be called directly or from a timer function.* If it is called directly right after the timer function has been * started, but before the timer function calls __pm_relax(), it is* possible that __pm_stay_awake() will be called in the meantime and* will set ws->active. Then, ws->active may be cleared immediately* by the __pm_relax() called from the timer function, but in such a* case ws->relax_count will be different from ws->active_count.*/if (ws->relax_count != ws->active_count) {ws->relax_count--;return;}ws->active = false;now = ktime_get();duration = ktime_sub(now, ws->last_time);ws->total_time = ktime_add(ws->total_time, duration);if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))ws->max_time = duration;ws->last_time = now;del_timer(&ws->timer);ws->timer_expires = 0;if (ws->autosleep_enabled)update_prevent_sleep_time(ws, now);/** Increment the counter of registered wakeup events and decrement the* couter of wakeup events in progress simultaneously.*/cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count);trace_wakeup_source_deactivate(ws->name, cec);split_counters(&cnt, &inpr);if (!inpr && waitqueue_active(&wakeup_count_wait_queue)){ wake_up(&wakeup_count_wait_queue); //当不存在任何active wake_up source的时候唤醒try_to_suspend进程。
Linux(Ubuntu)休眠,挂起,待机,关机的命令详细介绍
Linux(Ubuntu)休眠,挂起,待机,关机的命令详细介绍
这里对linux 的几个命令整理下,有:休眠,挂起,待机,关机等几个命令的区别及如何实现。
休眠是一种更加省电的模式,它将内存中的数据保存于硬盘中,所有设备都停止工作。
当再次使用时需按开关机键,机器将会恢复到您的执行休眠时的状态,而不用再次执行启动操作系统复杂的过程。
待机(挂起)是将当前处于运行状态的数据保存在内存中,机器只对内存供电,而硬盘、屏幕和CPU等部件则停止供电。
由于数据存储在速度快的内存中,因此进入等待状态和唤醒的速度比较快。
不过这些数据是保存在内存中,如果断电则会使数据丢失。
立刻关机:
sudo halt
sudo init 0
sudo shutdown -h now
sudo shutdown -h 0
定时/延时关机:
sudo shutdown -h 19:30
sudo shutdown -h +30 ##单位为分钟
重启:
sudo reboot
sudo init 6
sudo shutdown -r now
休眠:
sudo pm-hibernate
echo “disk” > /sys/power/state
sudo hibernate-disk
待机(挂起):
sudo pm-suspend
sudo pm-suspend-hybrid
echo “mem” > /sys/power/state sudo hibernate-ram。
linux 进程 休眠 原理
linux 进程休眠原理
在Linux系统中,进程的休眠是通过进程调度器和操作系统提供的休眠机制实现的。
休眠的原理如下:
1. 进程调度器:进程调度器是操作系统中一个重要的组件,负责决定将CPU资源分配给哪个进程。
当一个进程处于休眠状态时,调度器不会将CPU资源分配给它,而是将CPU资源分配给其他可运行的进程。
2. 休眠状态:当进程需要休眠时,它会主动调用系统调用,并传递一个休眠时间。
系统调用会将该进程的状态设置为休眠状态,并将其从可运行队列中移除,以便调度器可以将CPU资源分配给其他进程。
3. 挂起与唤醒:在休眠期间,操作系统会将进程的程序状态保存到内存中的进程控制块(PCB)中,并将进程的上下文切换到其他进程。
当休眠时间到达或者满足某个特定条件时,操作系统会将进程的状态从休眠状态转换为可运行状态,即唤醒进程。
总结来说,Linux系统中的进程休眠是通过进程调度器和系统调用实现的。
进程调度器负责将CPU资源分配给其他可运行的进程,而系统调用将进程的状态设置为休眠状态,并在满足特定条件时将其唤醒。
linux内核睡眠函数
linux内核睡眠函数Linux内核睡眠函数在Linux内核中,睡眠函数是一种重要的机制,用于控制系统的休眠和唤醒。
睡眠函数的主要作用是在系统空闲时将其置于睡眠状态,以节省能量并延长电池寿命。
本文将探讨Linux内核中常用的睡眠函数,并介绍其工作原理和使用方法。
一、Linux内核睡眠函数的分类Linux内核中的睡眠函数可以分为两类:延时睡眠和条件睡眠。
1. 延时睡眠:延时睡眠函数用于暂停程序的执行一段时间,可以是毫秒、秒或其他时间单位。
常用的延时睡眠函数有msleep()和usleep()。
msleep()函数以毫秒为单位进行延时,而usleep()函数以微秒为单位进行延时。
2. 条件睡眠:条件睡眠函数用于在满足特定条件之前使进程进入睡眠状态。
常用的条件睡眠函数有wait_event()和wait_event_interruptible()。
wait_event()函数会使进程进入睡眠状态,直到某个条件满足为止。
而wait_event_interruptible()函数与wait_event()函数类似,但是可以被信号中断。
二、Linux内核睡眠函数的工作原理Linux内核睡眠函数的工作原理可以简单地描述为:当调用睡眠函数时,进程会被挂起,不再参与CPU的调度,直到睡眠条件满足或指定的时间到达。
1. 延时睡眠的工作原理:延时睡眠函数通过设置进程的状态为TASK_UNINTERRUPTIBLE,并将进程的当前时间加上指定的延时时间,然后将进程插入到延时队列中。
当延时时间到达时,内核会将进程从延时队列中唤醒。
2. 条件睡眠的工作原理:条件睡眠函数通过设置进程的状态为TASK_INTERRUPTIBLE,并检查条件是否满足。
如果条件不满足,进程会被挂起,不再参与CPU的调度。
当条件满足时,内核会将进程从睡眠状态唤醒。
三、使用Linux内核睡眠函数的注意事项在使用Linux内核睡眠函数时,需要注意以下几点:1. 避免长时间的睡眠:长时间的睡眠会导致系统响应变慢,甚至出现卡死的情况。
- 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内核,则这个宏是需要定义的。