1:待机节点创建

static int __init pm_init(void)
{
int error = pm_start_workqueue();
if (error)
return error;
hibernate_image_size_init();
hibernate_reserved_size_init();
power_kobj = kobject_create_and_add("power", NULL);
if (!power_kobj)
return -ENOMEM;
#ifdef CONFIG_X86_INTEL_XGOLD
power_hal_kobj = kobject_create_and_add("power_HAL_suspend",
power_kobj);
if (!power_hal_kobj)
return -ENOMEM;
#endif
error = sysfs_create_group(power_kobj, &attr_group);
if (error)
return error;
pm_print_times_init();
return pm_autosleep_init();
} core_initcall(pm_init);

pm_init使用core_initcall,顾名思义这是内核一个核心节点。pm_init完毕创建"power"节点,至于怎样创建,节点位置,想了解的看下例如以下这一小部分

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
int retval; kobj = kobject_create();
if (!kobj)
return NULL; retval = kobject_add(kobj, parent, "%s", name);
if (retval) {
printk(KERN_WARNING "%s: kobject_add error: %d\n",
__func__, retval);
kobject_put(kobj);
kobj = NULL;
}
return kobj;
}
EXPORT_SYMBOL_GPL(kobject_create_and_add);

节点是挂在kobj下。那么kobj在哪里呢?

struct kobject *kobject_create(void)
{
struct kobject *kobj; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return NULL; kobject_init(kobj, &dynamic_kobj_ktype);
return kobj;
}
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};

所以。能够知道power节点是在sysfs的ops下创建。它的位置就是 /sys/power/

1.1:power节点下子节点群创建

power作为内核待机主要节点。也能够说是父节点,非常多相关节点都在它低下创建。这就要看它的attribute_group

static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
&pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
&wakeup_count_attr.attr,
#ifdef CONFIG_PM_AUTOSLEEP
&autosleep_attr.attr,
#endif
#ifdef CONFIG_PM_WAKELOCKS
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
#endif
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP_DEBUG
&pm_print_times_attr.attr,
#endif
#endif
#ifdef CONFIG_FREEZER
&pm_freeze_timeout_attr.attr,
#endif
NULL,
};

例如以下是创建的节点群

# ls /sys/power/

autosleep

pm_async

pm_freeze_timeout

pm_print_times

pm_test

power_HAL_suspend

state

wake_lock

wake_unlock

wakeup_count

2:待机入口函数

从之前的文章分析,内核还在3.10之前。state节点是待机唯一入口。

当然内核延续性,这些接口作用都不变也能够用,例如以下命令待机:

# cat /sys/power/state                                         

freeze mem

# echo mem > sys/power/state                                  

[  168.644948] PM: suspend entry 2015-06-12 06:48:07.903751731 UTC

[  168.651206] PM: Syncing filesystems ... done.

[  168.704213] Freezing user space processes ... (elapsed 0.005 seconds) done.

[  168.717090] Freezing remaining freezable tasks ... (elapsed 0.004 seconds) done.

[  168.728986] Suspending console(s) (use no_console_suspend to debug)

从上面子节点群,我们注意到添加了一个叫autosleep的节点。没错!这是新增的接口,autosleep替代曾经google加在linux之上的earlysuspend机制,所以从这个版本号開始,不须要google的android来担心内核添加什么待机方面代码了。

2.1:autosleep入口函数

我们先看这个入口函数的store回调

static ssize_t autosleep_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
suspend_state_t state = decode_state(buf, n);
int error; if (state == PM_SUSPEND_ON
&& strcmp(buf, "off") && strcmp(buf, "off\n"))
return -EINVAL; error = pm_autosleep_set_state(state);
return error ? error : n;
}

假设autosleep节点值是off的话。autosleep功能关闭。和旧接口state不同的是,autosleep不须要关心唤醒,所以传入的state不能是on。

接下来就进入到autosleep的主要回调pm_autosleep_set_state去了。

2.2:wake lock/unlock 和 __pm_stay_awake/__pm_relax

内核3.10用的加锁解锁流程是

wake_lock_init初始化一个lock

wake_lock加锁(非超时锁。须要手动解锁)

wake_lock_timeout加超时锁。到时自己主动解锁

wake_unlock解锁

wake_lock_destroy销毁一个lock

内核3.14兼容这些接口。开发人员能够使用之前的驱动代码,也能够保持风格。由于这些接口都做了兼容,

在include/linux/wakelock.h里面都有兼容性定义;如

