艾巴生活网

您现在的位置是:主页>科技 >内容

科技

基于Android的Linux内核的电源管理:Early(Suspend)

2024-07-31 09:19:51科技帅气的蚂蚁
1 用户空间的接口在内核 电源 main c中,定义了一组sysfs的属性文件,其中一个定义是:功率属性(状态);把这个宏展开后:[CPP]查看纯文本sta

基于Android的Linux内核的电源管理:Early(Suspend)

1.用户空间的接口

在内核/电源/main.c中,定义了一组sysfs的属性文件,其中一个定义是:

功率属性(状态);

把这个宏展开后:

[CPP]查看纯文本

staticstructkobj _ attributestate _ attr={ \。attr={\。name='state '\。mode=0644,\

},\。显示=状态显示,\。商店=状态商店,\

}

我们再看看主网站的入口:

[CPP]查看纯文本

staticint__initpm_init(void)

{

.

power _ kobj=ko object _ create _ and _ add(' power 'NULL);

如果(!power_kobj)

return-ENOMEM;

returnsysfs _ create _ group(power _ kobj,attr _ group);

}

显然,该函数执行后,会在生成/sys/power目录,该目录下会建立一系列属性文件,其中一个就是/sys/power/state文件。用户空间向该文件的写入将会导致状态_存储被调用,读取该文件将会导致状态_显示函数被调用。

现在回到机器人的硬件抽象层(硬件抽象层的缩写)层中,查看一下代码:hardware/lib hardware _ legacy/power/power。丙:

[CPP]查看纯文本

//定义写入/sys/power/state的命令字符串

staticconstchar * off _ state=' mem '

staticconstchar * on _ state=' on

//打开/sys/power/state等属性文件,保存相应的文件描述符

staticint

打开文件描述符(常量字符*常量路径[])

{

英蒂

for(I=0;我

intfd=open(paths[i],O _ RDWR);

if(fd0){

fprintf(stderr,' fatalerroropening'%s'\n 'paths[I]);

g _ error=errno

return-1;

}

g _ FDS[I]=FD;

}

g _ error=0;

返回0

}

最终,用户空间的电源管理系统会调用设置屏幕状态函数来触发暂停的流程,该函数实际上就是往/sys/power/state文件写入记忆或"开"命令字符串。

[CPP]查看纯文本

(同国际组织)国际组织

设置屏幕状态

{

.

初始化_ FDS();

.

charbuf[32];

因特伦

如果(开)

len=snprintf(buf,sizeof(buf),' %s 'on _ state);

其他

len=snprintf(buf,sizeof(buf),' %s 'off _ state);

buf[sizeof(buf)-1]=' \ 0 '

len=write(g _ FDS[请求状态],buf,len);

.

返回0

}

/********************************************************************************************/声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!/********************************************************************************************/

2.内核中数据结构和接口

与早期暂停相关的数据结构和接口都在早期暂停中进行了定义。

-早期_暂停结构

[CPP]查看纯文本

structearly_suspend{

#ifdefCONFIG_HAS_EARLYSUSPEND

结构列表_标题链接

intlevel

void(* suspend)(struct early _ suspend * h);

void(* resume)(struct early _ suspend * h);

#endif

};

希望执行提前暂停的设备,他的设备驱动程序需要向电源管理系统注册,该结构体用于向电源管理系统注册早暂停/晚暂停,当电源管理系统启动暂停流程时,回调函数暂停会被调用,相反,简历的最后阶段,回调函数简历会被调用,级别字段用于调整该结构体在注册链表中的位置,暂停时,级别的数值越小,回调函数的被调用的时间越早,简历时则反过来安卓。预先定义了3个水平等级:

[CPP]查看纯文本

枚举{

早期_暂停_级别_空白_屏幕=50

早期_暂停_级别_停止_绘图=100

早期挂起级别禁用FB=150

};

[CPP]查看纯文本

如果你想你的设备在运货单(运费清单)设备被禁止之前执行他的早期暂停回调,设备驱动程序应该把水平值设定为小于150的某个数值,然后向系统注册早期_暂停结构。注册和反注册函数是:

void register _ early _ suspend(struct early _ suspend * handler);

void unregister _ early _ suspend(struct early _ suspend * handler);

-早期挂起处理程序链表

所有注册到系统中的早期_暂停结构都会按水平值按顺序加入到全局链表早期挂起处理程序中。

3.工作流程

首先,我们从内核/电源/唤醒锁中的初始化函数开始:

[CPP]查看纯文本

static int _ _ initwakelocks _ init(void)

