《linux内核源代码情景分析》读书笔记。
tty_open函数源码:
static int tty_open(struct inode * inode, struct file * filp)
{
......
retval = init_dev(device, &tty);调用此函数,为需要打开的终端设备建立一个(或找到其)tty_struct数据结构。每个已打开的终端设备都有一个tty_struct数据结构代表。在tty_struct数据结构中有个struct tty_flip_buffer flip
struct tty_flip_buffer {
struct tq_struct tqueue;
struct semaphore pty_sem;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int count;
int buf_num;
unsigned char char_buf[2*TTY_FLIPBUF_SIZE];
char flag_buf[2*TTY_FLIPBUF_SIZE];
unsigned char slop[4]; /* N.B. bug overwrites buffer by 1 */
};
是终端设备的输入缓冲区,底层的中断服务程序将接受到字符存储于这个缓冲区中,供其上层读取。init_dev函数代码如下:
static int init_dev(kdev_t device, struct tty_struct **ret_tty)
{
........
driver = get_tty_driver(device);
if (!driver)
return -ENODEV;
根据设备号从tty_drivers链表中找到相应的tty_driver数据结构,这个结构中有个指针table,指向一个tty_struct结构指针数组。数组中包含了所有该种终端设备的tty_struct结构指针。
idx = MINOR(device) - driver->minor_start;
/*
* Check whether we need to acquire the tty semaphore to avoid
* race conditions. For now, play it safe.
*/
down_tty_sem(idx);
/* check whether we're reopening an existing tty */
tty = driver->table[idx];
如果tty_struct结构存在,就不需从新创建
if (tty) goto fast_track;
/*
* First time open is complex, especially for PTY devices.
* This code guarantees that either everything succeeds and the
* TTY is ready for operation, or else the table slots are vacated
* and the allocated memory released. (Except that the termios
* and locked termios may be retained.)
*/
o_tty = NULL;
tp = o_tp = NULL;
ltp = o_ltp = NULL;
tty = alloc_tty_struct();分配空间
if(!tty)
goto fail_no_mem;
initialize_tty_struct(tty);
初始化,源码如下:
/*
* This subroutine initializes a tty structure.
*/
static void initialize_tty_struct(struct tty_struct *tty)
{
memset(tty, 0, sizeof(struct tty_struct));
tty->magic = TTY_MAGIC;
tty->ldisc = ldiscs[N_TTY];
tty->pgrp = -1;
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
init_MUTEX(&tty->flip.pty_sem);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
tty->tq_hangup.routine = do_tty_hangup;
tty->tq_hangup.data = tty;
sema_init(&tty->atomic_read, 1);
sema_init(&tty->atomic_write, 1);
spin_lock_init(&tty->read_lock);
INIT_LIST_HEAD(&tty->tty_files);
INIT_TQUEUE(&tty->SAK_tq, 0, 0);
}
回到init_dev中
tty->device = device;
tty->driver = *driver;
tp_loc = &driver->termios[idx];
if (!*tp_loc) {
tp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!tp)
goto free_mem_out;
*tp = driver->init_termios;
}
ltp_loc = &driver->termios_locked[idx];
if (!*ltp_loc) {
ltp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!ltp)
goto free_mem_out;
memset(ltp, 0, sizeof(struct termios));
}
if (driver->type == TTY_DRIVER_TYPE_PTY) {
伪终端时,进行一些特殊的处理
........
}
/*
* All structures have been allocated, so now we install them.
* Failures after this point use release_mem to clean up, so
* there's no need to null out the local pointers.
*/
driver->table[idx] = tty;
将新创建的tty_struct结构“安装”在其所属tty_driver结构的table[]数组中的相应位置
if (!*tp_loc)
*tp_loc = tp;
if (!*ltp_loc)
*ltp_loc = ltp;
tty->termios = *tp_loc;
tty->termios_locked = *ltp_loc;
安装新创建的termios结构
(*driver->refcount)++;
tty->count++;
/*
* Structures all installed ... call the ldisc open routines.
* If we fail here just call release_mem to clean up. No need
* to decrement the use counts, as release_mem doesn't care.
*/
if (tty->ldisc.open) {
retval = (tty->ldisc.open)(tty);
if (retval)
不管是哪一种终端设备,开始时总是采用与下标N_TTY对应的tty_ldisc结构。源码如下:
struct tty_ldisc tty_ldisc_N_TTY = {
TTY_LDISC_MAGIC, /* magic */
"n_tty", /* name */
0, /* num */
0, /* flags */
n_tty_open, /* open */
n_tty_close, /* close */
n_tty_flush_buffer, /* flush_buffer */
n_tty_chars_in_buffer, /* chars_in_buffer */
read_chan, /* read */
write_chan, /* write */
n_tty_ioctl, /* ioctl */
n_tty_set_termios, /* set_termios */
normal_poll, /* poll */
n_tty_receive_buf, /* receive_buf */
n_tty_receive_room, /* receive_room */
n_tty_write_wakeup /* write_wakeup */
};
n_tty_open函数源码:
static int n_tty_open(struct tty_struct *tty)
{
if (!tty)
return -EINVAL;
if (!tty->read_buf) {
tty->read_buf = alloc_buf();
if (!tty->read_buf)
return -ENOMEM;
}分配read_buf缓冲区
memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
reset_buffer_flags(tty);设置一些缓冲区需使用的标志等等
tty->column = 0;
n_tty_set_termios(tty, 0);根据终端设备的termios数据结构设置其tty_struct结构中的字符位图process_char_map和其他几个标志。
tty->minimum_to_wake = 1;
tty->closing = 0;
return 0;
}
回到tty_open中
.......
#ifdef CONFIG_UNIX98_PTYS
init_dev_done:
#endif
filp->private_data = tty;
用file结构中的private_data指针指向目标设备的tty_struct数据结构,从当前进程到目标终端设备的连接就建立起来了。
file_move(filp, &tty->tty_files);
将指向该终端设备的file结构挂入到tty_struct结构中的tty_files队列中。
check_tty_count(tty, "tty_open");
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER)
noctty = 1;
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "opening %s...", tty_name(tty, buf));
#endif
if (tty->driver.open)
retval = tty->driver.open(tty, filp);
具体终端设备的open函数
else
retval = -ENODEV;
filp->f_flags = saved_flags;
.........
return 0;
}