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