Chinaunix首页 | 论坛 | 博客
  • 博客访问: 25192
  • 博文数量: 4
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 55
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-24 10:45
文章分类
文章存档

2008年(4)

我的朋友
最近访客

分类: LINUX

2008-08-05 10:32:07

等待队列操作分析:
     linux驱动程序中可以用等待队列(wiat queue)来实现阻塞的唤醒
1)定义等待队列头
等待队列头结构体的定义:
struct __wait_queue_head {
	spinlock_t  lock;          //自旋锁变量,用于在对等待队列头
//指向的等待队列链表进行操作时锁上,查看哪有对
lock
的操作?
struct list_head task_list; // 指向等待队列的list_head }; typedef struct __wait_queue_head wait_queue_head_t; #define DECLARE_WAIT_QUEUE_HEAD(name) \ \\声明一个待队列头对象 name: wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name) #define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \\\待队列头的初始化: .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .task_list = { &(name).task_list, &(name).task_list } } lock赋为unlocked,将等待队列头指向的等待队列链表指向name,从而将等待队列头和
等待队列连起来
; 2)等待队列中存放的是在执行设备操作时不能获得资源而挂起的进程 定义等待对列: struct __wait_queue { unsigned int flags; //prepare_to_wait()里有对flags的操作,查看以得出其含义 #define WQ_FLAG_EXCLUSIVE 0x01 //一个常数,在prepare_to_wait()用于修改flags的值
wait_queue_func_t func; //唤醒阻塞任务的函数 struct list_head task_list; // 阻塞任务链表 }; typedef struct __wait_queue wait_queue_t; #define DECLARE_WAITQUEUE(name, tsk) \\\声明一个等待队列并初始化为name wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk) #define __WAITQUEUE_INITIALIZER(name, tsk) { \ \\等待对列初始化: .private = tsk, \ .func = default_wake_function, \ .task_list = { NULL, NULL } } 下列两个函数用于对特定的成员进行赋值(当传入不同类型的参数时); static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p) { q->flags = 0; q->private = p; //私有数据指针 q->func = default_wake_function; //使用默认的唤醒函数 } static inline void init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func) { q->flags = 0; q->private = NULL; q->func = func; // 自定义的唤醒函数 } 3)对等待队列进行操作 static inline int waitqueue_active(wait_queue_head_t *q) { return !list_empty(&q->task_list); } 判断等待对列头是否为空,当一个进程访问设备而得不到资源时就会被放入等待队列头指
向的等待队列中,当该它是第一个被阻塞的进程,(等待队列头是一开始就有的还
是有了第一个被阻塞的进程后才创建的?)
若此时等待队列头还是空的,要先创建(见上面)
然后再插入新的等待队列


对等待队列的链表操作
static inline void __add_wait_queue(wait_queue_head_t *head,\
 wait_queue_t *new) /
{ list_add(&new->task_list, &head->task_list); } /增加一个等待队列new到等待
队列头head指向的等待队列链表中;
static inline void __add_wait_queue_tail(wait_queue_head_t *head, // wait_queue_t *new) { list_add_tail(&new->task_list, &head->task_list); } 增加一个等待队列到表尾 static inline void __remove_wait_queue \
(wait_queue_head_t *head,wait_queue_t *old)
{ list_del(&old->task_list); } 删除一个等待队列4)等待事件当等待队列加入到链表中以后,就要等待特定的condition来 唤醒它; #define __wait_event(wq, condition) \\\wq:在等待事件的等待队列;condition:等待的条件 do { \ DEFINE_WAIT(__wait); \定义并初始化一个wait_queue_t结构 \ for (;;) { \ prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ if (condition) \ //wait_queue:wq要等的condition是否满足 break; \ schedule(); \//condition不成立,放弃cpu重新调度一个task } \ finish_wait(&wq, &__wait); \ } while (0) 等待condition在成立,否则进程睡眠(TASK_UNINTERRUPTIBLE);condition满足
后等待结束,跳出循环(后面调用
wake_up(x)
进行唤醒)当任何能改变等待条件
的值的变量发生改变时,要调用
wake_up()


wait_event(wq, condition) 			
__wait_event()的基础上多了一次查询(每次被唤醒的时候)		

#define __wait_event_timeout(wq, condition, ret) \

condition满足或ret使用完了时进程被唤醒; 返回值为:return timeout < 0 ? 0 : timeout

timeout是一个jiffies类型的变量,当时间用完了,函数返回0,当等待的条件成立了,
timeout
还未用完,则将最后的jiffies保留下来。类似的操作还有:
 #define __wait_event_interruptible_timeout(wq, condition, ret)
\
可中断,有超时时间的__wait_event(),当timeout长的时间完了后,函数返回0;当时间未完,
函数被信号中断则返回
-ERESTARTSYS
;如果timeout isn't out,保留jiffies最后的值;
#define wait_event_interruptible_timeout(wq, condition, timeout) \

多了一次查询


#define __wait_event_interruptible_exclusive(wq, condition, ret)	\

#define wait_event_interruptible_exclusive(wq, condition)		\

这几个函数都有用到prepare_to_wait()下面分析一下这个函数;
prepare_to_wait() 函数

void fastcall

prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)

