linux 进程睡眠与wait_even t_interruptible()分析

驱动中最底层处理buffer通常会对该进程睡眠和唤醒操作,比如上层写入buffer时,内核buffer缓冲区是满的,那么写入进程需要阻塞住,直到内核buffer缓冲区可以容纳上层写入的buffer才唤醒该进程。

首先必须认清一个事实:
先将进程置为INTERRUPTIBLE,再调用schedule()后本进程才睡眠;而不是执行set_current_state(TASK_INTERRUPTIBLE)后就睡眠了。
set_current_state(TASK_INTERRUPTIBLE);    
schedule();       

下面是对wait_event_interruptible()的流程分析,有助于自己理解对进程的处理。wait_event_interruptible()只是封装了对进程处理的函数,理解后我们可以单独操作等待队列加进程处理函数而实现睡眠与唤醒功能。

#define wait_event_interruptible(wq, condition) 
({ 
int __ret = 0; 
if (!(condition)) 
__wait_event_interruptible(wq, condition, __ret); 
__ret; 
})

#define __wait_event_interruptible(wq, condition, ret) 
do { 
DEFINE_WAIT(__wait); 

for (;;) { 
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); //将进程设置成INTERRUPTIBLE 
if (condition)             //通常第一次进来条件为假 
break; 
if (!signal_pending(current)) { //signal_pending无信号处理返回0,反之非0 
schedule(); //调用schedule()一直等待返回,进程睡眠直到有wake_up唤醒后 schedule()返回 执行continue结束本次循环,接着执行下次循环condition为真break出来
//wait_event_interruptible()返回0;
continue; 
} 
ret = -ERESTARTSYS; 
break; 
} 
finish_wait(&wq, &__wait); 
} while (0)



void
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);
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait);

void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;

__set_current_state(TASK_RUNNING);
/*
* We can check for list emptiness outside the lock
* IFF:
*  - we use the "careful" check that verifies both
*    the next and prev pointers, so that there cannot
*    be any half-pending updates in progress on other
*    CPU's that we haven't seen yet (and that might
*    still change the stack area.
* and
*  - all other users take the lock (ie we can only
*    have _one_ other CPU that looks at or modifies
*    the list).
*/
if (!list_empty_careful(&wait->task_list)) {
spin_lock_irqsave(&q->lock, flags);
list_del_init(&wait->task_list);
spin_unlock_irqrestore(&q->lock, flags);
}
}


还有一个实例,串口网卡驱动的buffer读取功能,发送很好实现将socket里面的buffer丢给串口文件的write函数即可。但是实现有串口buffer过来就读取到socket buffer里面就不容易。
该网卡驱动实现方法:在register net设备的时候起一个RX进程,然后去poll串口文件,poll_schedule()实现进程进程睡眠,poll_initwait(&wait_table)会实现软中断当有buffer进来时就会wake_up()唤醒该进程,进而读取buffer 上交到socket buffer里面

static int wi629_poll(struct file *filep)
{
int ret;
unsigned int mask = 0;
struct poll_wqueues wait_table;
mm_segment_t oldfs;

poll_initwait(&wait_table);   /* register callback function: __pollwait() */

for(;;) {
set_current_state(TASK_INTERRUPTIBLE);

oldfs = get_fs(); set_fs(get_ds());
if (filep->f_op->poll) 
mask = filep->f_op->poll(filep, &wait_table.pt);
set_fs(oldfs);

if (mask & POLLIN) {  /* got data */
ret = 0;
break;
}

if (wait_table.error) {
printk(KERN_INFO "  error in f_op->poll()\n");
ret = wait_table.error;
break;
}

if (signal_pending(task_rx)) {  /* got signal */
ret = -ERESTARTSYS;
break;
}

poll_schedule(&wait_table,TASK_INTERRUPTIBLE); /* sleep, and wait to be wakeup */
}
set_current_state(TASK_RUNNING);
poll_freewait(&wait_table);

return ret;
}


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值