2.4.3 短延时
在内核中,小于jiffy的延时被认为是短延时。这种延时在进程或中断上下文都可能发生。由于不可能使用基于jiffy的方法实现短延时,之前讨论的睡眠等待将不再能用于短的超时。这种情况下,唯一的解决途径就是忙等待。
实现短延时的内核API包括mdelay()、udelay()和ndelay(),分别支持毫秒、微秒和纳秒级的延时。这些函数的实际实现取决于体系架构,而且也并非在所有平台上都被完整实现。
忙等待的实现方法是测量处理器执行一条指令的时间,为了延时,执行一定数量的指令。从前文可知,内核会在启动过程中进行测量并将该值存储在loops_per_jiffy变量中。短延时API就使用了loops_per_jiffy值来决定它们需要进行循环的数量。为了实现握手进程中1微秒的延时,USB主机控制器驱动程序(drivers/usb/host/ehci-hcd.c)会调用udelay(),而udelay()会内部调用loops_per_jiffy:
result = ehci_readl(ehci, ptr);
/* ... */
if (result == done) return 0;
udelay(1); /* Internally uses loops_per_jiffy */
usec--;
} while (usec > 0);
2.4.4 Pentium时间戳计数器
时间戳计数器(TSC)是Pentium兼容处理器中的一个计数器,它记录自启动以来处理器消耗的时钟周期数。由于TSC随着处理器周期速率的比例的变化而变化,因此提供了非常高的精确度。TSC通常被用于剖析和监测代码。使用rdtsc指令可测量某段代码的执行时间,其精度达到微秒级。TSC的节拍可以被转化为秒,方法是将其除以CPU时钟速率(可从内核变量cpu_khz读取)。
在如下代码片段中,low_tsc_ticks和high_tsc_ticks分别包含了TSC的低32位和高32位。低32位可能在数秒内溢出(具体时间取决于处理器速度),但是这已经用于许多代码的剖析了:
unsigned long low_tsc_ticks1, high_tsc_ticks1;
unsigned long exec_time;
rdtsc(low_tsc_ticks0, high_tsc_ticks0); /* Timestamp before */
printk("Hello World\n"); /* Code to be profiled */
rdtsc(low_tsc_ticks1, high_tsc_ticks1); /* Timestamp after */
exec_time = low_tsc_ticks1 - low_tsc_ticks0;
在1.8 GHz Pentium 处理器上,exec_time的结果为871(或半微秒)。