Chinaunix首页 | 论坛 | 博客
  • 博客访问: 56294
  • 博文数量: 47
  • 博客积分: 2095
  • 博客等级: 大尉
  • 技术积分: 560
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-01 18:42
文章分类

全部博文(47)

文章存档

2011年(1)

2008年(46)

我的朋友

分类: LINUX

2008-04-21 15:27:32

Handling wait queues

A new wait queue head may be defined by using the DECLARE_WAIT_QUEUE_HEAD(name) macro, which statically declares a new wait queue head variable called name and initializes its lock and task_list fields. The init_waitqueue_head( ) function may be used to initialize a wait queue head variable that was allocated dynamically.

一个新的等待队列头必须使用宏DECLARE_WAIT_QUEUE_HEAD(name)定义,这个宏静态地声明了一个新的等待队列头变量name并且初始化它的locktask_list成员。函数init_waitqueue_head()可以用来初始化一个动态分配的等待队列头变量。

The init_waitqueue_entry(q,p ) function initializes a wait_queue_t structure q as follows:

函数init_waitqueue_entry(q,p)如下初始化一个wait_queue_t结构p

    q->flags = 0;
    q->task = p;
    q->func = default_wake_function;

The nonexclusive process p will be awakened by default_wake_function( ), which is a simple wrapper for the try_to_wake_up( ) function discussed in Chapter 7.

非独占进程p将被default_wake_functin()唤醒,这个函数是一个简单的包装器。

Alternatively, the DEFINE_WAIT macro declares a new wait_queue_t variable and initializes it with the descriptor of the process currently executing on the CPU and the address of the autoremove_wake_function( ) wake-up function. This function invokes default_wake_function( ) to awaken the sleeping process, and then removes the wait queue element from the wait queue list. Finally, a kernel developer can define a custom awakening function by initializing the wait queue element with the init_waitqueue_func_entry( ) function.

DEFINE_WAIT声明一个新的wait_queue_t变量,并用当前在CPU上运行的进程的描述符和唤醒函数autoremove_wake_function()的地址初始化这个变量。这个唤醒函数调用default_wake_functin()去唤醒睡眠进程,然后将这个等待队列元素从等待队列中移除。最后,内核开发者可以定义一个自定义的唤醒函数,使用init_waitqueue_func_entry()函数初始化等待队列元素。

Once an element is defined, it must be inserted into a wait queue. The add_wait_queue( ) function inserts a nonexclusive process in the first position of a wait queue list. The add_wait_queue_exclusive( ) function inserts an exclusive process in the last position of a wait queue list. The remove_wait_queue( ) function removes a process from a wait queue list. The waitqueue_active( ) function checks whether a given wait queue list is empty.

一旦定义了一个元素,它就必须插入到等待队列,函数add_wait_queue()将一个非独占进程插入到一个等待队列的第一个位置。函数add_wait_queue_exclusive()将一个独占进程插入到一个等待队列的最后一个位置。函数waitqueue_active()检查是否一个给定的等待队列是空的。

A process wishing to wait for a specific condition can invoke any of the functions shown in the following list.

一个进程希望等待某一特定的条件,可以调用任意一个下列函数。

  • The sleep_on( ) function operates on the current process:

函数sleep_on()操作当前进程

void sleep_on(wait_queue_head_t *wq)
{
  wait_queue_t wait;
  init_waitqueue_entry(&wait, current);
  current->state = TASK_UNINTERRUPTIBLE;
  add_wait_queue(wq,&wait); /* wq points to the wait queue head */
  schedule( );
  remove_wait_queue(wq, &wait);
}

The function sets the state of the current process to TASK_UNINTERRUPTIBLE and inserts it into the specified wait queue. Then it invokes the scheduler, which resumes the execution of another process. When the sleeping process is awakened, the scheduler resumes execution of the sleep_on( ) function, which removes the process from the wait queue.

这个函数将当前进程的状态设置为TASK_UNINTERRUPTIBLE,并将它插入到特定的等待队列。然后它调用调度程序,恢复下一个进程的执行。当睡眠进程被唤醒时,调度程序恢复sleep_on()函数的执行,它会将进程从等待队列中移除。

  • The interruptible_sleep_on( ) function is identical to sleep_on( ), except that it sets the state of the current process to TASK_INTERRUPTIBLE instead of setting it to TASK_UNINTERRUPTIBLE, so that the process also can be woken up by receiving a signal.

