A timer is a software facility that allows functions to be invoked at some future moment, after a given time interval has elapsed; a time-out denotes a moment at which the time interval associated with a timer has elapsed.
一个软件计时器能够允许在未来的某个时间点来调用函数,特别是经过给定的时间已经流逝;超时代表一个和某个计时器相关的时间已流逝。
Timers are widely used both by the kernel and by processes. Most device drivers use timers to detect anomalous conditions floppy disk drivers, for instance, use timers to switch off the device motor after the floppy has not been accessed for a while, and parallel printer drivers use them to detect erroneous printer conditions.
计时器被广泛被用kernel和进程使用。大多数设备驱动使用计时器来侦测软盘驱动器异常,例如用计时器来关闭设备马达如果驱动在一段时间内没有被使用,并行打印机驱动使用计时器来侦测打印机错误状态。
Timers are also used quite often by programmers to force the execution of specific functions at some future time (see the later section "The setitimer( ) and alarm( ) System Calls").
计时器也被程序广泛用来在未来某个时间点执行某个特定的函数(在下一节中的"The setimer()和alarm()系统调用;);
Implementing a timer is relatively easy. Each timer contains a field that indicates how far in the future the timer should expire. This field is initially calculated by adding the right number of ticks to the current value of jiffies. The field does not change. Every time the kernel checks a timer, it compares the expiration field to the value of jiffies at the current moment, and the timer expires when jiffies is greater than or equal to the stored value.
完成一个计时器相对来说很容易,每一计时器包含一个域来说明到什么时间过期。这个域以合适数量的时钟滴答加上当前的jiffies值来初始化。这个域不会变化,每次kernel检查一个计时器,它将这个说明过期的域和当前的时间比较,时间到期当这个表示jiffies值大于或者等于储存的那个值。
Linux considers two types of timers called dynamic timers and interval timers . The first type is used by the kernel, while interval timers may be created by processes in User Mode.
Linux 将动态计时器和内部计时器看作两种计时器。第一种被kernel使用,而内部计时器是创建被User 模式使用的。
One word of caution about Linux timers: since checking for timer functions is always done by deferrable functions that may be executed a long time after they have been activated, the kernel cannot ensure that timer functions will start right at their expiration times. It can only ensure that they are executed either at the proper time or after with a delay of up to a few hundreds of milliseconds. For this reason, timers are not appropriate for real-time applications in which expiration times must be strictly enforced.
谨慎linux计时器:因为检测计时器函数是被一些可延迟的函数完成的,这些函数可能在他们被启动后运行了很长一段时间,kernel无法在他们的过期时间恰好保证执行计时器函数。唯一可以保证的是这些计时器函数可以在恰好的过期时间或者往后几百微妙的时间内执行。正因为如此,计时器计时器对于实时性的这些到达过期时间被强迫执行的程序不合适。
Besides software timers , the kernel also makes use of delay functions , which execute a tight instruction loop until a given time interval elapses. We will discuss them in the later section "Delay Functions."
出了软件计时器,kernel也用一些延时函数,这些函数执行紧凑的循环指令直到给定的时间间隔被用完。我们将会在下节讨论“延时函数”。
6.5.1. Dynamic Timers 动态计时器
Dynamic timers may be dynamically created and destroyed. No limit is placed on the number of currently active dynamic timers.
动态计时器可以被动态的创建和销毁。当前动态有效的动态计时器的数目没有限制。
A dynamic timer is stored in the following timer_list structure:
struct timer_list {
struct list_head entry;
unsigned long expires;
spinlock_t lock;
unsigned long magic;
void (*function)(unsigned long);
unsigned long data;
tvec_base_t *base;
};
一个动态的计时器被存储在接下来的timer_list 结构体:
struct timer_list{
struct list_head entry; /*这个结构体将timer_list链入链表*/
unsigned long expires; /*到期时间*/
spinlock_t lock;
unsigned long magic;
. . . . .
}
The function field contains the address of the function to be executed when the timer expires. The data field specifies a parameter to be passed to this timer function. Thanks to the data field, it is possible to define a single general-purpose function that handles the time-outs of several device drivers; the data field could store the device ID or other meaningful data that could be used by the function to differentiate the device.
这个函数域包含了将要执行函数的地址当计时器到期。data域描述了给timer function的参数。由于data 域,使定义一个为通用的控制几个设备驱动器为目的的函数成为可能;这个data域可以存储设备ID,或者其他有意义的数据,比如可以被函数用来区分设备。
The expires field specifies when the timer expires; the time is expressed as the number of ticks that have elapsed since the system started up. All timers that have an expires value smaller than or equal to the value of jiffies are considered to be expired or decayed.
这个expires 域描述什么时候计时器到期:这个时间用自从系统启动来的滴答数来描述。所有计时器所含有到期时间值的如果小于等于jiffies就被认为是过期。
The entry field is used to insert the software timer into one of the doubly linked circular lists that group together the timers according to the value of their expires field. The algorithm that uses these lists is described later in this chapter.
这个entry 域用来将软件计时器插入双向循环链表中,这个双向链表根据这些过期域将他们组合起来。用在这个链表的算法在后面介绍。
To create and activate a dynamic timer, the kernel must: 为了创建和激活一个动态计算时,kernel 要做:
一、Create, if necessary, a new timer_list object for example, t. This can be done in several ways by: 如果有必要,例如建立一个新的time_list对象,t.这个可以用几种方法完成:
1way、Defining a static global variable in the code. 定义一个静态的全局变量;
2way、Defining a local variable inside a function; in this case, the object is stored on the Kernel Mode stack.
在一个函数中定义一个局部变量;这种情况下,对象被存储到kernel Mode 栈上。
3way、Including the object in a dynamically allocated descriptor. 在动态申请的描述符中包含对象。
二、
Initialize the object by invoking the init_timer(&t) function. This essentially sets the t.base pointer field to NULL and sets the t.lock spin lock to "open."
调用init_timer(&t)函数来初始化对象。本质上就是将t.base执行域设置为NULL,将t.lock spinlock打开。
三、Load the function field with the address of the function to be activated when the timer decays. If required, load the data field with a parameter value to be passed to the function.
用函数地址装载函数域,当计时器到期时此函数将会被激活。如果需要的话,用一个参数值装载data域传递给funcition()
四、 If the dynamic timer is not already inserted in a list, assign a proper value to the expires field and invoke the add_timer(&t) function to insert the t element in the proper list.
如果动态计时器还没有插入链表,分配一个合适的值个过期域。然后调用add_timer(&t)函数将其t 元素插入到合适的队列中。
五、Otherwise, if the dynamic timer is already inserted in a list, update the expires field by invoking the mod_timer( ) function, which also takes care of moving the object into the proper list (discussed next).
否则,如果动态计时器本来已经在链表中,调用mod_timer()function更新过期值,这样仍然主义将对象转移到合适的队列。(下面讨论);
Once the timer has decayed, the kernel automatically removes the t element from its list. Sometimes, however, a process should explicitly remove a timer from its list using the del_timer( ), del_timer_sync( ), or del_singleshot_timer_sync( ) functions. Indeed, a sleeping process may be woken up before the time-out is over; in this case, the process may choose to destroy the timer. Invoking del_timer( ) on a timer already removed from a list does no harm, so removing the timer within the timer function is considered a good practice.
一旦计时器过期,kernel动态将t 元素移出队列。但是有时候,一个进程应该明确地调用del_timer(),del_timer_sync(),del_sigleshot_timer_sync()函数将计时器从对联中移出。事实上,一个睡眠的进程可能在计时器到期之前被唤醒;这种情况,进程或许会选择销毁计时器。调用del_timer()移出本来就已经移出的计时器没有害。所以用计时器函数移出计时器是被考虑很好的做法。
In Linux 2.6, a dynamic timer is bound to the CPU that activated it that is, the timer function will always run on the same CPU that first executed the add_timer( ) or later the mod_timer( ) function. The del_timer( ) and companion functions, however, can deactivate every dynamic timer, even if it is not bound to the local CPU.
在linux 2.6,动态的计时器被cpu约束,是这样,计时器函数会经常在相同的cpu上运行,这个cpu是第一次运行add_timer()或者后来的mod_timer函数的。但是del_timer和类似函数,可以在每一个动态计时器上被关闭,甚至它是不被当地cpu约束的。
6.5.1.1. Dynamic timers and race conditions 动态计时器和竞争条件
Being asynchronously activated, dynamic timers are prone to race conditions. For instance, consider a dynamic timer whose function acts on a discardable resource (e.g., a kernel module or a file data structure). Releasing the resource without stopping the timer may lead to data corruption if the timer function got activated when the resource no longer exists. Thus, a rule of thumb is to stop the timer before releasing the resource:
由于异步被激活,动态计时器易于产生竞争。比如:设想一个动态计时器,它的函数作用在一个舍弃的资源上(一个kernel 模块或者一个文件数据结构)。没有停止计时器而释放资源可能导致数据腐败,如果计时器函数在资源不存在的时候被获得激活。因此,一个经验做法是在释放资源前停止计时器。
...
del_timer(&t);
X_Release_Resources( );
...
In multiprocessor systems, however, this code is not safe because the timer function might already be running on another CPU when del_timer( ) is invoked. As a result, resources may be released while the timer function is still acting on them. To avoid this kind of race condition, the kernel offers the del_timer_sync( ) function. It removes the timer from the list, and then it checks whether the timer function is executed on another CPU; in such a case, del_timer_sync( ) waits until the timer function terminates.
在多处理器的系统中,这个代码不安全,因为计时器函数可能在另一个cpu上运行,当del_timer()被调用。结果,资源可能被释放,但是timer function 可能还在此资源上运行。为了避免此类竞争,kernel提供了del_temer_sync()function.它会将此计时器移出链表,然后检查此计时器是否在另一个cpu上运行;这样,del_timer_sync()登台计时器函数结束。
The del_timer_sync( ) function is rather complex and slow, because it has to carefully take into consideration the case in which the timer function reactivates itself. If the kernel developer knows that the timer function never reactivates the timer, she can use the simpler and faster del_singleshot_timer_sync( ) function to deactivate a timer and wait until the timer function terminates.
del_timer_sync函数很复杂而且慢,因为它必须将计时器函数将自己激活的情况考虑进去。如果kernel开发者知道timer function 从不激活此计时器,它会使用简单比较块的del_singleshot_timer_sync()去关闭一个计时器,等待计时器函数结束。
Other types of race conditions exist, of course. For instance, the right way to modify the expires field of an already activated timer consists of using mod_timer( ), rather than deleting the timer and re-creating it thereafter. In the latter approach, two kernel control paths that want to modify the expires field of the same timer may mix each other up badly. The implementation of the timer functions is made SMP-safe by means of the lock spin lock included in every timer_list object: every time the kernel must access a dynamic timer, it disables the interrupts and acquires this spin lock.
当然有其他竞争条件存在。比如:正确更新一个本来就是激活状态的expires域用mod_timer(),而不是删除一个计时器然后其后再创建一个计时器。后一种方法,两个kernel control paths 都想改正同一个计时器的expiers域可能会很混乱。SMP-safe计时器的意思是lock spinlock被每一个timer_list对象所包含:每次kernel进入一个动态计时器,它会禁止中断并且加锁。
6.5.1.2. Data structures for dynamic timers 动态计时器器的数据结构
Choosing the proper data structure to implement dynamic timers is not easy. Stringing together all timers in a single list would degrade system performance, because scanning a long list of timers at every tick is costly. On the other hand, maintaining a sorted list would not be much more efficient, because the insertion and deletion operations would also be costly.
选择合适的数据结构去完成一个动态计时器是不容易的。将所有的计时器器串联到一个链表中会使系统效率降级,因为在每一tick都去遍历一个长链表是花费很大。另一方面,维持一个排序的链表效率不高,因为插入和删除也是很费时的。
The adopted solution is based on a clever data structure that partitions the expires values into blocks of ticks and allows dynamic timers to percolate efficiently from lists with larger expires values to lists with smaller ones. Moreover, in multiprocessor systems the set of active dynamic timers is split among the various CPUs.
通过的解决方法是基于将过期时间值分成到ticks的块中,允许动态的计时器从链表中从大到小有效过滤到期时间。不仅如此,在多处理器系统动态设置计时器被分布到各种cpu上。
The main data structure for dynamic timers is a per-CPU variable (see the section "Per-CPU Variables" in Chapter 5) named tvec_bases: it includes NR_CPUS elements, one for each CPU in the system. Each element is a tvec_base_t structure, which includes all data needed to handle the dynamic timers bound to the corresponding CPU:
主要的动态计时器器的数据结构是per_cpu的数据。叫做tves_bases;它包含NR_CPUS 元素,记录系统中的每个cpu.每个在tvec_base_t结构中的element,包含所有被限制到相关cpu操作所用的数据。
typedef struct tvec_t_base_s {
spinlock_t lock;
unsigned long timer_jiffies;
struct timer_list *running_timer;
tvec_root_t tv1;
tvec_t tv2;
tvec_t tv3;
tvec_t tv4;
tvec_t tv5;
} tvec_base_t;
The tv1 field is a structure of type tvec_root_t, which includes a vec array of 256 list_head elements that is, lists of dynamic timers. It contains all dynamic timers, if any, that will decay within the next 255 ticks.
这个tvl 域是个tvec_root_t类型结构的域,此结构包含一个256list_head向量元素,这些元素是动态计时器的链表。它包含所有的将会在255ticks中到期计时器。
The tv2, tv3, and tv4 fields are structures of type tvec_t consisting of a vec array of 64 list_head elements. These lists contain all dynamic timers that will decay within the next 2^14-1, 2^20-1, and 2^26-1 ticks, respectively.
tv2,tv3,tv4域是个tvec_t 类型的结构包含64个list_head元素向量。这些链表分别包含所有的将在下个2^14 -1 , 2^20 -1, 2^26 -1到期的计时器。
The tv5 field is identical to the previous ones, except that the last entry of the vec array is a list that includes dynamic timers with extremely large expires fields. It never needs to be replenished from another array. Figure 6-1 illustrates in a schematic way the five groups of lists.
tv5域和远来的相同,除非最后一个vec 数组entry是一个包含很大的到期时间域的计时器链表。它不需要从另一个数组中补充。 图6-1解释示意图。
The timer_jiffies field represents the earliest expiration time of the dynamic timers yet to be checked: if it coincides with the value of jiffies, no backlog of deferrable functions has accumulated; if it is smaller than jiffies, then lists of dynamic timers that refer to previous ticks must be dealt with. The field is set to jiffies at system startup and is increased only by the run_timer_softirq( ) function described in the next section. Notice that the timer_jiffies field might drop a long way behind jiffies when the deferrable functions that handle dynamic timers are not executed for a long time for instance because these functions have been disabled or because a large number of interrupt handlers have been executed.
timer_jiffies域表示最早的到期时间的将要被检查的动态计时器:如果它和jiffies值冲突,没有延迟函数被累计;如果它比jiffies小,动态计时器参考前面的ticks一定会被执行。这个域被设置从系统启动以来的jiffies值,只能被run_timer_softirq()来增加(下一节描述)。注意到timer_jiffie域可能会因为处理动态计时器的延迟函数长时间没有执行(比如这些函数在很多中断被执行期间被禁止执行)而被延迟到jiffies之后。
In multiprocessor systems, the running_timer field points to the timer_list structure of the dynamic timer that is currently handled by the local CPU.
在多处理器系统,running_timer域指向当前被cpu执行的timer_list动态计时器数据结构。
6.5.1.3. Dynamic timer handling 动态计时器处理
Despite the clever data structures, handling software timers is a time-consuming activity that should not be performed by the timer interrupt handler. In Linux 2.6 this activity is carried on by a deferrable function, namely the TIMER_SOFTIRQ softirq.
除了好的数据结构,处理软件计时器也是费时的操作,这些操作不应该被计时器中断处理函数处理。在Linux 2.6这个操作被一个叫TIME_SOFTIRQ的软中断延时函数处理。
The run_timer_softirq( ) function is the deferrable function associated with the TIMER_SOFTIRQ softirq. It essentially performs the following actions:
run_timer_softirq()函数是和TIMER_SOFTIRQ软中断相关的。本质上按照接下来的步执行:
一 、Stores in the base local variable the address of the tvec_base_t data structure associated with the local CPU.
一、 将和本地相关的address of tvec_base_t数据结构装载进base 本地变量。
二、Acquires the base->lock spin lock and disables local interrupts.
二、获得base->lock spinlock 禁止本地中断。
三、 Starts a while loop, which ends when base->timer_jiffies becomes greater than the value of jiffies. In every single execution of the cycle, performs the following substeps:
三、开始while 循环,当base->timer_jiffies大于jiffies的值的时候停止。在每一次循环执行时,执行接下来的子步:
1、 Computes the index of the list in base->tv1 that holds the next timers to be handled:
index = base->timer_jiffies & 255;
1、计算base->tvl这个持有下一个将要执行的计时器的索引。
index = base->timer_jiffies & 255;
2、 If index is zero, all lists in base->tv1 have been checked, so they are empty: the function therefore percolates the dynamic timers by invoking cascade( ):
if (!index &&
(!cascade(base, &base->tv2, (base->timer_jiffies>> 8)&63)) &&
(!cascade(base, &base->tv3, (base->timer_jiffies>>14)&63)) &&
(!cascade(base, &base->tv4, (base->timer_jiffies>>20)&63)))
cascade(base, &base->tv5, (base->timer_jiffies>>26)&63);
Consider the first invocation of the cascade( ) function: it receives as arguments the address in base, the address of base->tv2, and the index of the list in base->tv2 including the timers that will decay in the next 256 ticks. This index is determined by looking at the proper bits of the base->timer_jiffies value. cascade( ) moves all dynamic timers in the base->tv2 list into the proper lists of base->tv1; then, it returns a positive value, unless all base->tv2 lists are now empty. If so, cascade( ) is invoked once more to replenish base->tv2 with the timers included in a list of base->tv3, and so on.
2、如果在检查所有的base->tvl的链表中元素后index是0,所以他们是空的;这个function将会调用cascade()function来补充。
想象在第一次调用cascade()函数,它接受一个base基地址作为参数,base->tv2的地址作为参数,这个包含将会延迟256ticks的在base->tv2的索引。这个索引以查看base->timer_jiffies值的正确位决定。cascade()经base->tv2链表中的所有动态链表移入base->tv1;然后返回一个正数,除非所有的base->tv2都是空的。如果是这样,cascade()函数被调用用base->tv3的计时器来补充base->tv2。
三。三、 Increases by one base->timer_jiffies. 将base->timer_jiffies的值加一。
三。四、For each dynamic timer in the base->tv1.vec[index] list, executes the corresponding timer function. In particular, for each timer_list element t in the list essentially performs the following steps:
三。四 对于每一个在base->tv1.vec[index]链表中的动态计时器,执行相关的计时器函数。特别的,对于在链表中每一个timer_list元素 t,本质上执行接下来的步骤:
1、 Removes t from the base->tv1's list 从base->tv1的链表中删除
2、 In multiprocessor systems, sets base->running_timer to &t. 在多处理器系统中,将base->running_timer 设置为&t.
3、 Sets t.base to NULL. 将t.base 设置为 NULL;
Releases the base->lock spin lock, and enables local interrupts. 释放base->lock spin_lock锁,开启本地中断。
5、Executes the timer function t.function passing as argument t.data. 执行计时器处理函数t.function 传递一个参数t.data.
6 、 Acquires the base->lock spin lock, and disables local interrupts. 获取一个base->lock锁,关闭中断。
7、Continues with the next timer in the list, if any. 如果可能的话,继续链表中的下一个计时器。
三。五All timers in the list have been handled. Continues with the next iteration of the outermost whilecycle.
三。五:所有在链表的计时器一定被执行,继续在最外层的whilecycle里下个迭代。
四、The outermost while cycle is terminated, which means that all decayed timers have been handled. In multiprocessor systems, sets base->running_timer to NULL.
四。最外成的while循环被终止,意味着所有的延迟寄存器已经被执行完毕。在多处理器系统,将base->running_timer设置为NULL。
五、Releases the base->lock spin lock and enables local interrupts.
释放base->lock锁,开启本地中断。
Because the values of jiffies and timer_jiffies usually coincide, the outermost while cycle is often executed only once. In general, the outermost loop is executed jiffies - base->timer_jiffies + 1 consecutive times. Moreover, if a timer interrupt occurs while run_timer_softirq( ) is being executed, dynamic timers that decay at this tick occurrence are also considered, because the jiffies variable is asynchronously increased by the global timer interrupt handler (see the earlier section "The timer interrupt handler").
因为 jiffies的值和timer_jiffies经常冲突,外部while循环通常只被执行一次。通常情况下,外层的loop被执行连续的jiffies - base->timer_jiffies + 1 jiffies。此外,如果一个计时器中断发生在run_timer_softirq()发生时,动态计时器在此tick的出现也是被考虑的。因为jiffies值是被其他的全局计时器中断处理函数异步增加的。
(看前面的计时器中断处理函数;)
Notice that run_timer_softirq( ) disables interrupts and acquires the base->lock spin lock just before entering the outermost loop; interrupts are enabled and the spin lock is released right before invoking each dynamic timer function, until its termination. This ensures that the dynamic timer data structures are not corrupted by interleaved kernel control paths.
注意到run_timer_softirq()禁止中断获取base->lock锁在进入外部循环之前;中断是开启的,spin lock是被释放的在调用动态计时器函数之前,直到它结束。这保证了动态计时器数据结构是不能被交错的kernel control path 贪污的。
To sum up, this rather complex algorithm ensures excellent performance. To see why, assume for the sake of simplicity that the TIMER_SOFTIRQ softirq is executed right after the corresponding timer interrupt occurs. Then, in 255 timer interrupt occurrences out of 256 (in 99.6% of the cases), the run_timer_softirq( ) function just runs the functions of the decayed timers, if any. To replenish base->tv1.vec periodically, it is sufficient 63 times out of 64 to partition one list of base->tv2 into the 256 lists of base->tv1. The base->tv2.vec array, in turn, must be replenished in 0.006 percent of the cases (that is, once every 16.4 seconds). Similarly, base->tv3.vec is replenished every 17 minutes and 28 seconds, and base->tv4.vec is replenished every 18 hours and 38 minutes. base->tv5.vec doesn't need to be replenished.
总结下,这个复杂的算法保证了很好的运行。为了看原因,假设为了简单;这个TIMER_SOFTIRQ软中断在相关计时器中断发生时正好发生。然后在255个计时器中断发生的256个run_timer_softirq()正好运行延迟的计时器函数,周期地将base->tv2补充到到base->tv1链表。周期性地,在这执行run_timer_softirq()函数时间内将63个数组中的元素补充到base->tv1,这个base->tv2.vec数组,依次,一定会在0.006%本次更新周期时间。相似的,base->tv3.vec被每隔17分钟:28秒来更新,base->tv4.vec被每隔18个小时:38分钟被更新,base->tv5.vec不被更新;
6.5.2. An Application of Dynamic Timers: the nanosleep( ) System Call 关于动态计时器的应用: nanosleep ()系统调用。
To show how the outcomes of all the previous activities are actually used in the kernel, we'll show an example of the creation and use of a process time-out.
为了展示前面所说的结果在kernel中是如何被使用的。我们来展现一个创建和使用的进程超时。
Let's consider the service routine of the nanosleep() system call, that is, sys_nanosleep(), which receives as its parameter a pointer to a timespec structure and suspends the invoking process until the specified time interval elapses. The service routine first invokes copy_from_user() to copy the values contained in the User Mode timespec structure into the local variable t. Assuming that the timespec structure defines a non-null delay, the function then executes the following code:
我们来考虑这个nanosleep()系统调用的服务流程。那是,sys_nanosleep(),它接受指向timespec数据结构的指针作为参数,然后暂停调用进程,直到特定的时间流逝。这个服务程序首先调用copy_from_user()将在User MOde下的timerspec结构体copy 到kernel local variable t.假设timerspec结构体定义一个非零的延迟,这个function 执行接下来的代码:
current->state = TASK_INTERRUPTIBLE;
/*表明进程状态*/
remaining = schedule_timeout(timespec_to_jiffies(&t)+1);
The timespec_to_jiffies( ) function converts in ticks the time interval stored in the timespec structure. To be on the safe side, sys_nanosleep( ) adds one tick to the value computed by timespec_to_jiffies( ).
timespec_to_jiffies()函数将在timespec结构体中的time interval 转化成ticks。为了安全,sys_nanosleeep()在计算的timespec_to_jiffies加上 one tick;
The kernel implements process time-outs by using dynamic timers. They appear in the schedule_timeout( ) function, which essentially executes the following statements:
The kernel 用动态计时器完成进程超时。它们出现在schedule_timeout(_函数,这本质上执行下面的状态: struct timer_list timer;
unsigned long expire = timeout + jiffies;
init_timer(&timer);
timer.expires = expire;
timer.data = (unsigned long) current;
timer.function = process_timeout;
add_timer(&timer);
schedule( ); /* process suspended until timer expires */
del_singleshot_timer_sync(&timer);
timeout = expire - jiffies;
return (timeout < 0 ? 0 : timeout);
When schedule( ) is invoked, another process is selected for execution; when the former process resumes its execution, the function removes the dynamic timer. In the last statement, the function either returns 0, if the time-out is expired, or the number of ticks left to the time-out expiration if the process was awakened for some other reason.
当 schedule()被调用,另一个进程被选择执行;当前一个进程恢复操作,这个函数将移出定时器。在最后的陈述中,函数返回为0,如果超时过期,或者是剩下的ticks的数目,如果程序因为某种原因被唤醒。
When the time-out expires, the timer's function is executed:
当超时到期,计时器函数被执行:
void process_timeout(unsigned long __data)
{
wake_up_process((task_t *)__data);
}
The process_timeout( ) receives as its parameter the process descriptor pointer stored in the data field of the timer object. As a result, the suspended process is awakened.
process_timeout()接受在timer 对象中data域进程描述符指针作为参数,结果暂停的进程被唤醒。
Once awakened, the process continues the execution of the sys_nanosleep( ) system call. If the value returned by schedule_timeout( ) specifies that the process time-out is expired (value zero), the system call terminates. Otherwise, the system call is automatically restarted, as explained in the section "Reexecution of System Calls" in Chapter 11.
一旦被唤醒,进程继续执行sys_nanosleep()系统调用。如果这个被schedule_timeout()的返回值表明这个进程的超时过期,那么就系统call 终止。否则,系统调用被自动重启,在“Reexecution of System calls"in 11章“介绍。
6.5.3. Delay Functions 超时函数
Software timers are useless when the kernel must wait for a short time intervallet's say, less than a few milliseconds. For instance, often a device driver has to wait for a predefined number of microseconds until the hardware completes some operation. Because a dynamic timer has a significant setup overhead and a rather large minimum wait time (1 millisecond), the device driver cannot conveniently use it.
软件计时器在kernel必须等到一个很短的小于几微妙的时间时是无用的。比如,经常,一个驱动要等待一个预先定义数目的微妙直到硬件完成一些操作。因为动态定时器有一个重要的启动,这个操作要需要至少1millsecond。驱动程序不能故意使用它。
In these cases, the kernel makes use of the udelay( ) and ndelay( ) functions: the former receives as its parameter a time interval in microseconds and returns after the specified delay has elapsed; the latter is similar, but the argument specifies the delay in nanoseconds.
这中情况,kernel利用udelay()和ndelay()函数:前者接受微秒作为参数,在经过特定的延时过期后返回;
后者类似:但是参数为纳秒。
Essentially, the two functions are defined as follows:
本质上,两个函数被如下定义。
void udelay(unsigned long usecs)
{
unsigned long loops;
loops = (usecs*HZ*current_cpu_data.loops_per_jiffy)/1000000;
cur_timer->delay(loops);
}
void ndelay(unsigned long nsecs)
{
unsigned long loops;
loops = (nsecs*HZ*current_cpu_data.loops_per_jiffy)/1000000000;
cur_timer->delay(loops);
}
Both functions rely on the delay method of the cur_timer timer object (see the earlier section "Data Structures of the Timekeeping Architecture"), which receives as its parameter a time interval in "loops." The exact duration of one "loop," however, depends on the timer object referred by cur_timer (see Table 6-2 earlier in this chapter):
两个函数依赖cur_timer timer对象的方法(看前面几节的计时架构的数据结构),那里接收时间间隔loop作为参数。在精确的loop时间段内,但是也依赖于cur_timer(见前面的6-2)
If cur_timer points to the timer_hpet, timer_pmtmr, and timer_tsc objects, one "loop" corresponds to one CPU cyclethat is, the time interval between two consecutive CPU clock signals (see the earlier section "Time Stamp Counter (TSC)").
如果cur_timer 指向 timer_hpet,timer_pmtmr,timer_tsc对象,一个loop相关到一个cup周期。时间间隔是两个相邻CPU时钟的信号。(见时间戳(TSC)。
If cur_timer points to the timer_none or timer_pit objects, one "loop" corresponds to the time duration of a single iteration of a tight instruction loop.
(如果cur_timer指向timer_none或者timer_pit 对象,一个loop相关到一个紧凑的loop指令。
During the initialization phase, after cur_timer has been set up by select_timer( ), the kernel executes the calibrate_delay( ) function, which determines how many "loops" fit in a tick. This value is then saved in the current_cpu_data.loops_per_jiffy variable, so that it can be used by udelay( ) and ndelay( ) to convert microseconds and nanoseconds, respectively, to "loops."
在初始化阶段,cur_timer 被select_timer()设置,kernel指向calibrate_delay(),这个会决定多少loops是一个tick。这个值被保留在current_cpu_data.loops_per_jiffy变量,所以我们可以使用udelay()和ndelay()去转化微秒和纳秒到loops.
Of course, the cur_timer->delay( ) method makes use of the HPET or TSC hardware circuitry, if available, to get an accurate measurement of time. Otherwise, if no HPET or TSC is available, the method executes loops iterations of a tight instruction loop.
当然cur_timer->delay()方法使用HPET或者TSC硬件电路,如果可能的话,去得到一个精确的时间测量。否则,没有HPET或者TSC变量,这个方法就是循环紧凑指令的迭代。
阅读(1954) | 评论(0) | 转发(0) |