udelay实现分析(转)
o. 短延迟:比时钟节拍还短的延迟,并且要求延迟时间很精确。
void udelay(unsigned long usecs); //利用忙等待将任务延迟到指定的微秒数后运行
void mdelay(unsigned long msecs); //利用忙等待将任务延迟到指定的毫秒数后运行
o. schedule_timeout()
延迟执行的任务睡眠到指定的延迟时间耗尽后再重新运行。不能保证睡眠时间正好等于指定的延迟时间。调用之前需要把任务设置为TASK_INTERRUPTIBLE或者TASK_UNINTERRUPTIBLE状态,否则任务不会睡眠
o. 设置超时时间,在等待队列上睡眠
BogoMIPS纪录处理器在给定时间内忙循环执行的次数(该值存放在loops_per_jiffy),所以udelay函数仅仅需要根据指定时间在1秒所占的比例,就可以知道执行多少次循环能达到要求的推迟时间。
DE>/* DE>/* 计算的精度,这个值设定的越大,loops_per_jiffy的值越精确 */
/* 不知道preset_lpj是什么,查了一下,说是可以在引导程序里面设置的 */ /* 先假设了loops_per_jiffy是一个挺大的数字1<<12 */ loops_per_jiffy = (1<<12); /* 第一次循环,每次都将loops_per_jiffy扩大2倍 */ /* 下面这段是循环直到一个新的节拍的开始 */ /* 纪录新的节拍开始的tick值 */ /* __delay下面说明,是执行loops_per_jiffy次指令 */ /* 执行完了看看当前的ticks是否>0(意味着超过了1个节拍) */ /* 超过一个节拍就跳出循环 */ / *第二次循环,*/ /* 第一次循环找出的loops_per_jiffy是满足这样子的条件,*/ /* 即执行它超过一个节拍,但是loop_per_jiffy/2则不到一个节拍 */ /* 则一定在[ loops_per_jiffy/2, loops_per_jiffy ]区间内。*/ /* 这次循环是更加精确的寻找 */ /* 从loops_per_jiffy / 2开始探测 */ /* lps_precision决定了循环的次数,从而影响了结果的精度 */ /* 大于一个tick,说明loops_per_jiffy取值过大 */ /* 恢复原来的loops_per_jiffy,且loopbit会在下个循环 */ /* 里将值变小*/ /* loops_per_jiffy表示的是每个tick内执行的指令条数 */ /* 并不影响loops_per_jiffy的值 */ |
2. 下面说明一下__delay函数(arch/i386/lib/delay.c)
DE>static void delay_loop(unsigned long loops) |
主要观察delay_loop函数的汇编指令部分:把/t,/n都去掉,简化一下
L1: jmp 1f //跳转指令,1表示label 1,f表示forward,跳到L3指令处
L2: .align 16 //下一条指令对齐在16bit处,大概是这个意思吧。不管它了
L3:1: jmp 2f //跳转指令,1:表示label 1,2f表示forward到label 2,即跳到L5指令处
L4: .align 16
L5:2: decl %0 //decl表示long型的dec(减1指令)
L6: jns 2b //jns判断是否有符号数,有符号则跳转到标签2(backward 2)
%0表示参数,由这个指定:"=&a" (d0),把减数的结果放到d0里面
:"0" (loops)); 表示loops使用%0一样的寄存器
它的意思就是从1跳到2,每次减少d0(一开始d0等于loops),如果d0>0则继续跳到2,循环执行直到d0==0
3. 下面来看看udelay的实现(arch/i386/lib/delay.c)
DE>inline void __const_udelay(unsigned long xloops) DE> /* 这段汇编取出每CPU的loops_per_jiffy的值,*/DE> DE> /* 乘上HZ表示每秒执行的指令条数,最后与xloops相乘 */DE> DE> /* 乘法会溢出,将高32bit放入寄存器edx,将低32bit的值放入寄存器eax */DE> DE> /* xloops保留了高32bit的值 */ DE> /* 调用__delay函数,这里之所以要再++,我估计是弥补低32bit的精度损失 */ DE> /* 微妙换算成秒,数值太小,乘上2**32,即左移32bit */ |