linux tty core 源码分析(3)

前面分析了open操作,现在分析读操作tty_read。tty_read直接调用线路规程中的读操作从tty->read_buf中读取数据到用户空间。其中tty.read_head记录已读数据的起始位置,tty.read_tail记录已读数据的末尾位置,tty.read_cnt记录已读数据的数量。至于所读数据从何而来我们在下一篇中分析,下面看具体代码:

 

/**
 * tty_read - read method for tty device files
 * @file: pointer to tty file
 * @buf: user buffer
 * @count: size of user buffer
 * @ppos: unused
 *
 * Perform the read system call function on this terminal device. Checks
 * for hung up devices before calling the line discipline method.
 *
 * Locking:
 *  Locks the line discipline internally while needed. Multiple
 * read calls may be outstanding in parallel.
 */

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
   loff_t *ppos)
{
 int i;
 struct tty_struct *tty;
 struct inode *inode;
 struct tty_ldisc *ld;

 tty = (struct tty_struct *)file->private_data;//得到open中设置的tty结构
 inode = file->f_path.dentry->d_inode;
 if (tty_paranoia_check(tty, inode, "tty_read"))  //tty及其幻数检查
  return -EIO;
 if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
  return -EIO;

 /* We want to wait for the line discipline to sort out in this
    situation */
 ld = tty_ldisc_ref_wait(tty);  //TTY_LDISC 表示tty和线路规程绑定
 if (ld->ops->read)
  i = (ld->ops->read)(tty, file, buf, count);
 else
  i = -EIO;
 tty_ldisc_deref(ld);
 if (i > 0)
  inode->i_atime = current_fs_time(inode->i_sb);
 return i;
}

 

读操作的具体细节都在线路规程中实现的,默认的线路规程的读操作时read_chan函数,下面看具体源码:

/**
 * read_chan  - read function for tty
 * @tty: tty device
 * @file: file object
 * @buf: userspace buffer pointer
 * @nr: size of I/O
 *
 * Perform reads for the line discipline. We are guaranteed that the
 * line discipline will not be closed under us but we may get multiple
 * parallel readers and must handle this ourselves. We may also get
 * a hangup. Always called in user context, may sleep.
 *
 * This code must be sure never to sleep through a hangup.
 */

static ssize_t read_chan(struct tty_struct *tty, struct file *file,
    unsigned char __user *buf, size_t nr)
{
 unsigned char __user *b = buf;
 DECLARE_WAITQUEUE(wait, current); //声明等待队列项,每次读操作都加入tty.read_wait等待队列
 int c;
 int minimum, time;
 ssize_t retval = 0;
 ssize_t size;
 long timeout;
 unsigned long flags;
 int packet;

do_it_again:

 if (!tty->read_buf) {
  printk(KERN_ERR "n_tty_read_chan: read_buf == NULL?!?/n");
  return -EIO;
 }

 c = job_control(tty, file);   //tty非控制台而是进程控制终端时的处理
 if (c < 0)
  return c;

 

 minimum = time = 0;
 timeout = MAX_SCHEDULE_TIMEOUT;

//tty设备对数据的处理分为原始模式和规范规范模式tty->icannon表示这种模式

//原始模式时根据c_cc[VTIME]和c_cc[VMIN]设置唤醒用户读读进程的超时时间和数据量
 if (!tty->icanon) {
  time = (HZ / 10) * TIME_CHAR(tty);
  minimum = MIN_CHAR(tty);
  if (minimum) {
   if (time)
    tty->minimum_to_wake = 1;
   else if (!waitqueue_active(&tty->read_wait) ||
     (tty->minimum_to_wake > minimum))
    tty->minimum_to_wake = minimum;
  } else {
   timeout = 0;
   if (time) {
    timeout = time;
    time = 0;
   }
   tty->minimum_to_wake = minimum = 1;
  }
 }

 

//tty->atomic_read_lock对读操作的互斥保护

 /*
  * Internal serialization of reads.
  */
 if (file->f_flags & O_NONBLOCK) {
  if (!mutex_trylock(&tty->atomic_read_lock))
   return -EAGAIN;
 } else {
  if (mutex_lock_interruptible(&tty->atomic_read_lock))
   return -ERESTARTSYS;
 }

 

//伪终端可以用ioctl将主从两端的通讯方式设置为packet模式(信包模式),tty->link->ctrl_status非零表明提交的是链路控制信息
 packet = tty->packet;

 add_wait_queue(&tty->read_wait, &wait);  //把读进程加入读等待队列

 

//进行被唤醒,进程被唤醒时一切情况是不可预测的,因此需要对等待条件进行判断。

 

 while (nr) {
  /* First test for status change. */
  if (packet && tty->link->ctrl_status) {
   unsigned char cs;
   if (b != buf)
    break;
   spin_lock_irqsave(&tty->link->ctrl_lock, flags);
   cs = tty->link->ctrl_status;
   tty->link->ctrl_status = 0;
   spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
   if (tty_put_user(tty, cs, b++)) {
    retval = -EFAULT;
    b--;
    break;
   }
   nr--;
   break;
  }
  /* This statement must be first before checking for input
     so that any interrupt will set the state back to
     TASK_RUNNING. */
  set_current_state(TASK_INTERRUPTIBLE);

  if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
      ((minimum - (b - buf)) >= 1))
   tty->minimum_to_wake = (minimum - (b - buf));

 

//判断有没有数据可读,原始模式和规范模式有差异,以下表示无数据可读若读条件不成立则退出,否则再次加入到等待队列中

  if (!input_available_p(tty, 0)) {  

   if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {  //伪终端对待端关闭
    retval = -EIO;
    break;
   }
   if (tty_hung_up_p(file))
    break;
   if (!timeout)
    break;
   if (file->f_flags & O_NONBLOCK) { //非阻塞模式
    retval = -EAGAIN;
    break;
   }
   if (signal_pending(current)) {//TIF_SIGPENDING进程有信号要处理
    retval = -ERESTARTSYS;
    break;
   }
   /* FIXME: does n_tty_set_room need locking ? */
   n_tty_set_room(tty); //设置接收空间
   timeout = schedule_timeout(timeout);
   continue;
  }

 

 //有数据可读,设置进程为可执行状态
  __set_current_state(TASK_RUNNING);

  /* Deal with packet mode. */
  if (packet && b == buf) {    //伪终端的信包模式
   if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
    retval = -EFAULT;
    b--;
    break;
   }
   nr--;
  }

 

