这是我的摘自我的串口驱动的一段代码:
static ssize_t serial_read(struct file *file, char * buf,
size_t count, loff_t *ppos)
{
while(count) {
cnt = MIN(count, MIN(info->xmit_cnt,
SERIAL_XMIT_SIZE - info->xmit_tail));
if(cnt <= 0)
break;
if(copy_to_user(buf + rtn, info->xmit_buf + info->xmit_tail,cnt))
//将驱动缓冲区中的数据拷贝到应用层的buf中
{
rtn = -EFAULT;
goto done;
}
rtn += cnt;
count -= cnt;
DOWN(info->tx_lock, flags);
info->xmit_cnt -= cnt;
//为驱动缓冲区腾出了一部分空间
info->xmit_tail += cnt;
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1);
#if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0))
info->icount.tx += cnt;
#endif
UP(info->tx_lock, flags);
}
//在结尾处,有这么一句代码,很不解?
wake_up_interruptible(&tty->write_wait);
done:
return rtn;
}
后来读到了ldisc的write函数,代码如下:
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
...
add_wait_queue(&tty->write_wait, &wait);//将当前进程放到等待队列中
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
//进入此处继续执行的原因可能是被信号打断,而不是条件得到了满足。
//只有条件得到了满足,我们才会继续,否则,直接返回!
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
while (nr > 0) {
ssize_t num = process_output_block(tty, b, nr);
if (num < 0) {
if (num == -EAGAIN)
break;
retval = num;
goto break_out;
}
b += num;
nr -= num;
if (nr == 0)
break;
c = *b;
if (process_output(c, tty) < 0)
break;
b++; nr--;
}
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
} else {
while (nr > 0) {
c = tty->ops->write(tty, b, nr);
//众所周知,此处的write指具体的驱动里write函数
if (c < 0) {
retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
}
}
if (!nr)
break;
//全部写入,返回
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
/*
假如是以非阻塞的方式打开的,那么也直接返回。否则,让出cpu,等条件满足以后再继续执行。
*/
schedule();//执行到这里,当前进程才会真正让出cpu!!!
/*
此处让出cpu以后,什么时候才会继续执行呢?
答案是当某个地方调用wake_up(&tty->write_wait)!
那么什么地方的代码会调用wake_up(&tty->write_wait)呢?
让我们分析一下此处让出cpu的原因:
c = tty->ops->write(tty, b, nr);调用失败!为什么会失败呢?
可能的原因是驱动里申请的的缓冲区满了,所以不能再往里边写了!
终于明白了serial_read函数的wake_up_interruptible(&tty->write_wait);这个语句是为了唤醒此处的代码!
从serial_read函数可以看出,在调用wake_up_interruptible(&tty->write_wait)
函数之前,我们将驱动缓冲区里的数据写入了用户缓冲buf中,这样驱动的缓冲区就又有空间了。
所以,有必要在serial_read中唤醒n_tty_write函数,让它再次尝试执行write动作。
*/
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
...
}
serial_read()函数中的wake_up_interruptible(&tty->write_wait)其实想表达的意思是:我读完了(把驱动缓冲中的数据拿走了,缓冲区空了),你继续写吧,于是唤醒了n_tty_write,n_tty_write再调用serial_write把用户层传下来的数据写到驱动缓冲区里边去。
需要注意的是:正常的进程调度也会让调用n_tty_write的进程重新得到cpu!
代码摘自:linux-2.6.29.RT.x
xin.jin
阅读(4020) | 评论(0) | 转发(0) |