linux tty core 源码分析(8)

      tty_ioctl和tty_compat_ioctl都是对设备的控制操作,比较容易理解这里就不做分析,有兴趣的读者可以自己分析。其中tty_compat_ioctl使用在用户空间为32位模式而内核空间为64位模式时将64位转化为32位的操作方式。   

      剩下的就是最后的操作,当关闭tty设备是调用的tty_release操作,主要是释放前面分配的资费做tty_open的反操作

 

/**
 * tty_release  - vfs callback for close
 * @inode: inode of tty
 * @filp: file pointer for handle to tty
 *
 * Called the last time each file handle is closed that references
 * this tty. There may however be several such references.
 *
 * Locking:
 *  Takes bkl. See release_dev
 */

static int tty_release(struct inode *inode, struct file *filp)
{
 lock_kernel();
 release_dev(filp);
 unlock_kernel();
 return 0;
}

 

/*
 * Even releasing the tty structures is a tricky business.. We have
 * to be very careful that the structures are all released at the
 * same time, as interrupts might otherwise get the wrong pointers.
 *
 * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
 * lead to double frees or releasing memory still in use.
 */
static void release_dev(struct file *filp)
{
 struct tty_struct *tty, *o_tty;
 int pty_master, tty_closing, o_tty_closing, do_sleep;
 int devpts;
 int idx;
 char buf[64];

 tty = (struct tty_struct *)filp->private_data;
 if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode,
       "release_dev"))
  return;

 check_tty_count(tty, "release_dev");  //对tty设备打开次数进行统计,tty设备的每次打开tty->files创建一个文件描述符

 tty_fasync(-1, filp, 0); //从文件队列中删去异步通知结构struct fasync_struct

 idx = tty->index;
 pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
        tty->driver->subtype == PTY_TYPE_MASTER);  //设备是伪终端主设备
 devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
 o_tty = tty->link;

//对设备在驱动对应对对象进行检查

#ifdef TTY_PARANOIA_CHECK
 if (idx < 0 || idx >= tty->driver->num) {
  printk(KERN_DEBUG "release_dev: bad idx when trying to "
      "free (%s)/n", tty->name);
  return;
 }
 if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
  if (tty != tty->driver->ttys[idx]) {
   printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
          "for (%s)/n", idx, tty->name);
   return;
  }
  if (tty->termios != tty->driver->termios[idx]) {
   printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
          "for (%s)/n",
          idx, tty->name);
   return;
  }
  if (tty->termios_locked != tty->driver->termios_locked[idx]) {
   printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
          "termios_locked for (%s)/n",
          idx, tty->name);
   return;
  }
 }
#endif

#ifdef TTY_DEBUG_HANGUP
 printk(KERN_DEBUG "release_dev of %s (tty count=%d)...",
        tty_name(tty, buf), tty->count);
#endif

//伪终端设备对等端的检查

#ifdef TTY_PARANOIA_CHECK
 if (tty->driver->other &&
      !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
  if (o_tty != tty->driver->other->ttys[idx]) {
   printk(KERN_DEBUG "release_dev: other->table[%d] "
       "not o_tty for (%s)/n",
          idx, tty->name);
   return;
  }
  if (o_tty->termios != tty->driver->other->termios[idx]) {
   printk(KERN_DEBUG "release_dev: other->termios[%d] "
       "not o_termios for (%s)/n",
          idx, tty->name);
   return;
  }
  if (o_tty->termios_locked !=
        tty->driver->other->termios_locked[idx]) {
   printk(KERN_DEBUG "release_dev: other->termios_locked["
       "%d] not o_termios_locked for (%s)/n",
          idx, tty->name);
   return;
  }
  if (o_tty->link != tty) {
   printk(KERN_DEBUG "release_dev: bad pty pointers/n");
   return;
  }
 }