{

unsigned long flags;

wait->flags &= ~WQ_FLAG_EXCLUSIVE; //弄清楚这一行是什么意思;

spin_lock_irqsave(&q->lock, flags); //获得自旋锁并保存EFLAGS的值

if (list_empty(&wait->task_list)) //判断是等待队列是否为空:空时返回1;函数为:

//static inline int list_empty(const struct list_head *head)

__add_wait_queue(q, wait); //{ return head->next == head;}

/* 插入等待队列中(为何空时插入,非空时不行?

* don't alter the task state if this is just going to

* queue an async wait queue callback

*/

if (is_sync_wait(wait))

set_current_state(state); //因为非阻塞进程访问不到设备时并不挂起,所以不改状态

//set_current_state()->set_mb()->mb() :强制顺序执行(更改状态)

//函数mb()内存栅头文件中定义的

spin_unlock_irqrestore(&q->lock, flags);//解锁将EFLAGS的值读回

}

prepare_to_wait()的作用是将等待队列插入等待队列链表中,并更改等待队列的状态为state





inux将进程状态描述为如下五种:

TASK_RUNNING:可运行状态。处于该状态的进程可以被调度执行而成为当前进程。

TASK_INTERRUPTIBLE:可中断的睡眠状态。处于该状态的进程在所需资源有效时被唤醒,也可以通过信号或定时中断唤醒。

TASK_UNINTERRUPTIBLE:不可中断的睡眠状态。处于该状态的进程仅当所需资源有效时被唤醒。

TASK_ZOMBIE:僵尸状态。表示进程结束且已释放资源,但其task_struct仍未释放。

TASK_STOPPED:暂停状态。处于该状态的进程通过其他进程的信号才能被唤醒。


唤醒:

#define wake_up(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)


void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode,

int nr_exclusive, void *key)

{

unsigned long flags;


spin_lock_irqsave(&q->lock, flags);

__wake_up_common(q, mode, nr_exclusive, 0, key);

spin_unlock_irqrestore(&q->lock, flags);

}

Wake_up()唤醒等待队列中的进程,其参数含义:

q:等待队列;

mode:要唤醒的进程

nr_exclusive:要唤醒的进程数;

keyis directly passed to the wakeup function




阅读(3351) | 评论(5) | 转发(0) |
0

上一篇:local_irq_disable()分析

下一篇:没有了

给主人留下些什么吧!~~

niutao.linux2008-08-07 11:27:47

的确是,以后要多多注意!FIGHTING!

chinahhucai2008-08-06 16:07:41

结构体__wait_queue中少了成员private;

niutao.linux2008-08-05 19:18:11

”prepare_to_wait()不是设置flag值,而是和sleep_on()功能一样,把当前进程加入等待队列“ 你说错了。prepare_to_wait的基本功能是把当前进程加入等待队列头中,并改变进程的状态。而sleep_on()是先设置进程的状态为TASK_UNINTERRUPTIBLE,在将进程添加进等待队列中,并进入睡眠,等到等待的资源可用时被唤醒并将其从等待队列中删除。 如此说来sleep_on()和wait_event()的功能到是有些相似。

KYlinux2008-08-05 16:51:19

wait->flags &= ~WQ_FLAG_EXCLUSIVE; //弄清楚这一行是什么意思; 表明是非互斥进程

kylinux2008-08-05 16:44:57

flag: 互斥进程(exclusive processes)和非互斥进程: 其中在__wake_up()有个参数nr,判断唤醒的进程数。 0:代表唤醒所有进程 1:代表唤醒一个 nr:当然还有其他 总是唤醒所有等待该事件的进程并不一定是合适的。比如考虑这样一种情况:如果队列中的多个进程等待的资源是要互斥访问的,一定时间内只允许一个进程去访问的话,这时候,只需要唤醒一个进程就可以了,其他进程继续睡眠。如果唤醒所有的进程,最终也只有一个进程获得该资源,其他进程让需返回睡眠。 因此,等待队列中的睡眠进程可被划分为互斥、非互斥进程。 互斥进程:等待的资源是互斥访问的;互斥进程由内核有选择的唤醒,等待队列项的flag字段为1; 非互斥进程:等待的资源是可多进程同时访问的。非互斥进程在事件发生时,总是被内核唤醒,等待队列元素的flag字段为0。 prepare_to_wait()不是设置flag值,而是和sleep_on()功能一样,把当前进程加入等待队列