static inline void wake_lock_init(struct wake_lock *lock, int type,
const char *name)
{
wakeup_source_init(&lock->ws, name);
}

其它接口就不全列出来。

内核3.14使用wakelock source概念,通过锁的红黑树记录并管理系统全部的锁,这是锁机制的最大改变

static struct rb_root wakelocks_tree = RB_ROOT;

我们后面单独介绍。如今先把使用流程搞清楚。

2.3:try_to_suspend的作用

int pm_autosleep_set_state(suspend_state_t state)
{ #ifndef CONFIG_HIBERNATION
if (state >= PM_SUSPEND_MAX)
return -EINVAL;
#endif __pm_stay_awake(autosleep_ws); mutex_lock(&autosleep_lock); autosleep_state = state; __pm_relax(autosleep_ws); if (state > PM_SUSPEND_ON) {
pm_wakep_autosleep_enabled(true);
queue_up_suspend_work();
} else {
pm_wakep_autosleep_enabled(false);
} mutex_unlock(&autosleep_lock);
return 0;
}

进入到autosleep的回调后,先加上一些锁,保证状态机的切换完毕。然后激活待机工作队列运行suspend_work

try_to_suspend是autosleep最核心的部分,也是替换旧版内核使用timer结束后轮询是否还有timer来实现不停尝试待机。

开函数名字就知道,它会一直尝试待机。这就要求它可以及时响应,当系统最后一个锁被释放,它要能及时响应进入待机。

我们先看下它的函数实现

static void try_to_suspend(struct work_struct *work)
{
unsigned int initial_count, final_count; if (!pm_get_wakeup_count(&initial_count, true))
goto out;
检查wakeup count,注意所带參数true表示不仅仅是get,而是有可能会停留在里面
mutex_lock(&autosleep_lock); if (!pm_save_wakeup_count(initial_count) ||
system_state != SYSTEM_RUNNING) {
mutex_unlock(&autosleep_lock);
goto out;
}
保存当前的wakeup事件count,用于对wakeup事件count的统计。进一步是为了避免过多特别快的wakeup事件
if (autosleep_state == PM_SUSPEND_ON) {
mutex_unlock(&autosleep_lock);
return;
}
if (autosleep_state >= PM_SUSPEND_MAX)
hibernate();
else
pm_suspend(autosleep_state);
满足待机条件。进入待机如后函数pm_suspend。这个曾经是在state_store调用的
mutex_unlock(&autosleep_lock); if (!pm_get_wakeup_count(&final_count, false))
goto out;
检查wakeup count,带參数false表示仅仅是get下count。不会block在里面,确切说仅仅是更新下count table
/*
* If the wakeup occured for an unknown reason, wait to prevent the
* system from trying to suspend and waking up in a tight loop.
*/
if (final_count == initial_count)
schedule_timeout_uninterruptible(HZ / 2);
假设唤醒过快(前面提到了),就等待0.5秒
out:
queue_up_suspend_work();
激活自己,又一次開始工作队列。这也是try-的意思
}

我们认识下pm_get_wakeup_count函数

bool pm_get_wakeup_count(unsigned int *count, bool block)
{
unsigned int cnt, inpr; if (block) {
假设參数是true,就会进入这个case,有可能会block在这里面
DEFINE_WAIT(wait); for (;;) {
prepare_to_wait(&wakeup_count_wait_queue, &wait,
TASK_INTERRUPTIBLE);
prepare好wait的条件
split_counters(&cnt, &inpr);
if (inpr == 0 || signal_pending(current))
break; schedule();等待
}
finish_wait(&wakeup_count_wait_queue, &wait);结束等待,被interrupt了
} split_counters(&cnt, &inpr);
更新lock table,假设是參数false。就直接过来到这里,所以仅仅会更新table而不会block
*count = cnt;
return !inpr;
}

3:怎样在模块中加锁、解锁

1. 使用兼容的接口

wake_lock_init初始化一个lock

wake_lock加锁(非超时锁,须要手动解锁)

wake_lock_timeout加超时锁,到时自己主动解锁

wake_unlock解锁

wake_lock_destroy销毁一个lock

2. 使用新的接口

定义一个struct wakeup_source

wakeup_source_register("wakelockname");注冊这个wakeup_source

__pm_stay_awake(wakelockname);加锁(假设须要超时锁,在ws里面赋值timer_expires)

__pm_relax(wakelockname)

wakeup_source_unregister(wakelockname);撤销ws

