当前位置: 技术问答>linux和unix
Linux进程等待队列的地址?
来源: 互联网 发布时间:2016-08-03
本文导语: 系统内处于TASK_UNINTERRUPTIBLE和TASK_INTERRUPTIBLE的进程是不是都在等待队列上呢? 那么如何获得这些等待队列的地址呢? 我是个新手,希望高人解答,感激不尽! | lz表达有误,误导了后来人啊 TASK_UNINTERRUP...
系统内处于TASK_UNINTERRUPTIBLE和TASK_INTERRUPTIBLE的进程是不是都在等待队列上呢?
那么如何获得这些等待队列的地址呢?
我是个新手,希望高人解答,感激不尽!
那么如何获得这些等待队列的地址呢?
我是个新手,希望高人解答,感激不尽!
|
lz表达有误,误导了后来人啊
TASK_UNINTERRUPTIBLE和TASK_INTERRUPTIBLE对应的数据结构是运行队列,不是等待队列
一般用宏 this_rq()获得
运行队列的数据结构在2.6内核里都变化较大
早期的是 struct runqueue
最近的版本是 struct rq
lz可以参考你当前内核的做法,例如在 schedule()里肯定有这样的操作
我的2.6.25-14为
TASK_UNINTERRUPTIBLE和TASK_INTERRUPTIBLE对应的数据结构是运行队列,不是等待队列
一般用宏 this_rq()获得
运行队列的数据结构在2.6内核里都变化较大
早期的是 struct runqueue
最近的版本是 struct rq
lz可以参考你当前内核的做法,例如在 schedule()里肯定有这样的操作
我的2.6.25-14为
asmlinkage void __sched schedule(void)
{
struct task_struct *prev, *next;
unsigned long *switch_count;
struct rq *rq;
int cpu;
need_resched:
preempt_disable();
cpu = smp_processor_id();
rq = cpu_rq(cpu);
//.....
}
|
不好意思,看得太快,加上理解不正确.....
我跟了一下,发现所有的将进程加入到等待队列的函数原型都需要wait_queue_head_t *q作为参数
所有的唤醒函数(宏)也都需要wait_queue_head_t *q作为参数
就带timeout,如sleep_on_timeout,其超时唤醒是靠参数传递的task_struct来确定唤醒那个进程,其他部分与一般的等待队列无异
我的理解是等待队列不同于运行队列,运行队列一个cpu只有一个。
等待队列可以由程序员任意添加和使用,唤醒等待队列的函数需要的wait_queue_head_t *q
只能再在进程主动加入等待队列并睡眠时指定
内核无法像获得运行队列那样获得等待队列,只能靠程序员在进程陷入睡眠时指定其插入的等待队列
我跟了一下,发现所有的将进程加入到等待队列的函数原型都需要wait_queue_head_t *q作为参数
所有的唤醒函数(宏)也都需要wait_queue_head_t *q作为参数
就带timeout,如sleep_on_timeout,其超时唤醒是靠参数传递的task_struct来确定唤醒那个进程,其他部分与一般的等待队列无异
我的理解是等待队列不同于运行队列,运行队列一个cpu只有一个。
等待队列可以由程序员任意添加和使用,唤醒等待队列的函数需要的wait_queue_head_t *q
只能再在进程主动加入等待队列并睡眠时指定
内核无法像获得运行队列那样获得等待队列,只能靠程序员在进程陷入睡眠时指定其插入的等待队列
|
没错,只有TASK_RUNNING才表示运行。
我觉得可以跟踪wait_event和wake_up函数来理解“运行队列”和“等待队列”。
但需要把当前进程放入等待队列(如等待某个事件),可以用wait_event及其类似函数,同时将该进程的状态变为TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE,当等待的事件到来时用wake_up唤醒该进程,即把进程的状态设为TASK_RUNNING,让其运行。
#define __wait_event(wq, condition)
do {
DEFINE_WAIT(__wait);
for (;;) {
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);
if (condition)
break;
schedule();
}
finish_wait(&wq, &__wait);
} while (0)
关注DEFINE_WAIT(__wait);
#define DEFINE_WAIT_FUNC(name, function)
wait_queue_t name = {
.private = current,
.func = function,
.task_list = LIST_HEAD_INIT((name).task_list),
}
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
让我们看看是如何加到等待队列的呢:
答案是:通过链表
首先,在程序中需要定义wait_queue_head_t *q;
通过DEFINE_WAIT定义的wait_queue_t数据结构链到q->task_list就可以了。
在唤醒的时候,即wake_up会遍历上述链表,并调用.func函数(这里即为autoremove_wake_function),
后者所作的事情就是将 唤醒被休眠的进程了。
这就是粗略的过程。
|
进程在睡眠的时候是无法获得CPU资源的,所以唤醒自己的操作肯定不是由进程本身完成的。既然程序员在进程陷入睡眠时指定其插入的等待队列,那么如何让唤醒的人知道要唤醒哪个进程呢?(其实就是得到这个等待队列的地址吧,然后把上面所有进程都唤醒,因为不同的等待队列等待的资源不一样。)
----------------------------
我想通了,虽然不知道正确否
所以唤醒自己的操作肯定不是由进程本身完成的,是需要双方约定好的
比如读某个外设的值,当前进程会等待某个条件为真进入睡眠
在中断处理函数中会设置这个条件为真,然后唤醒等待这个条件的等待队列的进程
其等待队列的地址是事前约定好的
参见http://www.linuxforum.net/forum/showthreaded.php?Board=linuxK&Number=644445
----------------------------
我想通了,虽然不知道正确否
所以唤醒自己的操作肯定不是由进程本身完成的,是需要双方约定好的
比如读某个外设的值,当前进程会等待某个条件为真进入睡眠
在中断处理函数中会设置这个条件为真,然后唤醒等待这个条件的等待队列的进程
其等待队列的地址是事前约定好的
参见http://www.linuxforum.net/forum/showthreaded.php?Board=linuxK&Number=644445
|
对于处于TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE状态的进程将会被划分进若干的类别中,每个类别代表了一个指定的事件。
在这里,进程状态并没有提供足够的信息来迅速地访问到进程,所以有必要引入一个额外的进程链表。它们叫做等待队列,我们下面将会着重讨论。
等待队列
等待队列在内核中有很多用途,尤其用在中断处理、进程同步及定时。因为这些主题将在以后章节讨论,所以我们只在这里说明,进程必须经常等待某些事件发生,例如,等待一个磁盘操作的终止,等待释放系统资源,或等待时间经过固定的间隔。等待队列实现了在事件上的条件等待:希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制权。因此,等待队列表示一组睡眠的进程,当某一条件变为真时,由内核唤醒它们。
等待队列由双向链表实现,其元素包括指向进程描述符的指针。每个等待队列都有一个等待队列头,等待队列头是一个类型为wait_queue_head_t的数据结构:
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
因为等待队列是由中断处理程序和主要内核函数修改的,因此必须对其双向链表进行保护以免对其进行同时访问,因为同时访问会导致不可预测的后果。同步是通过等待队列头中的lock自旋锁达到的。task_list字段是等待进程链表的头。
等待进程链表的元素类型为wait_queue_t:
struct __wait_queue {
unsigned int flags;
struct task_struct * task;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
等待队列链表中的每个元素代表一个睡眠进程,该进程等待某已事件的发生;它的描述符地址存放在task字段中。task_list字段中包含的是指针,由这个指针把一个元素链接到等待相同事件的进程链表中。
然而,要唤醒等待队列中所有睡眠的进程有时并不方便。例如,如果两个或多个进程正在等待互斥访问某一要释放的资源,仅唤醒等待队列中的一个进程才有意义。这个进程占有资源,而其他进程继续睡眠。
因此,有两种睡眠进程:互斥进程(的报告的队列元素的flags字段为1)由内核有选择地唤醒,而非互斥进程(flags值为0)总是由内核在事件发生时唤醒。等待访问临界资源的进程就是互斥进程的典型例子。等待相关事件的进程是非互斥的。例如,我们考虑等待磁盘传输结束的一组进程:一旦磁盘传输完成,所有等待的进程都会被唤醒。正如我们将在下面所看到的那样,等待队列元素func字段用来表示等待队列中睡眠进程应该用什么方式唤醒。
等待队列的操作
可以用DECLARE_WAIT_QUEUE_HEAD(name)宏定义一个新等待队列的头,它静态地申明一个叫name的等待队列的头变量并对该变量的lock和task_list字段进行初始化。函数init_waitqueue_head()可以用来初始化动态分配的等待队列的头变量。
函数init_waitqueue_entry(q,p)如下所示初始化wait_queue_t结构的变量q:
q->flags = 0;
q->task = p;
q->func = default_wake_function;
非互斥进程p将由default_wake_function()函数唤醒,default_wake_function()函数是在第七章中要讨论的try_to_wake_up( )函数的一个简单的封装。
也可以选择DEFINE_WAIT宏申明一个wait_queue_t类型的新变量,并用CPU上运行的当前进程的描述符和唤醒函数autoremove_wake_function( )的地址初始化这个新变量。这个函数调用default_wake_function()函数来唤醒进程,然后从等待队列的链表中删除对应的元素(每个等待队列链表中的一个元素其实就是指向睡眠进程描述符的指针)。最后,内核开发者可以通过init_waitqueue_func_entry( )函数来自定义唤醒函数,该函数负责初始化等待队列的元素。
一旦定义了一个元素,必须把它插入等待队列。add_wait_queue( )函数把一个非互斥进程插入等待队列链表的第一个位置。add_wait_queue_exclusive( )函数把一个互斥进程插入等待队列链表的最后一个位置。remove_wait_queue( )函数从等待队列链表中删除一个进程。waitqueue_active( )函数检查一个给定的等待队列是否为空。
要等待特定条件的进程可以调用如下列表中的任何一个函数。
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);
}
该函数把当前进程的状态设置为TASK_UNINTERRUPTIBLE,并把它插入到特定的等待队列中去。然后,它调用调度程序,而调度程序重新开始另一个程序的执行。当睡眠进程被唤醒时,调度程序重新开始执行sleep_on()函数,把该进程从等待队列中删除。
interruptible_sleep_on()与sleep_on()函数是一样的,但稍有不同,前者把当前进程的状态设置为TASK_INTERRUPTIBLE而不是TASK_UNINTERRUPTIBLE,因此,接受一个信号就可以唤醒当前进程。
sleep_on_timeout() 和 interruptible_sleep_on_timeout()与前面函数类似,但它们允许调用者定义一个时间间隔,过了这个间隔以后,进程将由内核唤醒。为了做到这点,它们调用schedule_timeout()而不是schedule()函数。
在Linux 2.6中引入的prepare_to_wait( )、prepare_to_wait_exclusive( )和finish_wait( )函数提供了另外一种途径来使当前进程在一个等待队列中睡眠。它们的典型应用如下:
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);
函数prepare_to_wait( )和prepare_to_wait_exclusive( )用传递的第三个参数设置进程的状态,然后把等待队列元素的互斥标识flag分别设置为0(非互斥)或1(互斥),最后,把等待元素wait插入到以wq为头的等待队列的链表中。
进程一旦被唤醒就执行finish_wait()函数,它把进程的状态再次设置为TASK_RUNNING(仅发生在schedule()之前,唤醒条件变为真的情况下),并从等待队列中删除等待元素(除非这个工作已经由唤醒函数完成)。
wait_event和wait_event_interruptible宏使它们的调用进程在等待队列上睡眠,一直到修改了给定条件为止。例如wait_event(wq,condition)宏本质上实现下面的功能:
DEFINE_WAIT(_ _wait);
for (;;) {
prepare_to_wait(&wq, &_ _wait, TASK_UNINTERRUPTIBLE);
if (condition)
break;
schedule( );
}
finish_wait(&wq, &_ _wait);
对上面列出的函数做一些说明:sleep_on()类函数在以下条件下不能使用,那就是必须测试条件并且当前条件还没有得到验证时又紧接着让进程去睡眠;由于那些条件是众所周知的竞争条件产生的根源,所以不鼓励这样使用。此外,为了把一个互斥进程插入等待队列,内核必须使用prepare_to_wait_exclusive()函数(或者只是直接调用add_wait_queue_exclusive( ))。所有其他的相关函数把进程当做非互斥进程来插入。最后,除非使用DEFINE_WAIT或finish_wait(),否则内核必须在唤醒等待进程后从等待队列中删除对应的等待队列元素。
内核通过下面任何一个宏唤醒等待队列中的进程并把他们的状态置为TASK_RUNNING:wake_up,wake_up_nr,wake_up_all, wake_up_interruptible,wake_up_interruptible_nr,wake_up_interruptible_all, wake_up_interruptible_sync和 wake_up_locked。从每个宏的名字我们可以明白其功能:
所有宏都考虑处于TASK_INTERRUPTIBLE状态的睡眠进程;如果宏的名字中不含字符串“interruptible”,那么处于TASK_UNINTERRUPTIBLE状态的睡眠进程也被考虑。
所有宏都唤醒具有请求状态的所有互斥进程(参见上一项)。
名字中含有“nr”字符串的宏唤醒给定数的具有请求装提的互斥进程;这个数字是宏的一个参数。名字中含有“all”字符串的宏唤醒具有请求状态的所有互斥进程。最后,名字中不含“nr”或“all”字符串的宏只唤醒具有请求状态的一个互斥进程。
名字中不含有“sync”字符串的宏检查被唤醒进程的优先级是否高于系统中正在运行行进程的优先级,并在必要时调用schedule()。这些检查并不是由名字中含有“sync”字符串的宏进行的,造成的结果是高优先级进程的执行稍有延迟。
wake_up_locked宏和wake_up宏相类似,仅有的不同是当wait_queue_head_t中的自旋锁已经被持有时要调用wake_up_locked。
侧如,wake_up宏等价于下列代码片段:
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;
}
}
List_for_each宏扫描双向链表q->task_list中的所有项,即等待队列中的所有进程。对每一项,list_entry宏都计算wait_queue_t变量对应的地址。这个变量的func字段存放唤醒函数的地址,它试图唤醒由等待队列元素的task字段标识的进程。如果一个进程已经被有效地唤醒(函数返回1)并且进程是互斥的(curr->flags等于1),循环结束。因为所有的非互斥进程总是在双向链表的开始位置,而所有的互斥进程在双向链表的尾部,所以函数总是先唤醒非互斥进程然后再唤醒互斥进程,如果有进程存在的话。
在这里,进程状态并没有提供足够的信息来迅速地访问到进程,所以有必要引入一个额外的进程链表。它们叫做等待队列,我们下面将会着重讨论。
等待队列
等待队列在内核中有很多用途,尤其用在中断处理、进程同步及定时。因为这些主题将在以后章节讨论,所以我们只在这里说明,进程必须经常等待某些事件发生,例如,等待一个磁盘操作的终止,等待释放系统资源,或等待时间经过固定的间隔。等待队列实现了在事件上的条件等待:希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制权。因此,等待队列表示一组睡眠的进程,当某一条件变为真时,由内核唤醒它们。
等待队列由双向链表实现,其元素包括指向进程描述符的指针。每个等待队列都有一个等待队列头,等待队列头是一个类型为wait_queue_head_t的数据结构:
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
因为等待队列是由中断处理程序和主要内核函数修改的,因此必须对其双向链表进行保护以免对其进行同时访问,因为同时访问会导致不可预测的后果。同步是通过等待队列头中的lock自旋锁达到的。task_list字段是等待进程链表的头。
等待进程链表的元素类型为wait_queue_t:
struct __wait_queue {
unsigned int flags;
struct task_struct * task;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
等待队列链表中的每个元素代表一个睡眠进程,该进程等待某已事件的发生;它的描述符地址存放在task字段中。task_list字段中包含的是指针,由这个指针把一个元素链接到等待相同事件的进程链表中。
然而,要唤醒等待队列中所有睡眠的进程有时并不方便。例如,如果两个或多个进程正在等待互斥访问某一要释放的资源,仅唤醒等待队列中的一个进程才有意义。这个进程占有资源,而其他进程继续睡眠。
因此,有两种睡眠进程:互斥进程(的报告的队列元素的flags字段为1)由内核有选择地唤醒,而非互斥进程(flags值为0)总是由内核在事件发生时唤醒。等待访问临界资源的进程就是互斥进程的典型例子。等待相关事件的进程是非互斥的。例如,我们考虑等待磁盘传输结束的一组进程:一旦磁盘传输完成,所有等待的进程都会被唤醒。正如我们将在下面所看到的那样,等待队列元素func字段用来表示等待队列中睡眠进程应该用什么方式唤醒。
等待队列的操作
可以用DECLARE_WAIT_QUEUE_HEAD(name)宏定义一个新等待队列的头,它静态地申明一个叫name的等待队列的头变量并对该变量的lock和task_list字段进行初始化。函数init_waitqueue_head()可以用来初始化动态分配的等待队列的头变量。
函数init_waitqueue_entry(q,p)如下所示初始化wait_queue_t结构的变量q:
q->flags = 0;
q->task = p;
q->func = default_wake_function;
非互斥进程p将由default_wake_function()函数唤醒,default_wake_function()函数是在第七章中要讨论的try_to_wake_up( )函数的一个简单的封装。
也可以选择DEFINE_WAIT宏申明一个wait_queue_t类型的新变量,并用CPU上运行的当前进程的描述符和唤醒函数autoremove_wake_function( )的地址初始化这个新变量。这个函数调用default_wake_function()函数来唤醒进程,然后从等待队列的链表中删除对应的元素(每个等待队列链表中的一个元素其实就是指向睡眠进程描述符的指针)。最后,内核开发者可以通过init_waitqueue_func_entry( )函数来自定义唤醒函数,该函数负责初始化等待队列的元素。
一旦定义了一个元素,必须把它插入等待队列。add_wait_queue( )函数把一个非互斥进程插入等待队列链表的第一个位置。add_wait_queue_exclusive( )函数把一个互斥进程插入等待队列链表的最后一个位置。remove_wait_queue( )函数从等待队列链表中删除一个进程。waitqueue_active( )函数检查一个给定的等待队列是否为空。
要等待特定条件的进程可以调用如下列表中的任何一个函数。
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);
}
该函数把当前进程的状态设置为TASK_UNINTERRUPTIBLE,并把它插入到特定的等待队列中去。然后,它调用调度程序,而调度程序重新开始另一个程序的执行。当睡眠进程被唤醒时,调度程序重新开始执行sleep_on()函数,把该进程从等待队列中删除。
interruptible_sleep_on()与sleep_on()函数是一样的,但稍有不同,前者把当前进程的状态设置为TASK_INTERRUPTIBLE而不是TASK_UNINTERRUPTIBLE,因此,接受一个信号就可以唤醒当前进程。
sleep_on_timeout() 和 interruptible_sleep_on_timeout()与前面函数类似,但它们允许调用者定义一个时间间隔,过了这个间隔以后,进程将由内核唤醒。为了做到这点,它们调用schedule_timeout()而不是schedule()函数。
在Linux 2.6中引入的prepare_to_wait( )、prepare_to_wait_exclusive( )和finish_wait( )函数提供了另外一种途径来使当前进程在一个等待队列中睡眠。它们的典型应用如下:
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);
函数prepare_to_wait( )和prepare_to_wait_exclusive( )用传递的第三个参数设置进程的状态,然后把等待队列元素的互斥标识flag分别设置为0(非互斥)或1(互斥),最后,把等待元素wait插入到以wq为头的等待队列的链表中。
进程一旦被唤醒就执行finish_wait()函数,它把进程的状态再次设置为TASK_RUNNING(仅发生在schedule()之前,唤醒条件变为真的情况下),并从等待队列中删除等待元素(除非这个工作已经由唤醒函数完成)。
wait_event和wait_event_interruptible宏使它们的调用进程在等待队列上睡眠,一直到修改了给定条件为止。例如wait_event(wq,condition)宏本质上实现下面的功能:
DEFINE_WAIT(_ _wait);
for (;;) {
prepare_to_wait(&wq, &_ _wait, TASK_UNINTERRUPTIBLE);
if (condition)
break;
schedule( );
}
finish_wait(&wq, &_ _wait);
对上面列出的函数做一些说明:sleep_on()类函数在以下条件下不能使用,那就是必须测试条件并且当前条件还没有得到验证时又紧接着让进程去睡眠;由于那些条件是众所周知的竞争条件产生的根源,所以不鼓励这样使用。此外,为了把一个互斥进程插入等待队列,内核必须使用prepare_to_wait_exclusive()函数(或者只是直接调用add_wait_queue_exclusive( ))。所有其他的相关函数把进程当做非互斥进程来插入。最后,除非使用DEFINE_WAIT或finish_wait(),否则内核必须在唤醒等待进程后从等待队列中删除对应的等待队列元素。
内核通过下面任何一个宏唤醒等待队列中的进程并把他们的状态置为TASK_RUNNING:wake_up,wake_up_nr,wake_up_all, wake_up_interruptible,wake_up_interruptible_nr,wake_up_interruptible_all, wake_up_interruptible_sync和 wake_up_locked。从每个宏的名字我们可以明白其功能:
所有宏都考虑处于TASK_INTERRUPTIBLE状态的睡眠进程;如果宏的名字中不含字符串“interruptible”,那么处于TASK_UNINTERRUPTIBLE状态的睡眠进程也被考虑。
所有宏都唤醒具有请求状态的所有互斥进程(参见上一项)。
名字中含有“nr”字符串的宏唤醒给定数的具有请求装提的互斥进程;这个数字是宏的一个参数。名字中含有“all”字符串的宏唤醒具有请求状态的所有互斥进程。最后,名字中不含“nr”或“all”字符串的宏只唤醒具有请求状态的一个互斥进程。
名字中不含有“sync”字符串的宏检查被唤醒进程的优先级是否高于系统中正在运行行进程的优先级,并在必要时调用schedule()。这些检查并不是由名字中含有“sync”字符串的宏进行的,造成的结果是高优先级进程的执行稍有延迟。
wake_up_locked宏和wake_up宏相类似,仅有的不同是当wait_queue_head_t中的自旋锁已经被持有时要调用wake_up_locked。
侧如,wake_up宏等价于下列代码片段:
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;
}
}
List_for_each宏扫描双向链表q->task_list中的所有项,即等待队列中的所有进程。对每一项,list_entry宏都计算wait_queue_t变量对应的地址。这个变量的func字段存放唤醒函数的地址,它试图唤醒由等待队列元素的task字段标识的进程。如果一个进程已经被有效地唤醒(函数返回1)并且进程是互斥的(curr->flags等于1),循环结束。因为所有的非互斥进程总是在双向链表的开始位置,而所有的互斥进程在双向链表的尾部,所以函数总是先唤醒非互斥进程然后再唤醒互斥进程,如果有进程存在的话。
|
NO
|
这么长的回复,哪里找来的啊?~~~