在应用层,使用timeout是做不到延迟微秒级别,甚至在很多架构上,连ms都做不多,为什么这样说,线索如下:
timeout = ROUND_UP(usec, 1000000/HZ);
timeout += sec * (unsigned long) HZ;
#define ROUND_UP(x,y) (((x)+(y)-1)/(y)),这里假设sec为0。
在x86,hz为100,当usec<10000,也就是小于10ms的时候,在kernel里面,会认为,是一个10ms的timeout,也就是一个hz(一个时钟中断),所以精度只能到达10ms。
通过函数schedule_timeout,把进程主动调度出去,schedule()。
在kernel里面使用的udelay是完全不一样,
udelay 通过下面的来实现:
udelay函数通过BogoMIPS实现。Bogo的意思是Bogus,MIPS的意思是每秒百万条指令(million of instructions per second)
BogoMIPS纪录处理器在给定时间内忙循环执行的次数(该值存放在loops_per_jiffy),所以udelay函数仅仅需要根据指定时间在1秒所占的比例,就可以知道执行多少次循环能达到要求的推迟时间。
inline
void
__const_udelay(
unsigned
long
xloops)
{
int
d0;
xloops *
=
4;
/* 这段汇编取出每CPU的loops_per_jiffy的值,*/
/* 乘上HZ表示每秒执行的指令条数,最后与xloops相乘 */
/* 乘法会溢出,将高32bit放入寄存器edx,将低32bit的值放入寄存器eax */
/* xloops保留了高32bit的值 */
__asm__
(
"mull %0"
:
"=d"
(
xloops)
,
"=&a"
(
d0)
:
"1"
(
xloops)
,
"0"
(
cpu_data[
raw_smp_processor_id(
)
]
.
loops_per_jiffy *
(
HZ/
4)
)
)
;
/* 调用__delay函数,这里之所以要再++,我估计是弥补低32bit的精度损失 */
__delay(
+
+
xloops)
;
}
void
__udelay(
unsigned
long
usecs)
{
/* 微妙换算成秒,数值太小,乘上2**32,即左移32bit */
__const_udelay(
usecs *
0x000010c7)
;
/* 2**32 / 1000000 (rounded up) */
}
static
void
delay_loop(
unsigned
long
loops)
{
int
d0;
__asm__
__volatile__(
"/tjmp 1f/n"
".align 16/n"
"1:/tjmp 2f/n"
".align 16/n"
"2:/tdecl %0/n/tjns 2b"
:
"=&a"
(
d0)
:
"0"
(
loops)
)
;
}
static
void
(
*
delay_fn)
(
unsigned
long
)
=
delay_loop;
void
__delay(
unsigned
long
loops)
{
delay_fn(
loops)
;
}