Linux 3.14 待机流程分析的更多相关文章

  1. 【转】linux文件系统之mount流程分析

    本质上,Ext3 mount的过程实际上是inode被替代的过程. 例如,/dev/sdb块设备被mount到/mnt/alan目录.命令:mount -t ext3 /dev/sdb /mnt/al ...

  2. Linux文件系统之Mount流程分析

    转载:原文地址http://www.linuxeye.com/linuxrumen/1121.html 本质上,Ext3 mount的过程实际上是inode被替代的过程.例如,/dev/sdb块设备被 ...

  3. linux网络协议栈--路由流程分析

    转:http://blog.csdn.net/hsly_support/article/details/8797976 来吧,路由 路由是网络的核心,是linux网络协议栈的核心,我们找个入口进去看看 ...

  4. 嵌入式Linux开发之uboot启动Linux整体流程分析

    嵌入式Linux开发之uboot启动Linux整体流程分析 Uboot全称Universal Boot Loader,一个遵循GPL协议的的开源项目,其作用是引导操作系统,支持引导linux.VxWo ...

  5. 基于linux与busybox的reboot命令流程分析

    http://www.xuebuyuan.com/736763.html 基于Linux与Busybox的Reboot命令流程分析 ********************************** ...

  6. 网易视频云技术分享:linux软raid的bitmap分析

    网易视频云是网易倾力打造的一款基于云计算的分布式多媒体处理集群和专业音视频技术,提供稳定流畅.低时延.高并发的视频直播.录制.存储.转码及点播等音视频的PAAS服务,在线教育.远程医疗.娱乐秀场.在线 ...

  7. Linux信号(signal) 机制分析

    Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...

  8. Linux内核态抢占机制分析(转)

    Linux内核态抢占机制分析  http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...

  9. ubuntu为什么没有/etc/inittab文件? 深究ubuntu的启动流程分析

    Linux 内核启动 init ,init进程ID是1,是所有进程的父进程,所有进程由它控制. Ubuntu 的启动由upstart控制,自9.10后不再使用/etc/event.d目录的配置文件,改 ...

随机推荐

  1. 如何修改vos2009/vos3000的web端口?

    vos 2009. VOS 3000 2120 -2138版本在这里 /usr/apache-tomcat-5.5.15/conf 编辑 server.xml 找到 <!-- Define a ...

  2. ACM_买粽子(UVA唯一的雪花)

    买粽子 Time Limit: 2000/1000ms (Java/Others) Problem Description: 端午节快到了,小蛋准备到集市上买粽子.于是周六这天,小蛋和舍友搭着公交到了 ...

  3. B - Sleuth

    Problem description Vasya plays the sleuth with his friends. The rules of the game are as follows: t ...

  4. 如何使用SQL Developer创建数据库连接

    SQL Develope启动后,需要创建一个数据库连接,只有创建了数据库连接,才能在该数据库的方案中创建.更改对象或编辑表中的数据. 创建数据库连接的步骤如下. (1)在主界面左边窗口的“连接”选项卡 ...

  5. 【Oracle】数据迁移工具(2):Data Dump

    Data Dump 使用命令行IMPDP/EXPDP实现导入导出表.schema.表空间及数据库.IMPDP/EXPDP命令行中可以加入以下选项,来实现更细粒度的导入导出. IMPDP/EXPDP和I ...

  6. HDFS 处理命令记录

    hdfs dfs -ls hdfs dfs -mkdir hdfs dfs -put hdfs dfs -get hdfs dfs -cat hadoop 执行jar  输出的目录 必须要不存在的 y ...

  7. Sobel算子取代:基于特定点方向的canny边缘检测

    前言: Canny边缘检测使用了Sobel算子,计算dx和dy两个方向,对于特定方向的边缘检测,可以作少量修改. 代码: 计算特定方向上的边缘 void CannyOrient( cv::Mat &a ...

  8. 创建一个类Person

    创建一个类Person,包含以下属性:姓名(name).年龄(age).朋友(friends数组).问候(sayhi方法,输出问候语,例如:"你好!").交朋友(addFriend ...

  9. 读白帽子web安全笔记

    点击劫持 frame buseting if (top.location != location) { top.location = self.location } html5的sandbox属性   ...

  10. LINQ to Entities 不识别方法“System.Nullable`1[System.Int32] DiffDays(System.Nullable`1[System.DateTime], System.Nullable`1[System.DateTime])”,因此该方法无法转换为存储表达式。

    解决方案: db.table.Where(m=>System.Data.Objects.EntityFunctions.DiffDays(m.CreateTime, DateTime.Now) ...