//规范模式的读处理:规范模式下缓冲区的数据是经过加工的,要积累起一个缓冲行,才唤醒等待读的进程。

  if (tty->icanon) {

   /* N.B. avoid overrun if nr == 0 */
   while (nr && tty->read_cnt) {
    int eol;

//tty->read_tail对应的tty->read_flags为1表示缓冲行的终点

    eol = test_and_clear_bit(tty->read_tail,
      tty->read_flags);
    c = tty->read_buf[tty->read_tail];
    spin_lock_irqsave(&tty->read_lock, flags);
    tty->read_tail = ((tty->read_tail+1) &   //环形缓冲区的处理
        (N_TTY_BUF_SIZE-1));
    tty->read_cnt--;
    if (eol) {
     /* this test should be redundant:
      * we shouldn't be reading data if
      * canon_data is 0
      */
     if (--tty->canon_data < 0)
      tty->canon_data = 0;
    }
    spin_unlock_irqrestore(&tty->read_lock, flags);

    if (!eol || (c != __DISABLED_CHAR)) {  //DISABLED '/0'
     if (tty_put_user(tty, c, b++)) {
      retval = -EFAULT;
      b--;
      break;
     }
     nr--;
    }
    if (eol) {
     tty_audit_push(tty);
     break;
    }
   }
   if (retval)
    break;
  } else { //原始模式的处理:直接把数据批量复制,注意调用两次是处理环形缓冲区的回头
   int uncopied;
   /* The copy function takes the read lock and handles
      locking internally for this case */
   uncopied = copy_from_read_buf(tty, &b, &nr);
   uncopied += copy_from_read_buf(tty, &b, &nr);
   if (uncopied) {
    retval = -EFAULT;
    break;
   }
  }

  /* If there is enough space in the read buffer now, let the
   * low-level driver know. We use n_tty_chars_in_buffer() to
   * check the buffer, as it now knows about canonical mode.
   * Otherwise, if the driver is throttled and the line is
   * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
   * we won't get any more characters.
   */

//读缓冲区中的可读数据少于某阈值时就调用tty->ops->unthrottle()操作
  if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {
   n_tty_set_room(tty);
   check_unthrottle(tty);
  }

  if (b - buf >= minimum)
   break;
  if (time)
   timeout = time;
 }
 mutex_unlock(&tty->atomic_read_lock);
 remove_wait_queue(&tty->read_wait, &wait);

 if (!waitqueue_active(&tty->read_wait))
  tty->minimum_to_wake = minimum;

 __set_current_state(TASK_RUNNING);
 size = b - buf;
 if (size) {
  retval = size;
  if (nr)
   clear_bit(TTY_PUSH, &tty->flags);
 } else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
   goto do_it_again;

 n_tty_set_room(tty);
 return retval;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值