函数interruptible_sleep_on()sleep_on()相同,例外的是它将当前进程的状态设置为TASK_INTERRUPTIBLE而不是TASK_UNINTERRUPTIBLE,使得这个进程在收到某个信号时也能被唤醒。

  • The sleep_on_timeout( ) and interruptible_sleep_on_timeout( ) functions are similar to the previous ones, but they also allow the caller to define a time interval after which the process will be woken up by the kernel. To do this, they invoke the schedule_timeout( ) function instead of schedule( ) (see the section "An Application of Dynamic Timers: the nanosleep( ) System Call" in Chapter 6).

函数sleep_on_timeout()interruptible_sleep_on_timeout()同前面的近似,但是它们允许调用者在进程将被内核唤醒之后定义一个时间间隔。为了达到这个目的,它们调用函数schedule_timeout()而不是schedule()

  • The prepare_to_wait( ), prepare_to_wait_exclusive( ), and finish_wait( ) functions, introduced in Linux 2.6, offer yet another way to put the current process to sleep in a wait queue. Typically, they are used as follows:

函数prepare_to_wait()prepare_to_wait_exclusive()finish_wait()是在Linux2.6中引入的,提供了另外的方法将当前的进程放入某个等待队列中睡觉:

DEFINE_WAIT(wait);
prepare_to_wait_exclusive(&wq, &wait, TASK_INTERRUPTIBLE);
/* wq is the head of the wait queue */
...
if (!condition)
  schedule();
finish_wait(&wq, &wait);

The prepare_to_wait( ) and prepare_to_wait_exclusive( ) functions set the process state to the value passed as the third parameter, then set the exclusive flag in the wait queue element respectively to 0 (nonexclusive) or 1 (exclusive), and finally insert the wait queue element wait into the list of the wait queue head wq.

函数prepare_to_wait()prepare_to_wait_exclusive()将进程状态设置为第三个参数的值,然后分别设置等待队列元素的独占标志为0(非独占)或1(独占),最后将这个等待队列元素wait插入到等待队列头wq指向的链表。

As soon as the process is awakened, it executes the finish_wait( ) function, which sets again the process state to TASK_RUNNING (just in case the awaking condition becomes true before invoking schedule( )), and removes the wait queue element from the wait queue list (unless this has already been done by the wake-up function).

一旦这个进程被唤醒,它执行函数finish_wait(),这个函数再次将进程状态设为TASK_RUNNING(仅在唤醒条件在调用schedule()前变为真),并将这个元素从等待队列中移除(除非这个已经被唤醒函数执行了)。

  • The wait_event and wait_event_interruptible macros put the calling process to sleep on a wait queue until a given condition is verified. For instance, the wait_event(wq,condition) macro essentially yields the following fragment:

wait_eventwait_event_inerruptible将调用进程放进某个等待队列睡觉,直到某个跟定的条件验证了。比如,宏wait_event(wq, condition)基本上产生下列片段:

DEFINE_WAIT(_ _wait);
for (;;) {
  prepare_to_wait(&wq, &_ _wait, TASK_UNINTERRUPTIBLE);
  if (condition)
    break;
  schedule( );
}
finish_wait(&wq, &_ _wait);

A few comments on the functions mentioned in the above list: the sleep_on( )-like functions cannot be used in the common situation where one has to test a condition and atomically put the process to sleep when the condition is not verified; therefore, because they are a well-known source of race conditions, their use is discouraged. Moreover, in order to insert an exclusive process into a wait queue, the kernel must make use of the prepare_to_wait_exclusive( ) function (or just invoke add_wait_queue_exclusive( ) directly); any other helper function inserts the process as nonexclusive. Finally, unless DEFINE_WAIT or finish_wait( ) are used, the kernel must remove the wait queue element from the list after the waiting process has been awakened.

类似sleep_on()的函数不能用于在通常情况,当条件没有验证时,不得不测试这个条件并自动地将进程睡眠;因此,因为它们是著名的竞态的来源,它们的使用就被阻止了。还有,为了将一个独占进程插入到一个等待队列,内核必须使用prepare_to_wait_exclusive()函数(或仅直接调用add_wait_queue_exclusive()函数);任何其他函数都是将进程当作非独占插入。最后,除非使用DEFINE_WAITfinish_wait(),在等待进程唤醒后,内核必须将等待元素从链表中移除。