#endif
 if (tty->ops->close)
  tty->ops->close(tty, filp);  //调用tty->ops->close释放相应资源

 /*
  * Sanity check: if tty->count is going to zero, there shouldn't be
  * any waiters on tty->read_wait or tty->write_wait.  We test the
  * wait queues and kick everyone out _before_ actually starting to
  * close.  This ensures that we won't block while releasing the tty
  * structure.
  *
  * The test for the o_tty closing is necessary, since the master and
  * slave sides may close in any order.  If the slave side closes out
  * first, its count will be one, since the master side holds an open.
  * Thus this test wouldn't be triggered at the time the slave closes,
  * so we do it now.
  *
  * Note that it's possible for the tty to be opened again while we're
  * flushing out waiters.  By recalculating the closing flags before
  * each iteration we avoid any problems.
  */
 while (1) { //循环操作,知道tty设备的的读写操作都完成才退出循环
  /* Guard against races with tty->count changes elsewhere and
     opens on /dev/tty */

  mutex_lock(&tty_mutex);
  tty_closing = tty->count <= 1;  //是否执行真正的关闭
  o_tty_closing = o_tty &&
   (o_tty->count <= (pty_master ? 1 : 0));
  do_sleep = 0;

  if (tty_closing) {   //在设备关闭前先完成等待的读写操作
   if (waitqueue_active(&tty->read_wait)) {
    wake_up(&tty->read_wait);
    do_sleep++;
   }
   if (waitqueue_active(&tty->write_wait)) {
    wake_up(&tty->write_wait);
    do_sleep++;
   }
  }
  if (o_tty_closing) {  

   if (waitqueue_active(&o_tty->read_wait)) {
    wake_up(&o_tty->read_wait);
    do_sleep++;
   }
   if (waitqueue_active(&o_tty->write_wait)) {
    wake_up(&o_tty->write_wait);
    do_sleep++;
   }
  }
  if (!do_sleep)  //完成所有的读写操作
   break;

  printk(KERN_WARNING "release_dev: %s: read/write wait queue "
        "active!/n", tty_name(tty, buf));
  mutex_unlock(&tty_mutex);
  schedule();
 }

 /*
  * The closing flags are now consistent with the open counts on
  * both sides, and we've completed the last operation that could
  * block, so it's safe to proceed with closing.
  */
 if (pty_master) {
  if (--o_tty->count < 0) {
   printk(KERN_WARNING "release_dev: bad pty slave count "
         "(%d) for %s/n",
          o_tty->count, tty_name(o_tty, buf));
   o_tty->count = 0;
  }
 }
 if (--tty->count < 0) {
  printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s/n",
         tty->count, tty_name(tty, buf));
  tty->count = 0;
 }

 /*
  * We've decremented tty->count, so we need to remove this file
  * descriptor off the tty->tty_files list; this serves two
  * purposes:
  *  - check_tty_count sees the correct number of file descriptors
  *    associated with this tty.
  *  - do_tty_hangup no longer sees this file descriptor as
  *    something that needs to be handled for hangups.
  */
 file_kill(filp); //关闭文件描述符
 filp->private_data = NULL;

 /*
  * Perform some housekeeping before deciding whether to return.
  *
  * Set the TTY_CLOSING flag if this was the last open.  In the
  * case of a pty we may have to wait around for the other side
  * to close, and TTY_CLOSING makes sure we can't be reopened.
  */
 if (tty_closing)
  set_bit(TTY_CLOSING, &tty->flags);
 if (o_tty_closing)
  set_bit(TTY_CLOSING, &o_tty->flags);

 /*
  * If _either_ side is closing, make sure there aren't any
  * processes that still think tty or o_tty is their controlling
  * tty.
  */
 if (tty_closing || o_tty_closing) {
  read_lock(&tasklist_lock);
  session_clear_tty(tty->session);
  if (o_tty)
   session_clear_tty(o_tty->session);
  read_unlock(&tasklist_lock);
 }

 mutex_unlock(&tty_mutex);

 /* check whether both sides are closing ... */
 if (!tty_closing || (o_tty && !o_tty_closing))
  return;

#ifdef TTY_DEBUG_HANGUP
 printk(KERN_DEBUG "freeing tty structure...");
#endif
 /*
  * Ask the line discipline code to release its structures
  */
 tty_ldisc_release(tty, o_tty);
 /*
  * The release_tty function takes care of the details of clearing
  * the slots and preserving the termios structure.
  */
 release_tty(tty, idx);

 /* Make this pty number available for reallocation */
 if (devpts)
  devpts_kill_index(idx);
}

 

自此我们分析了linux操作系统中/dev/tty /dev/tty0 /dev/console等设备作为字符设备的驱动程序,同时tty核心也为其他tty设备驱动的注册提供了一个通用的接口和一个通用的管理平台,为其他tty设备驱动的实现提供了一个通用层,下一节中我们将分析tty设备驱动的管理以及如何利用tty核心去实现一个tty设备驱动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值