{

因特雷

英蒂

.

for(I=0;iINIT _ LIST _ HEAD(active _ wake _ locks[I]);

.

wake_lock_init(main_wake_lock,WAKE_LOCK_SUSPEND,' main ');

wake _ lock(main _ wake _ lock);

wake_lock_init(unknown_wakeup,WAKE_LOCK_SUSPEND,' unknown _ wakeups ');

.

ret=平台_设备_寄存器(电源_设备);

ret=平台_驱动_寄存器(电源_驱动);

.

suspend _ work _ queue=create _ single thread _ work queue(' suspend ');

.

返回0

}

可以看到,显示初始化活动_唤醒_锁定链表数组,然后初始化并且锁住main_wake_lock,注册平台设备电源设备,这些数组、锁和电源设备我们在后续文章再讨论,这里我们关注的最后一个动作:创建了一个工作队列线程挂起工作队列,该工作队列是早期暂停的核心所在。

系统启动完成后,相关的驱动程序通过寄存器_早期_暂停()函数注册了提前暂停特性,等待一段时间后,如果没有用户活动(例如按键、触控等操作),用户空间的电源管理服务最终会调用第一节提到的set_screen_state()函数,透过sysfs,进而会调用到内核中的状态存储():

[CPP]查看纯文本

静态ssize _ tstate _ store(struct object * kobj,structkobj_attribute*attr,

constchar*buf,size_tn)

{

#ifdefCONFIG_SUSPEND

#ifdefCONFIG_EARLYSUSPEND

SUSPEND _ state _ tstate=PM _ SUSPEND _ ON;

#否则

挂起状态状态=PM挂起状态待机;

#endif

常数char *常数* s;

#endif

char * p;

因特伦

interror=-EINVAL;

p=memchr(buf,' \n 'n);

len=p?p-buf:n;

if(len==4!strncmp(buf,' disk 'len)){

error=hibernate();

gotoExit

}

#ifdefCONFIG_SUSPEND

for(s=pm _ States[state];stateif(*slen==strlen(*s)!strncmp(buf,*s,len))

打破;

}

if(state # ifdefCONFIG _ EARLYSUSPEND

if(state==PM _ SUSPEND _ ON | | valid _ state(state)){

误差=0;

请求_挂起_状态(州);

}

#否则

error=enter_state(状态);

#endif

#endif

退出:

returnerror?错误:n;

}

看到了没,前一篇文章说过,挂起到磁盘做了特殊处理,这里直接比较传入的字符串,而不是使用后续的pm_states数组,这里我不关心挂起到磁盘,所以略过冬眠的分析。

紧接着,通过pm_states数组,根据命令字符串查询得到请求的状态,默认情况下,安卓的内核都会配置了配置_早期挂起所以会调用请求挂起状态()函数,不过在调用该函数之前会先有效状态()一下,这给了平台相关的代码一个机会确认该平台是否支持所请求的电源状态有效状态()的具体实现请参考内核代码树。

[CPP]查看纯文本

无效请求挂起状态(挂起状态新状态)

{

unsignedlongirqflags

进入睡眠状态

spin_lock_irqsave(state_lock,IRQ标志);

old _ sleep=stateSUSPEND _ REQUESTED;

.

如果(!旧_睡眠新_状态!=PM_SUSPEND_ON){

状态|=挂起_请求;

if(queue _ work(suspend _ work _ queue,early_suspend_work))

pr _ info(' early _ suspend _ workisinqueuealready \ n ');

} else if(old _ sleep new _ state==PM _ SUSPEND _ ON){

状态=~挂起请求

wake _ lock(main _ wake _ lock);

如果(!queue_work(挂起_工作_队列,延迟_恢复_工作))

pr _ info(' late _ resume _ workisinqueuealready \ n ');

}

请求的挂起状态=新状态;

spin _ unlock _ irqrestore(state _ lock,IRQ标志);

}

还记得前面初始化时建立的工作队列挂起队列吗?根据之前的电源状态和请求的状态,请求挂起状态()只是简单地向挂起工作队列中加入提前暂停工作或者是迟到_恢复_工作并调度他们执行早_暂停_工作的工作函数是early_suspend()。

静态void early _ suspend(struct work _ struct * work)

{

struct early _ suspend * pos

无符号长irqflags

int abort=0;

互斥锁(early _ suspend _ lock);

spin_lock_irqsave(state_lock,IRQ标志);

如果(状态==挂起请求)

状态|=暂停;

其他

abort=1;

spin _ unlock _ irqrestore(state _ lock,IRQ标志);

如果(中止){

.

}

.

list_for_each_entry(pos,early_suspend_handlers,link) {

if (pos-suspend!=NULL) {

如果(调试掩码调试挂起)

printk(KERN _ DEBUG ' pos-suspend:% pF begin \ n 'pos-suspend);

位置-暂停(pos);

如果(调试掩码调试挂起)

printk(KERN _ DEBUG ' pos-suspend:% pF finish \ n 'pos-suspend);

}

}

互斥_解锁(early _ suspend _ lock);

if(调试掩码调试挂起)

pr _ info(' early _ suspend:sync \ n ');

sys _ sync();

中止:

spin_lock_irqsave(state_lock,IRQ flags);

if(state==SUSPEND _ REQUESTED _ AND _ SUSPEND)

wake _ unlock(main _ wake _ lock);

spin _ unlock _ irqrestore(state _ lock,IRQ flags);

}

最后,early_suspend()遍历early_suspend_handlers列表,取出每个驱动注册的early_suspend结构,然后调用它的suspend回调函数。最后,释放main_wake_lock锁,整个earlysuspend过程就完成了。下面的序列图清楚地显示了整个调用过程:

图3.1早期暂停呼叫流程

但是,此时整个系统只是处于所谓的空闲状态,cpu还在工作,后台进程也在工作。系统什么时候会真正进入睡眠状态?你注意到最后一个关键呼叫了吗:

wake _ unlock(main _ wake _ lock);