IBM的文档有时比较成旧,源码变了,但文档没做相应的更新,有时难免带来困惑,比如下面的这个案例:内核定时器。aix称之为trb -- Timer Request Block,其相关数据结构的原型定义的sys/timer.h:
struct trb {
struct trb *to_next; /* for the timeout() routines */
struct trb *knext; /* next timer in kernel chain */
struct trb *kprev; /* previous timer in kern. chain*/
//以上构成内核维护的定时器链表
ulong id; /* owner process, dev drvrs = -1*/
//id系该定时器的拥有者,如果是进程,则是进程ID,如果是设备驱动拥有该定时器,则该值为-1
volatile cpu_t cpunum; /* owning processor */
unsigned short flags; /* operation flags */
ulong timerid; /* timer identifier */
//定时器标识,可能值ITIMER_VIRTUAL,ITIMER_VIRT,ITIMER_PROF. 标识该定时器类型, BSD or AIX (历史!历史! unix95, unix98,32-bit and 64-bit mode....)
//一个进程一般拥有:1个alarm/real timer, 1个profiling timer, 2个virutal timers和5个time of day timers
tid_t eventlist; /* anchor for awaited event */
struct itimerstruc_t timeout; /* timer request */
void (*func)(); /* completion handler */
union parmunion { /* data for completion handler */
ulong data; /* handler unsigned data */
int sdata; /* handler signed data */
caddr_t addr; /* handler address */
} t_union;
int ipri; /* int. priority for func() */
//ipri为TCH调用时的中断优先级(TCH是在中断上下文),一般使用INTTIMER(timer interrupt priority)
void (*tof)(); /* timeout function */
//不清楚tof和TCH有啥特别不一样的地方,暂时没用到
};
在实际使用中,我们发现至少func的原型就不够完整,应该是void (*func)(void *)才比较符合call back一般的逻辑,另外就上面的这个数据结构而言, union parmunion处的注释说是:data for completion handler,可能你以为(至少是我以为)这个应该是传给call back的func里的那个void *,其实不然。我们实际使用中发现这个成员变量并不是作为定时器回调函数的参数,无论你给这个union parmunion赋予什么样的值,在时钟到期时调用那个call back函数时,总是将当前struct trb的指针传到func的void *中,所以call back实际上应该是:
void timer_callback(void *data)
{
struct trb *trbp = (struct trb *)data;
...
}
所以,在timer call back function(IBM称之为TCH--Timer Completion Handler)中,内核负责将trb对象的指针传递给它的callback function的,如果user需要传递一些其他的数据到它的callback function中,可以借助于union parmunion成员变量。
关于Timer状态的变迁:
0 . talloc一个T后, T->flags = 0. (既非T_PENDING也不是T_ACTIVE)
1. 在tstart一个T但其TCH尚未被调用前 --------------- T_PENDING | T_ACTIVE
2. 在TCH执行期间 ------------------------------------- T_ACTIVE
3. TCH执行完毕/tstop一个T之后 --------------------- T_PENDING 和T_ACTIVE都被置0(也即该timer既不是PENDING状态,也不是ACTIVE状态)
在MP环境下,调用tstop时其T所对应的TCH可能正在另一个处理器上执行,为了安全起见,需要检测tstop的返回值,IBM推荐的做法是:
while (tstop(&trp)) {
release_any_lock;
delay_some_time;
reacquire_the_lock;
} /* null while loop if locks not used */
对应Linux下的del_timer和del_timer_sync。这些函数在UP环境下效果是一样的。确保在timer所在的kernel extension被unload之前,所有该kex提交的timer必须从内核维护的定时器队列中摘除,如果当kernel extension被unload后,还有一些该kex所提交的timer处于ACTIVE状态,那么将导致系统崩溃。kernel ext开发者必须注意到这一点。
AIX内核也维护了一个定时器队列(双向链表),系统中所有进程或者是设备驱动程序提交的定时器trb对象都会被放到该链表中,当定时器到期(expire)或者被tstop时,该定时器对象将从链表中摘除。当一个定时器对象到期时,TCH在中断上下文中被调用,所以要求TCH是被pin的,以确保其所在的页面没有被swap out。在内核中发生的每个时钟中断,系统都会检测当前进程中有没有活动的ITIMER_VIRTUAL 和(或者)ITIMER_PROF类型的定时器,如果有,那么这些定时器会被更新。
阅读(3376) | 评论(2) | 转发(0) |