Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1772571
  • 博文数量: 272
  • 博客积分: 1272
  • 博客等级: 少尉
  • 技术积分: 1866
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-09 15:51
文章分类

全部博文(272)

文章存档

2016年(16)

2015年(28)

2014年(97)

2013年(59)

2012年(25)

2011年(47)

分类: LINUX

2013-11-15 16:12:33

  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设备驱动。

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