在2.1节中,我们对Linux内核的wakeup source模块做了解析,在理解了其工作机制后,我们在本节将通过对相关数据结构和接口函数的设计实现一套自己的wakeup source框架,并应用到非Linux的操作系统中。
wakeup source通过每次释放锁时检查当前的持锁状态来决策是否触发睡眠流程。通过学习,我们提取出wakeup source的主要功能,来帮助我们实现自己的wakeup source框架。
1)我们需要提供一对注册和去注册接口来供需要的组件注册、删除对应的wakeup source。
2)我们还需要一对接口来提供持锁和释放锁的功能,供需要的模块在对应的业务场景中调用,每次释放锁时,触发wakeup source任务检查当前睡眠条件。
3)根据2)的要求,我们还需要创建一个任务来检查当前持锁状态,如果满足条件则触发autosleep的睡眠任务。
梳理清楚要实现的wakeup source的工作流程,接下来我们就动手实现一个简化版的wakeu p source框架并将其应用到自己的系统中。
我们要设计一个结构体来维护wakeup source相关关键信息和维测记录,当其他模块想要注册节点时,可以定义一个此类型的本地变量。相关设计实现代码段如下所示:
相关参数说明如下。
name:记录对应的wakeup source的名字。
entry:链表节点,wakeup source全局信息通过链表维护,使用链表和注册机制,也是为了支持wakeup source更灵活的可扩展性,从而不受个数限制。
stay_wake_cnt:上锁反对睡眠的次数。
relax_cnt:释放锁不反对睡眠的次数。
timer:超时锁需要使用的定时器。
timer_expires:超时锁时间。
is_active:记录当前是否为激活状态。
…:开发者可以根据平台实际需要添加需要的成员变量。
该结构体在wakeup source模块内部使用,用于记录本模块相关全局信息。相关设计实现代码段如下所示:
相关变量说明如下。
head:所有注册的wakeup source节点信息全部维护在head链表中。
lock:保护head链表的操作。
task:wakeup source模块创建的睡眠任务。
sem:触发任务运行的同步信号量。
wake_cnt:记录当前处于active状态的wakeup source的个数。
…:开发者可以根据平台实际需要添加需要的成员变量。
接下来,我们就在wakeup source模块内部文件中定义一个struct wakeup_ctrl_s类型的全局变量:
函数功能是做wakeup source本模块相关的初始化操作,比如创建任务等。可以根据不同平台的实际需要自行添加变量。
本函数无输入参数。返回0表示初始化成功,返回其他值表示失败。通常在系统初始化过程中调用执行。
相关设计实现代码段如下所示:
该函数为任务的函数体,在my_wakeup_source_init函数中创建函数时作为入参传入创建函数的接口。
函数虽然定义上有返回值,但通常函数体为一个永远执行的循环:等待触发→满足条件→运行→等待触发。
相关设计实现代码段如下所示:
仔细思考发现,其实通过创建任务来检查可能有一些冗余,如果我们在每次释放wakeup source时做一次检查,满足睡眠条件就触发autosleep任务可能会更好一些,大家可以自由选取实现机制。
该函数是本模块的对外接口,供其他模块注册wakeup source时使用。
入参为要创建的wakeup source的名字。返回值为创建好的睡眠锁指针。
相关设计实现代码段如下所示:
该函数是本模块的对外接口,其他模块在不需要睡眠锁时通过调用该函数达到去注册对应的锁的目的。
入参为要注册的睡眠锁指针。该函数没有返回值。
相关设计实现代码段如下所示:
该函数是本模块的对外接口,相关模块在合适的时机调用该函数来反对系统睡眠。
入参为要投票反对系统进入睡眠的睡眠锁指针。该函数没有返回值。
相关设计实现代码段如下所示:
该函数是本模块的对外接口,相关模块在合适的时机调用该函数来允许系统睡眠。
入参为要释放的睡眠锁指针。该函数无返回值。
相关设计实现代码段如下所示:
该函数的功能是支持低功耗主流程在睡眠时检查睡眠锁个数。
该函数没有入参。返回值为当前睡眠锁个数。
相关设计实现代码段如下所示: