1. 程式人生 > >標準linu休眠和喚醒機制分析(一)

標準linu休眠和喚醒機制分析(一)

說明:

1. Based on linux2.6.32,only for mem(SDR)

2. 有興趣請先參考閱讀:電源管理方案APMACPI比較.doc

Linux系統的休眠與喚醒簡介.doc

3. 本文先研究標準linux的休眠與喚醒,android對這部分的增改在另一篇文章中討論

4. 基於手上的一個專案來討論,這裡只討論共性的地方

雖然linux支援三種省電模式:standbysuspend to ramsuspend to disk,但是在使用電池供電的手持裝置上,幾乎所有的方案都只支援STR模式(也有同時支援standby模式的),因為STD模式需要有交換分割槽的支援,但是像手機類的嵌入式裝置,他們普遍使用

nand來儲存資料和程式碼,而且其上使用的檔案系統yaffs一般都沒有劃分交換分割槽,所以手機類裝置上的linux都沒有支援STD省電模式。

一、專案power相關的配置

目前我手上的專案的linux電源管理方案配置如下,.config檔案的截圖,當然也可以通過make menuconfig使用圖形化來配置:

.config
#

# CPU Power Management

#

# CONFIG_CPU_IDLE is not set

 

#

# Power management options

#

CONFIG_PM=y

# CONFIG_PM_DEBUG is not set
CONFIG_PM_SLEEP=y CONFIG_SUSPEND=y CONFIG_SUSPEND_FREEZER=y CONFIG_HAS_WAKELOCK=y CONFIG_HAS_EARLYSUSPEND=y CONFIG_WAKELOCK=y CONFIG_WAKELOCK_STAT=y CONFIG_USER_WAKELOCK=y CONFIG_EARLYSUSPEND=y # CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set # CONFIG_CONSOLE_EARLYSUSPEND is not set
CONFIG_FB_EARLYSUSPEND=y # CONFIG_APM_EMULATION is not set # CONFIG_PM_RUNTIME is not set CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y

上面的配置對應下圖中的下半部分圖形化配置。。。,看來是直接在Kconfig檔案中刪除了配置STD模式的選項。

 

使用上面的配置編譯出來的系統,跑起來之後,進入sys目錄可以看到相關的介面:

# pwd

/sys/power

# ls

state  wake_lock   wake_unlock   wait_for_fb_sleep   wait_for_fb_wake

# cat state

mem

如果配置了巨集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和相關屬性檔案建立

2.1

系統在bootup時候,會在sys目錄下新建power目錄和相關屬性檔案,相關原始碼位置: kernel/kernel/power/main.c

static int __init pm_init(void)

{

       int error = pm_start_workqueue();// CONFIG_PM_RUNTIME not set, so this fun is null

       if (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;
       /*建立一組屬性檔案,可以在/sys/power/目錄下建立一個子目錄來存放這些屬性檔案,不過需要在結構體attr_group中指定name,否則直接將這些屬性檔案放在power_kobj對應的目錄下。*/
       return sysfs_create_group(power_kobj, &attr_group);

}

core_initcall(pm_init);  // 看的出來,該函式是很早就被呼叫,initcall等級為1

sysfs_create_group(power_kobj, &attr_group);在/sys/power/目錄下建立一組屬性檔案。下面我們來看一下attr_group的定義

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,
#endif
       NULL,
};
static struct attribute_group attr_group = {
       .attrs = g,
};

從上我們可以看出陣列g包含元素&state_attr.attr, &wake_lock_attr.attr, &wake_unlock_attr.attr,那麼state_attr又是在哪裡定義的呢?由power_attr定義

#ifdef CONFIG_PM_SLEEP
#ifdef CONFIG_PM_DEBUG
power_attr(pm_test);
#endif
#endif

power_attr(state);

#ifdef CONFIG_PM_TRACE
power_attr(pm_trace);
#endif

#ifdef CONFIG_USER_WAKELOCK
power_attr(wake_lock);
power_attr(wake_unlock);
#endif

展開巨集power_attr

#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函式來呼叫。