Chinaunix首页 | 论坛 | 博客
  • 博客访问: 572595
  • 博文数量: 70
  • 博客积分: 3736
  • 博客等级: 中校
  • 技术积分: 1728
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-08 09:15
文章分类
文章存档

2014年(1)

2012年(21)

2011年(7)

2010年(28)

2009年(13)

分类: LINUX

2010-05-26 17:49:26

这是我的摘自我的串口驱动的一段代码:
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



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