The kernel awakens processes in the wait queues, putting them in the TASK_RUNNING state, by means of one of the following macros: wake_up, wake_up_nr, wake_up_all, wake_up_interruptible, wake_up_interruptible_nr, wake_up_interruptible_all, wake_up_interruptible_sync, and wake_up_locked. One can understand what each of these nine macros does from its name:

内核将等待队列中的进程唤醒,把他们的状态设为TASK_RUNNING,这一行动使用下列宏之一:wake_upwake_up_nrwake_up_allwake_up_interruptiblewake_up_interruptible_upwake_up_interruptible_allwake_up_interruptible_sync、和wake_up_locked。从它们的名字可以知道这九个宏的作用。

  • All macros take into consideration sleeping processes in the TASK_INTERRUPTIBLE state; if the macro name does not include the string "interruptible," sleeping processes in the TASK_UNINTERRUPTIBLE state also are considered.

所有的宏考虑睡眠进程在TASK_INTERRUPTIBLE状态;如果宏的名字没有包含字符串“interruptible”,处在TASK_UNINTERRUPTIBLE的睡眠进程也会考虑。

  • All macros wake all nonexclusive processes having the required state (see the previous bullet item).

所有宏唤醒所有拥有要求状态的非独占进程

  • The macros whose name include the string "nr" wake a given number of exclusive processes having the required state; this number is a parameter of the macro. The macros whose names include the string "all" wake all exclusive processes having the required state. Finally, the macros whose names don't include "nr" or "all" wake exactly one exclusive process that has the required state.

名字中包含“nr”的宏唤醒给定数目的拥有要求状态的独占进程;这个数目是宏的参数。名字中包含“all”的宏唤醒所有拥有要求状态的独占进程。最后,名字中既没有“nr”也没有“all”的宏只唤醒一个拥有要求状态的独占进程。

  • The macros whose names don't include the string "sync" check whether the priority of any of the woken processes is higher than that of the processes currently running in the systems and invoke schedule( ) if necessary. These checks are not made by the macro whose name includes the string "sync"; as a result, execution of a high priority process might be slightly delayed.

名字中没有包含“sync”的宏,检查是否任意一个已唤醒的进程的优先级比系统当前运行的进程高,必要时调用schedule()。而名字中没有“sync”的宏不做这样的检查;结果是,一个高优先级的进程可能被稍微延迟执行。

  • The wake_up_locked macro is similar to wake_up, except that it is called when the spin lock in wait_queue_head_t is already held.

wake_up_lockedwake_up相同,例外的是,它在wait_queue_head_t结构中的自旋锁被占用时调用。

For instance, the wake_up macro is essentially equivalent to the following code fragment:

void wake_up(wait_queue_head_t *q)
{
  struct list_head *tmp;
  wait_queue_t *curr;
  list_for_each(tmp, &q->task_list) {
    curr = list_entry(tmp, wait_queue_t, task_list);
    if (curr->func(curr, TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE,0,NULL) && curr->flags)
      break;
  }
}



The list_for_each macro scans all items in the q->task_list doubly linked list, that is, all processes in the wait queue. For each item, the list_entry macro computes the address of the corresponding wait_queue_t variable. The func field of this variable stores the address of the wake-up function, which tries to wake up the process identified by the task field of the wait queue element. If a process has been effectively awakened (the function returned 1) and if the process is exclusive (curr->flags equal to 1), the loop terminates. Because all nonexclusive processes are always at the beginning of the doubly linked list and all exclusive processes are at the end, the function always wakes the nonexclusive processes and then wakes one exclusive process, if any exists.[*]

[*] By the way, it is rather uncommon that a wait queue includes both exclusive and nonexclusive processes.

list_for_each遍历q->task_list双向链表中的所有项,也就是说,等待队列中的所有进程。对每个项,list_entry宏算出对应wait_queue_t变量的地址。这个变量的func成员存储了唤醒函数的地址,唤醒函数试图唤醒那个由等待队列元素中task成员标识的进程。如果一个进程被有效地唤醒(函数返回1)并且如果进程是独占的(curr->flags等于1),这个循环就结束了。因为所有的非独占进程总是在双向链表的开始,而所有的独占进程在末尾,这个函数总是先唤醒非独占进程,然后唤醒一个独占进程(如何存在独占进程的话)。(一个等待队列中既包括非独占进程又包括独占进程是相当不普通的)。

阅读(524) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~