/********************************************************/
int tty_open(struct inode *inode, struct file *filp);
1.分配tty_file具体的数据,并加入到common 数据结构file
int tty_alloc_file(struct file *file)
{
struct tty_file_private *priv;
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
file->private_data = priv;
return 0;
}
crash> struct file | grep private_data
void *private_data;
crash> tty_file_private
struct tty_file_private {
struct tty_struct *tty;
struct file *file;
struct list_head list;
}
2. 得到tty_struct
2.1 tty_open_current_tty(device, filp);
这里的device来自dev_t device = inode->i_rdev;
crash> inode | grep i_rdev
dev_t i_rdev;
/*判断打开设备文件的dev_t是否为MKDEV(TTYAUX_MAJOR, 0),即/dev/tty
*如果是则得到当前进程的tty_struct,即tty终端
*/
/*从当前进程得到对应的 tty
get_current_tty -> tty_kref_get(current->signal->tty)
crash> task_struct | grep signal
struct signal_struct *signal;
crash> signal_struct | grep tty
struct tty_struct *tty;
*/
static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
struct tty_struct *tty;
if (device != MKDEV(TTYAUX_MAJOR, 0))
return NULL;
tty = get_current_tty();
if (!tty)
return ERR_PTR(-ENXIO);
filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
/* noctty = 1; */
tty_kref_put(tty);
/* FIXME: we put a reference and return a TTY! */
return tty;
}
2.2 如果打开的设备文件不是/dev/tty:
2.2.1 通过函数tty_lookup_driver得到设备文件对应的 tty_driver;
/*根据设备文件的类型得到对应的tty_driver
dev/console:
tty_init -> vty_init:
struct tty_driver *console_driver;
console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
console_driver->name = "tty";
console_driver->name_base = 1;
console_driver->major = TTY_MAJOR;
console_driver->minor_start = 1;
tty_set_operations(console_driver, &con_ops);
tty_register_driver(console_driver);
dev/tty:
从console 得到tty_driver:通过函数uart_register_driver(&serial8250_reg);建立了
console 和 tty_driver的关系
struct tty_driver *console_device(int *index)
{
struct console *c;
struct tty_driver *driver = NULL;
console_lock();
for_each_console(c) {
if (!c->device)
continue;
driver = c->device(c, index);
if (driver)
break;
}
console_unlock();
return driver;
}
crash> console_drivers
console_drivers = $5 = (struct console *) 0xc05de228 <serial8250_console>
crash> struct console 0xc05de228
struct console {
name = "ttyS\000\000\000\000\000\000\000\000\000\000\000",
write = 0xc01f06f8 <serial8250_console_write>,
read = 0x0,
device = 0xc01eafc8 <uart_console_device>,
unblank = 0x0,
setup = 0xc059f7e8 <serial8250_console_setup>,
early_setup = 0xc01ee838 <serial8250_console_early_setup>,
flags = 23,
index = 0,
cflag = 0,
data = 0xc05de198 <serial8250_reg>,
next = 0x0
}
怎样从console 得到 tty_driver?
struct tty_driver *uart_console_device(struct console *co, int *index)
{
struct uart_driver *p = co->data;
*index = co->index;
return p->tty_driver;
}
crash> serial8250_reg
serial8250_reg = $6 = {
owner = 0x0,
driver_name = 0xc05407c1 "serial",
dev_name = 0xc050ef55 "ttyS",
major = 4,
minor = 64,
nr = 4,
cons = 0xc05de228 <serial8250_console>,
state = 0xed8ad400,
tty_driver = 0xee0a3200
}
default:
tty_drivers是双向链表的head,每个tty_driver都在这个链表中,使用dev_t和 tty_drivers链表中
的每一项比较,如果设备文件的dev_t在某个tty_driver的dev_t范围内,在找到对应的tty_driver和设备对应的index
static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
struct tty_driver *p;
list_for_each_entry(p, &tty_drivers, tty_drivers) {
dev_t base = MKDEV(p->major, p->minor_start);
if (device < base || device >= base + p->num)
continue;
*index = device - base;
return tty_driver_kref_get(p);
}
return NULL;
}
*/
tty_driver driver = tty_lookup_driver(device, filp, &noctty, &index);
static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
int *noctty, int *index)
{
struct tty_driver *driver;
switch (device) {
case MKDEV(TTY_MAJOR, 0):
driver = console_driver;
case MKDEV(TTYAUX_MAJOR, 1):
driver = console_device(index);
default:
driver = get_tty_driver(device, index);
return driver;
}
2.2.2 check whether we're reopening an existing tty
/*如果函数tty_driver_lookup_tty能得到tty,说明tty已经创建,这次是reopen.
否则使用函数tty_init_dev新创建一个tty_struct.
怎样从tty_driver得到对应的tty?
static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
struct inode *inode, int idx)
{
return driver->ttys[idx];
}
怎样创建一个新的tty?函数tty_init_dev比较复杂,后面详细分析
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
tty = alloc_tty_struct();
initialize_tty_struct(tty, driver, idx);
tty_driver_install_tty(driver, tty);
tty_ldisc_setup(tty, tty->link);
*/
tty = tty_driver_lookup_tty(driver, inode, index);
if (tty) retval = tty_reopen(tty);
else
tty = tty_init_dev(driver, index);
2.3 Associate a new file with the tty structure
tty_add_file(tty, filp);
/* Associate a new file with the tty structure */
void tty_add_file(struct tty_struct *tty, struct file *file)
{
struct tty_file_private *priv = file->private_data;
priv->tty = tty;
priv->file = file;
spin_lock(&tty_files_lock);
list_add(&priv->list, &tty->tty_files);
spin_unlock(&tty_files_lock);
}
2.4 调用tty_driver中注册的 tty_operation的 open函数
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
2.5 如果return value 错误,进行错误处理
使当前进程睡眠,唤醒后重新open等
if (retval) {
tty_unlock(); /* need to call tty_release without BTM */
tty_release(inode, filp);
if (retval != -ERESTARTSYS)
return retval;
if (signal_pending(current))
return retval;
schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
tty_lock();
if (filp->f_op == &hung_up_tty_fops)
filp->f_op = &tty_fops;
tty_unlock();
goto retry_open;
}
2.6 设置当前进程的tty
如果tty_driver是pty 且是 master,则设置noctty
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
noctty = 1;
if (!noctty &&
current->signal->leader &&
!current->signal->tty && tty->session == NULL)
__proc_set_tty(current, tty);
void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
{
if (tty) {
unsigned long flags;
/* We should not have a session or pgrp to put here but.... */
spin_lock_irqsave(&tty->ctrl_lock, flags);
put_pid(tty->session);
put_pid(tty->pgrp);
tty->pgrp = get_pid(task_pgrp(tsk));
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
tty->session = get_pid(task_session(tsk));
if (tsk->signal->tty) {
printk(KERN_DEBUG "tty not NULL!!\n");
tty_kref_put(tsk->signal->tty);
}
}
put_pid(tsk->signal->tty_old_pgrp);
tsk->signal->tty = tty_kref_get(tty);
tsk->signal->tty_old_pgrp = NULL;
}
3. 关于tty_init_dev
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
tty = alloc_tty_struct();
initialize_tty_struct(tty, driver, idx);
tty_driver_install_tty(driver, tty);
tty_ldisc_setup(tty, tty->link);
}
3.1 为tty_struct分配memory并初始化为0
struct tty_struct *tty;
tty = alloc_tty_struct();
struct tty_struct *alloc_tty_struct(void)
{
return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
}
3.2 initialize_tty_struct
void initialize_tty_struct(struct tty_struct *tty,
struct tty_driver *driver, int idx)
{
kref_init(&tty->kref);
/*line discipline*/
tty_ldisc_init(tty);
/*wait queue*/
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
/*mutext*/
mutex_init(&tty->termios_mutex);
mutex_init(&tty->ldisc_mutex);
mutex_init(&tty->atomic_read_lock);
mutex_init(&tty->atomic_write_lock);
mutex_init(&tty->output_lock);
mutex_init(&tty->echo_lock);
/*spin lock*/
spin_lock_init(&tty->read_lock);
spin_lock_init(&tty->ctrl_lock);
/*work*/
INIT_WORK(&tty->hangup_work, do_tty_hangup);
INIT_WORK(&tty->SAK_work, do_SAK_work);
/*list header*/
INIT_LIST_HEAD(&tty->tty_files);
/*memory?*/
tty_buffer_init(tty);
/*设备驱动模型*/
tty->dev = tty_get_device(tty);
/*others*/
tty->magic = TTY_MAGIC;
tty->session = NULL;
tty->pgrp = NULL;
tty->overrun_time = jiffies;
tty->driver = driver;
tty->ops = driver->ops;
tty->index = idx;
tty_line_name(driver, idx, tty->name);
}
3.2.1 读buffer的管理
/*prepare a tty buffer structure
*Set up the initial state of the buffer management for a tty device.
*/
void tty_buffer_init(struct tty_struct *tty)
{
spin_lock_init(&tty->buf.lock);
tty->buf.head = NULL;
tty->buf.tail = NULL;
tty->buf.free = NULL;
tty->buf.memory_used = 0;
INIT_WORK(&tty->buf.work, flush_to_ldisc);
}
crash> tty_struct | grep buf
struct tty_bufhead buf;
crash> struct tty_bufhead
struct tty_bufhead {
struct work_struct work;
spinlock_t lock;
struct tty_buffer *head;
struct tty_buffer *tail;
struct tty_buffer *free;
int memory_used;
}
crash> tty_buffer
struct tty_buffer {
struct tty_buffer *next;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int used;
int size;
int commit;
int read;
unsigned long data[];
}
3.2.2 有关work
INIT_WORK(&tty->hangup_work, do_tty_hangup);
INIT_WORK(&tty->SAK_work, do_SAK_work);
3.2.3 tty_ldisc_init
有关tty_ldisc有两个数据结构tty_ldisc_ops and tty_ldisc,从数组tty_ldiscs中得到tty_ldisc_ops,
得到tty_ldisc_ops后创建tty_ldisc
void tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty_ldisc_assign(tty, ld);
}
struct tty_ldisc {
struct tty_ldisc_ops *ops;
atomic_t users;
};
struct tty_ldisc_ops *get_ldops(int disc) -> ldops = tty_ldiscs[disc];
crash> tty_ldiscs
tty_ldiscs = $7 =
{0xc05daea4 <tty_ldisc_N_TTY>, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0}
static struct tty_ldisc *tty_ldisc_get(int disc)
{
struct tty_ldisc *ld;
struct tty_ldisc_ops *ldops;
ldops = get_ldops(disc);
ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
ld->ops = ldops;
atomic_set(&ld->users, 1);
return ld;
}
static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
{
tty->ldisc = ld;
}
3.3 tty_driver_install_tty/*install a tty entry in the driver*/
static int tty_driver_install_tty(struct tty_driver *driver,
struct tty_struct *tty)
{
return driver->ops->install ? driver->ops->install(driver, tty) :
tty_standard_install(driver, tty);
}
int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
{
driver->ttys[tty->index] = tty;
return 0;
}
3.4 tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty);
/*@o_tty: pair tty for pty/tty pairs,虚拟设备pty是成对的*/
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
struct tty_ldisc *ld = tty->ldisc;
int retval;
retval = tty_ldisc_open(tty, ld);
if (retval)
return retval;
if (o_tty) {
retval = tty_ldisc_open(o_tty, o_tty->ldisc);
if (retval) {
tty_ldisc_close(tty, ld);
return retval;
}
tty_ldisc_enable(o_tty);
}
tty_ldisc_enable(tty);
return 0;
}
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
{
if (ld->ops->open) {
int ret;
/* BTM here locks versus a hangup event */
ret = ld->ops->open(tty);
}
return 0;
}
/*这里分配的大小为N_TTY_BUF_SIZE的read_buf 和tty_buffer什么关系
*read_buf是在n_tty中分配的,但是在tty_io中使用。
*/
static int n_tty_open(struct tty_struct *tty)
{
/* These are ugly. Currently a malloc failure here can panic */
if (!tty->read_buf) {
tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
}
if (!tty->echo_buf) {
tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
}
reset_buffer_flags(tty);
tty_unthrottle(tty);
tty->column = 0;
n_tty_set_termios(tty, NULL);
tty->minimum_to_wake = 1;
tty->closing = 0;
return 0;
}
/* tty_struct中与read有关的成员
char *read_buf;
int read_head;
int read_tail;
int read_cnt;
read_flags是个挺大的数组
crash> tty_struct | grep read_flags
unsigned long read_flags[128];
unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
*/
static void reset_buffer_flags(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_head = tty->read_tail = tty->read_cnt = 0;
spin_unlock_irqrestore(&tty->read_lock, flags);
mutex_lock(&tty->echo_lock);
tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0;
mutex_unlock(&tty->echo_lock);
tty->canon_head = tty->canon_data = tty->erasing = 0;
memset(&tty->read_flags, 0, sizeof tty->read_flags);
n_tty_set_room(tty);
}
/*n_tty_set_room函数是read操作的主要函数,
*注意这里的read_cnt and receive_room
*/
static void n_tty_set_room(struct tty_struct *tty)
{
/* tty->read_cnt is not read locked ? */
int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
int old_left;
/*
* If we are doing input canonicalization, and there are no
* pending newlines, let characters through without limit, so
* that erase characters will be handled. Other excess
* characters will be beeped.
*/
if (left <= 0)
left = tty->icanon && !tty->canon_data;
old_left = tty->receive_room;
tty->receive_room = left;
/* Did this open up the receive buffer? We may need to flip */
if (left && !old_left)
schedule_work(&tty->buf.work);
}
4 tty_write
/*tty_write入口参数检查,如果没有 tty->ops->write这返回ioerror
*write_room method好像也是不需的
*使用ldisc_ops锁,调用do_tty_write
*/
ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty->driver->name);
ld = tty_ldisc_ref_wait(tty);
if (!ld->ops->write)
ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}
4.1 do_tty_write
/*tty->write_cnt初始化为0,所以开始时tty->write_cnt肯定小于2048
*所以这里肯定分配memory, 且赋值
*tty->write_cnt = chunk; write_cnt就这点用途,并不是用来标识写了多大的数据
*and tty->write_buf = buf_chunk;不管用户请求写入的数据多大,总是使用这么大的buffer慢慢传
*/
ssize_t do_tty_write(
ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const char __user *buf,
size_t count)
{
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
chunk = 65536;
if (count < chunk)
chunk = count;
/**/
if (tty->write_cnt < chunk) {
unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
buf_chunk = kmalloc(chunk, GFP_KERNEL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
/* Do the write .. */
for (;;) {
size_t size = count;
if (size > chunk)
size = chunk;
ret = -EFAULT;
if (copy_from_user(tty->write_buf, buf, size))
break;
ret = write(tty, file, tty->write_buf, size);
if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
if (signal_pending(current))
break;
cond_resched();
}
}
4.2 n_tty_write
/*调用tty具体的或者是tty_driver具体的 write 函数
*如果write函数的返回值等于0,则跳出 while(nr>0)循环
*如果此时请求的数据都传输完毕,则跳出 while(1)循环
*如果此时请求的数据没有传输完毕,则当前进程睡眠,重新调度schedule()。
*就是说请求底层的驱动发送nr个数据,却发送了0个或者说没有发送,所以此时当前进程选择睡眠。
*当函数wake_up_interruptible(&tty->write_wait);被调用后,该进程恢复运行
*/
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
ssize_t retval = 0;
add_wait_queue(&tty->write_wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
while (nr > 0) {
c = tty->ops->write(tty, b, nr);
if (c < 0) {
retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
if (b - buf != nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return (b - buf) ? b - buf : retval;
}
5. tty_read
/*调用line descipline的read函数,输入参数为已知的tty, file,user buf and size
**/
ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
if (tty_paranoia_check(tty, inode, "tty_read"))
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);
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;
}
5.1 n_tty_read
/*这里涉及icanon ICANON
*启用标准模式 (canonical mode)。允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS,
*和 WERASE,以及按行的缓冲
*#define MIN_CHAR(tty) ((tty)->termios->c_cc[VMIN]): VMIN 非 canonical 模式读的最小字符数。
*1]如果read_cnt >= 1,则调用函数copy_from_read_buf拷贝read_buf中的数据到user buffer
*2]如果n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE, read_buf读到的数据 <= xx, 就调用n_tty_set_room
* 触发一次tty_buf到read_buf的copy;
*3]如果read_cnt的数据==0,则当前进程睡眠,使用函数schedule_timeout(timeout)调度。
*/
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
unsigned char __user *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
int minimum, time;
ssize_t retval = 0;
ssize_t size;
long timeout;
unsigned long flags;
int packet;
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
if (!tty->icanon) {
time = (HZ / 10) * TIME_CHAR(tty);
minimum = MIN_CHAR(tty);
timeout = 0;
if (time) {
timeout = time;
time = 0;
}
tty->minimum_to_wake = minimum = 1;
}
packet = tty->packet;
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
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)) {
/* FIXME: does n_tty_set_room need locking ? */
n_tty_set_room(tty);
timeout = schedule_timeout(timeout);
BUG_ON(!tty->read_buf);
continue;
}
__set_current_state(TASK_RUNNING);
{
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 (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;
}
remove_wait_queue(&tty->read_wait, &wait);
__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;
}
5.1.1 input_available_p
input_available_p(tty, 0);
/*等待read_cnt >=1, 否则会睡眠*/
static inline int input_available_p(struct tty_struct *tty, int amt)
{
tty_flush_to_ldisc(tty);
if (tty->icanon && !L_EXTPROC(tty)) {
if (tty->canon_data)
return 1;
} else if (tty->read_cnt >= (amt ? amt : 1))
return 1;
return 0;
}
/*Push the terminal flip buffers to the line discipline.
*哪里看到了那个filp buffer?
*/
void tty_flush_to_ldisc(struct tty_struct *tty)
{
flush_work(&tty->buf.work);
}
/*about the work, 这个函数会触发work的执行?
*
*/
bool flush_work(struct work_struct *work)
{
struct wq_barrier barr;
if (start_flush_work(work, &barr, true)) {
wait_for_completion(&barr.done);
destroy_work_on_stack(&barr.work);
return true;
} else
return false;
}
/*about the work, 这个函数会触发work的执行?
*test_and_set_bit(TTY_FLUSHING, &tty->flags); 设置某值的第几位,并返回原来的值;
*clear/set_bit(TTY_FLUSHING, &tty->flags);
描述read buffer相关的数据结构
*crash> tty_struct | grep buf
struct tty_bufhead buf;
*struct tty_bufhead {
struct work_struct work;
spinlock_t lock;
struct tty_buffer *head;
struct tty_buffer *tail;
struct tty_buffer *free;
int memory_used;
}
struct tty_buffer {
struct tty_buffer *next;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int used;
int size;
int commit;
int read;
unsigned long data[];
}
*/
void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty = container_of(work, struct tty_struct, buf.work);
unsigned long flags;
struct tty_ldisc *disc = tty_ldisc_ref(tty);
if (disc == NULL) /* !TTY_LDISC */
return;
if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
struct tty_buffer *head;
while ((head = tty->buf.head) != NULL) {
int count;
char *char_buf;
unsigned char *flag_buf;
/*根据tty_buffer的commit[还没读出的位置] and read[已经读出的位置]
*如果commit等于read, 则移动tty_buf, 释放tty_buf的内存
*/
count = head->commit - head->read;
if (!count) {
if (head->next == NULL)
break;
tty->buf.head = head->next;
tty_buffer_free(tty, head);
continue;
}
/*此时找到一个commit > read的tty_buffer
*receive_room表示readbuffer中还有多少空间
*/
if (!tty->receive_room)
break;
if (count > tty->receive_room)
count = tty->receive_room;
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
/*该函数肯定要释放receive_room*/
disc->ops->receive_buf(tty, char_buf,flag_buf, count);
}
clear_bit(TTY_FLUSHING, &tty->flags);
}
tty_ldisc_deref(disc);
}
/*描述read buffer的数据成员:read_buffer的基地址,读位置,读到的数据
*crash> tty_struct | grep read_buf
* char *read_buf;
* crash> tty_struct | grep read_head
int read_head;
*对循环 buffer:char *read_buf的操作, 把数据从 tty_buf拷贝到read_buf中
*拷贝数据后调用wake_up_interruptible(&tty->read_wait)唤醒睡眠在该等待队列上的进程
*/
void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
spin_lock_irqsave(&tty->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - tty->read_cnt, N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
}
n_tty_set_room(tty);
if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
}
/*
* Check the remaining room for the input canonicalization
* mode. We don't want to throttle the driver if we're in
* canonical mode and don't have a newline yet!
*/
if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
tty_throttle(tty);
}
/*更改read_cnt后,或者说从tty_buf拷贝数据到read_buf后,read_cnt发生变化,
*调用该函数后更改receiver_room,如果前后的receive_room不等,则调用schedule_work(&tty->buf.work)
*这里可以看出flush_to_ldisc本身也可能flush_to_ldisc,注意这是才能看到flush_to_ldisc函数中
test_and_set_bit(TTY_FLUSHING, &tty->flags)的用途,如果嵌套将直接返回
**/
static void n_tty_set_room(struct tty_struct *tty)
{
/* tty->read_cnt is not read locked ? */
int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
int old_left;
old_left = tty->receive_room;
tty->receive_room = left;
/* Did this open up the receive buffer? We may need to flip */
if (left && !old_left)
schedule_work(&tty->buf.work);
}
/*从read buf拷贝数据到user buf: 使用的数据成员是read_tail
*从read_tail开始处拷贝数据到用户空间
**/
5.1.2 static int copy_from_read_buf(struct tty_struct *tty,
unsigned char __user **b,
size_t *nr)
{
int retval;
size_t n;
unsigned long flags;
retval = 0;
spin_lock_irqsave(&tty->read_lock, flags);
n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
n = min(*nr, n);
spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
/* Turn single EOF into zero-length read */
if (L_EXTPROC(tty) && tty->icanon && n == 1) {
if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
n--;
}
spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
return retval;
}
5.2 数据是怎样到tty_buffer的?
/*在中断处理程序中,根据实际的需求,申请memory即tty_buf, 然后调用tty_flip_buffer_push
*触发flush_to_ldisc
**/
/* tty_prepare_flip_string -make room for characters */
int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
size_t size)
{
int space = tty_buffer_request_room(tty, size);
if (likely(space)) {
struct tty_buffer *tb = tty->buf.tail;
*chars = tb->char_buf_ptr + tb->used;
memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
tb->used += space;
}
return space;
}
/*Queue a push of the terminal flip buffers to the line discipline.
*最终调用flush_to_ldisc,虽然从名字上看应该是copy tty_buf的数据到line discipline的某个变量出,
*而实际上copy的tty的read_buf内,看上去有点令人费解。而如果知道read_buf的分配是在函数n_tty_open
*可理解为tty_buf到n_tty分配的内存。
*tty_buf的内存是在中断处理函数中根据需要分配的,管理形式是list,而
*read_buf是循环buffer的使用方式。
**/
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work);
else
schedule_work(&tty->buf.work);
}
/*假象这种情形:用户程序调用read读取大小为 4字节的数据,此时没有数据。当期进程堵塞在【tty->read_wait】 wait_head上。
*当有数据收到后,在中断处理函数中分配tty_buf,并调用flush_to_ldisc函数把数据从tty_buf拷贝到read_buf中,并调用函数
*wake_up_interruptible(&tty->read_wait)唤醒睡眠的进程,把数据从read_buf拷贝到user buf中。
* flush_to_ldisc->disc->ops->receive_buf();
*/
int tty_open(struct inode *inode, struct file *filp);
1.分配tty_file具体的数据,并加入到common 数据结构file
int tty_alloc_file(struct file *file)
{
struct tty_file_private *priv;
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
file->private_data = priv;
return 0;
}
crash> struct file | grep private_data
void *private_data;
crash> tty_file_private
struct tty_file_private {
struct tty_struct *tty;
struct file *file;
struct list_head list;
}
2. 得到tty_struct
2.1 tty_open_current_tty(device, filp);
这里的device来自dev_t device = inode->i_rdev;
crash> inode | grep i_rdev
dev_t i_rdev;
/*判断打开设备文件的dev_t是否为MKDEV(TTYAUX_MAJOR, 0),即/dev/tty
*如果是则得到当前进程的tty_struct,即tty终端
*/
/*从当前进程得到对应的 tty
get_current_tty -> tty_kref_get(current->signal->tty)
crash> task_struct | grep signal
struct signal_struct *signal;
crash> signal_struct | grep tty
struct tty_struct *tty;
*/
static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
struct tty_struct *tty;
if (device != MKDEV(TTYAUX_MAJOR, 0))
return NULL;
tty = get_current_tty();
if (!tty)
return ERR_PTR(-ENXIO);
filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
/* noctty = 1; */
tty_kref_put(tty);
/* FIXME: we put a reference and return a TTY! */
return tty;
}
2.2 如果打开的设备文件不是/dev/tty:
2.2.1 通过函数tty_lookup_driver得到设备文件对应的 tty_driver;
/*根据设备文件的类型得到对应的tty_driver
dev/console:
tty_init -> vty_init:
struct tty_driver *console_driver;
console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
console_driver->name = "tty";
console_driver->name_base = 1;
console_driver->major = TTY_MAJOR;
console_driver->minor_start = 1;
tty_set_operations(console_driver, &con_ops);
tty_register_driver(console_driver);
dev/tty:
从console 得到tty_driver:通过函数uart_register_driver(&serial8250_reg);建立了
console 和 tty_driver的关系
struct tty_driver *console_device(int *index)
{
struct console *c;
struct tty_driver *driver = NULL;
console_lock();
for_each_console(c) {
if (!c->device)
continue;
driver = c->device(c, index);
if (driver)
break;
}
console_unlock();
return driver;
}
crash> console_drivers
console_drivers = $5 = (struct console *) 0xc05de228 <serial8250_console>
crash> struct console 0xc05de228
struct console {
name = "ttyS\000\000\000\000\000\000\000\000\000\000\000",
write = 0xc01f06f8 <serial8250_console_write>,
read = 0x0,
device = 0xc01eafc8 <uart_console_device>,
unblank = 0x0,
setup = 0xc059f7e8 <serial8250_console_setup>,
early_setup = 0xc01ee838 <serial8250_console_early_setup>,
flags = 23,
index = 0,
cflag = 0,
data = 0xc05de198 <serial8250_reg>,
next = 0x0
}
怎样从console 得到 tty_driver?
struct tty_driver *uart_console_device(struct console *co, int *index)
{
struct uart_driver *p = co->data;
*index = co->index;
return p->tty_driver;
}
crash> serial8250_reg
serial8250_reg = $6 = {
owner = 0x0,
driver_name = 0xc05407c1 "serial",
dev_name = 0xc050ef55 "ttyS",
major = 4,
minor = 64,
nr = 4,
cons = 0xc05de228 <serial8250_console>,
state = 0xed8ad400,
tty_driver = 0xee0a3200
}
default:
tty_drivers是双向链表的head,每个tty_driver都在这个链表中,使用dev_t和 tty_drivers链表中
的每一项比较,如果设备文件的dev_t在某个tty_driver的dev_t范围内,在找到对应的tty_driver和设备对应的index
static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
struct tty_driver *p;
list_for_each_entry(p, &tty_drivers, tty_drivers) {
dev_t base = MKDEV(p->major, p->minor_start);
if (device < base || device >= base + p->num)
continue;
*index = device - base;
return tty_driver_kref_get(p);
}
return NULL;
}
*/
tty_driver driver = tty_lookup_driver(device, filp, &noctty, &index);
static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
int *noctty, int *index)
{
struct tty_driver *driver;
switch (device) {
case MKDEV(TTY_MAJOR, 0):
driver = console_driver;
case MKDEV(TTYAUX_MAJOR, 1):
driver = console_device(index);
default:
driver = get_tty_driver(device, index);
return driver;
}
2.2.2 check whether we're reopening an existing tty
/*如果函数tty_driver_lookup_tty能得到tty,说明tty已经创建,这次是reopen.
否则使用函数tty_init_dev新创建一个tty_struct.
怎样从tty_driver得到对应的tty?
static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
struct inode *inode, int idx)
{
return driver->ttys[idx];
}
怎样创建一个新的tty?函数tty_init_dev比较复杂,后面详细分析
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
tty = alloc_tty_struct();
initialize_tty_struct(tty, driver, idx);
tty_driver_install_tty(driver, tty);
tty_ldisc_setup(tty, tty->link);
*/
tty = tty_driver_lookup_tty(driver, inode, index);
if (tty) retval = tty_reopen(tty);
else
tty = tty_init_dev(driver, index);
2.3 Associate a new file with the tty structure
tty_add_file(tty, filp);
/* Associate a new file with the tty structure */
void tty_add_file(struct tty_struct *tty, struct file *file)
{
struct tty_file_private *priv = file->private_data;
priv->tty = tty;
priv->file = file;
spin_lock(&tty_files_lock);
list_add(&priv->list, &tty->tty_files);
spin_unlock(&tty_files_lock);
}
2.4 调用tty_driver中注册的 tty_operation的 open函数
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
2.5 如果return value 错误,进行错误处理
使当前进程睡眠,唤醒后重新open等
if (retval) {
tty_unlock(); /* need to call tty_release without BTM */
tty_release(inode, filp);
if (retval != -ERESTARTSYS)
return retval;
if (signal_pending(current))
return retval;
schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
tty_lock();
if (filp->f_op == &hung_up_tty_fops)
filp->f_op = &tty_fops;
tty_unlock();
goto retry_open;
}
2.6 设置当前进程的tty
如果tty_driver是pty 且是 master,则设置noctty
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
noctty = 1;
if (!noctty &&
current->signal->leader &&
!current->signal->tty && tty->session == NULL)
__proc_set_tty(current, tty);
void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
{
if (tty) {
unsigned long flags;
/* We should not have a session or pgrp to put here but.... */
spin_lock_irqsave(&tty->ctrl_lock, flags);
put_pid(tty->session);
put_pid(tty->pgrp);
tty->pgrp = get_pid(task_pgrp(tsk));
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
tty->session = get_pid(task_session(tsk));
if (tsk->signal->tty) {
printk(KERN_DEBUG "tty not NULL!!\n");
tty_kref_put(tsk->signal->tty);
}
}
put_pid(tsk->signal->tty_old_pgrp);
tsk->signal->tty = tty_kref_get(tty);
tsk->signal->tty_old_pgrp = NULL;
}
3. 关于tty_init_dev
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
tty = alloc_tty_struct();
initialize_tty_struct(tty, driver, idx);
tty_driver_install_tty(driver, tty);
tty_ldisc_setup(tty, tty->link);
}
3.1 为tty_struct分配memory并初始化为0
struct tty_struct *tty;
tty = alloc_tty_struct();
struct tty_struct *alloc_tty_struct(void)
{
return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
}
3.2 initialize_tty_struct
void initialize_tty_struct(struct tty_struct *tty,
struct tty_driver *driver, int idx)
{
kref_init(&tty->kref);
/*line discipline*/
tty_ldisc_init(tty);
/*wait queue*/
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
/*mutext*/
mutex_init(&tty->termios_mutex);
mutex_init(&tty->ldisc_mutex);
mutex_init(&tty->atomic_read_lock);
mutex_init(&tty->atomic_write_lock);
mutex_init(&tty->output_lock);
mutex_init(&tty->echo_lock);
/*spin lock*/
spin_lock_init(&tty->read_lock);
spin_lock_init(&tty->ctrl_lock);
/*work*/
INIT_WORK(&tty->hangup_work, do_tty_hangup);
INIT_WORK(&tty->SAK_work, do_SAK_work);
/*list header*/
INIT_LIST_HEAD(&tty->tty_files);
/*memory?*/
tty_buffer_init(tty);
/*设备驱动模型*/
tty->dev = tty_get_device(tty);
/*others*/
tty->magic = TTY_MAGIC;
tty->session = NULL;
tty->pgrp = NULL;
tty->overrun_time = jiffies;
tty->driver = driver;
tty->ops = driver->ops;
tty->index = idx;
tty_line_name(driver, idx, tty->name);
}
3.2.1 读buffer的管理
/*prepare a tty buffer structure
*Set up the initial state of the buffer management for a tty device.
*/
void tty_buffer_init(struct tty_struct *tty)
{
spin_lock_init(&tty->buf.lock);
tty->buf.head = NULL;
tty->buf.tail = NULL;
tty->buf.free = NULL;
tty->buf.memory_used = 0;
INIT_WORK(&tty->buf.work, flush_to_ldisc);
}
crash> tty_struct | grep buf
struct tty_bufhead buf;
crash> struct tty_bufhead
struct tty_bufhead {
struct work_struct work;
spinlock_t lock;
struct tty_buffer *head;
struct tty_buffer *tail;
struct tty_buffer *free;
int memory_used;
}
crash> tty_buffer
struct tty_buffer {
struct tty_buffer *next;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int used;
int size;
int commit;
int read;
unsigned long data[];
}
3.2.2 有关work
INIT_WORK(&tty->hangup_work, do_tty_hangup);
INIT_WORK(&tty->SAK_work, do_SAK_work);
3.2.3 tty_ldisc_init
有关tty_ldisc有两个数据结构tty_ldisc_ops and tty_ldisc,从数组tty_ldiscs中得到tty_ldisc_ops,
得到tty_ldisc_ops后创建tty_ldisc
void tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty_ldisc_assign(tty, ld);
}
struct tty_ldisc {
struct tty_ldisc_ops *ops;
atomic_t users;
};
struct tty_ldisc_ops *get_ldops(int disc) -> ldops = tty_ldiscs[disc];
crash> tty_ldiscs
tty_ldiscs = $7 =
{0xc05daea4 <tty_ldisc_N_TTY>, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0}
static struct tty_ldisc *tty_ldisc_get(int disc)
{
struct tty_ldisc *ld;
struct tty_ldisc_ops *ldops;
ldops = get_ldops(disc);
ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
ld->ops = ldops;
atomic_set(&ld->users, 1);
return ld;
}
static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
{
tty->ldisc = ld;
}
3.3 tty_driver_install_tty/*install a tty entry in the driver*/
static int tty_driver_install_tty(struct tty_driver *driver,
struct tty_struct *tty)
{
return driver->ops->install ? driver->ops->install(driver, tty) :
tty_standard_install(driver, tty);
}
int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
{
driver->ttys[tty->index] = tty;
return 0;
}
3.4 tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty);
/*@o_tty: pair tty for pty/tty pairs,虚拟设备pty是成对的*/
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
struct tty_ldisc *ld = tty->ldisc;
int retval;
retval = tty_ldisc_open(tty, ld);
if (retval)
return retval;
if (o_tty) {
retval = tty_ldisc_open(o_tty, o_tty->ldisc);
if (retval) {
tty_ldisc_close(tty, ld);
return retval;
}
tty_ldisc_enable(o_tty);
}
tty_ldisc_enable(tty);
return 0;
}
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
{
if (ld->ops->open) {
int ret;
/* BTM here locks versus a hangup event */
ret = ld->ops->open(tty);
}
return 0;
}
/*这里分配的大小为N_TTY_BUF_SIZE的read_buf 和tty_buffer什么关系
*read_buf是在n_tty中分配的,但是在tty_io中使用。
*/
static int n_tty_open(struct tty_struct *tty)
{
/* These are ugly. Currently a malloc failure here can panic */
if (!tty->read_buf) {
tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
}
if (!tty->echo_buf) {
tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
}
reset_buffer_flags(tty);
tty_unthrottle(tty);
tty->column = 0;
n_tty_set_termios(tty, NULL);
tty->minimum_to_wake = 1;
tty->closing = 0;
return 0;
}
/* tty_struct中与read有关的成员
char *read_buf;
int read_head;
int read_tail;
int read_cnt;
read_flags是个挺大的数组
crash> tty_struct | grep read_flags
unsigned long read_flags[128];
unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
*/
static void reset_buffer_flags(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_head = tty->read_tail = tty->read_cnt = 0;
spin_unlock_irqrestore(&tty->read_lock, flags);
mutex_lock(&tty->echo_lock);
tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0;
mutex_unlock(&tty->echo_lock);
tty->canon_head = tty->canon_data = tty->erasing = 0;
memset(&tty->read_flags, 0, sizeof tty->read_flags);
n_tty_set_room(tty);
}
/*n_tty_set_room函数是read操作的主要函数,
*注意这里的read_cnt and receive_room
*/
static void n_tty_set_room(struct tty_struct *tty)
{
/* tty->read_cnt is not read locked ? */
int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
int old_left;
/*
* If we are doing input canonicalization, and there are no
* pending newlines, let characters through without limit, so
* that erase characters will be handled. Other excess
* characters will be beeped.
*/
if (left <= 0)
left = tty->icanon && !tty->canon_data;
old_left = tty->receive_room;
tty->receive_room = left;
/* Did this open up the receive buffer? We may need to flip */
if (left && !old_left)
schedule_work(&tty->buf.work);
}
4 tty_write
/*tty_write入口参数检查,如果没有 tty->ops->write这返回ioerror
*write_room method好像也是不需的
*使用ldisc_ops锁,调用do_tty_write
*/
ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty->driver->name);
ld = tty_ldisc_ref_wait(tty);
if (!ld->ops->write)
ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}
4.1 do_tty_write
/*tty->write_cnt初始化为0,所以开始时tty->write_cnt肯定小于2048
*所以这里肯定分配memory, 且赋值
*tty->write_cnt = chunk; write_cnt就这点用途,并不是用来标识写了多大的数据
*and tty->write_buf = buf_chunk;不管用户请求写入的数据多大,总是使用这么大的buffer慢慢传
*/
ssize_t do_tty_write(
ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const char __user *buf,
size_t count)
{
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
chunk = 65536;
if (count < chunk)
chunk = count;
/**/
if (tty->write_cnt < chunk) {
unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
buf_chunk = kmalloc(chunk, GFP_KERNEL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
/* Do the write .. */
for (;;) {
size_t size = count;
if (size > chunk)
size = chunk;
ret = -EFAULT;
if (copy_from_user(tty->write_buf, buf, size))
break;
ret = write(tty, file, tty->write_buf, size);
if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
if (signal_pending(current))
break;
cond_resched();
}
}
4.2 n_tty_write
/*调用tty具体的或者是tty_driver具体的 write 函数
*如果write函数的返回值等于0,则跳出 while(nr>0)循环
*如果此时请求的数据都传输完毕,则跳出 while(1)循环
*如果此时请求的数据没有传输完毕,则当前进程睡眠,重新调度schedule()。
*就是说请求底层的驱动发送nr个数据,却发送了0个或者说没有发送,所以此时当前进程选择睡眠。
*当函数wake_up_interruptible(&tty->write_wait);被调用后,该进程恢复运行
*/
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
ssize_t retval = 0;
add_wait_queue(&tty->write_wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
while (nr > 0) {
c = tty->ops->write(tty, b, nr);
if (c < 0) {
retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
if (b - buf != nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return (b - buf) ? b - buf : retval;
}
5. tty_read
/*调用line descipline的read函数,输入参数为已知的tty, file,user buf and size
**/
ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
if (tty_paranoia_check(tty, inode, "tty_read"))
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);
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;
}
5.1 n_tty_read
/*这里涉及icanon ICANON
*启用标准模式 (canonical mode)。允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS,
*和 WERASE,以及按行的缓冲
*#define MIN_CHAR(tty) ((tty)->termios->c_cc[VMIN]): VMIN 非 canonical 模式读的最小字符数。
*1]如果read_cnt >= 1,则调用函数copy_from_read_buf拷贝read_buf中的数据到user buffer
*2]如果n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE, read_buf读到的数据 <= xx, 就调用n_tty_set_room
* 触发一次tty_buf到read_buf的copy;
*3]如果read_cnt的数据==0,则当前进程睡眠,使用函数schedule_timeout(timeout)调度。
*/
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
unsigned char __user *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
int minimum, time;
ssize_t retval = 0;
ssize_t size;
long timeout;
unsigned long flags;
int packet;
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
if (!tty->icanon) {
time = (HZ / 10) * TIME_CHAR(tty);
minimum = MIN_CHAR(tty);
timeout = 0;
if (time) {
timeout = time;
time = 0;
}
tty->minimum_to_wake = minimum = 1;
}
packet = tty->packet;
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
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)) {
/* FIXME: does n_tty_set_room need locking ? */
n_tty_set_room(tty);
timeout = schedule_timeout(timeout);
BUG_ON(!tty->read_buf);
continue;
}
__set_current_state(TASK_RUNNING);
{
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 (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;
}
remove_wait_queue(&tty->read_wait, &wait);
__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;
}
5.1.1 input_available_p
input_available_p(tty, 0);
/*等待read_cnt >=1, 否则会睡眠*/
static inline int input_available_p(struct tty_struct *tty, int amt)
{
tty_flush_to_ldisc(tty);
if (tty->icanon && !L_EXTPROC(tty)) {
if (tty->canon_data)
return 1;
} else if (tty->read_cnt >= (amt ? amt : 1))
return 1;
return 0;
}
/*Push the terminal flip buffers to the line discipline.
*哪里看到了那个filp buffer?
*/
void tty_flush_to_ldisc(struct tty_struct *tty)
{
flush_work(&tty->buf.work);
}
/*about the work, 这个函数会触发work的执行?
*
*/
bool flush_work(struct work_struct *work)
{
struct wq_barrier barr;
if (start_flush_work(work, &barr, true)) {
wait_for_completion(&barr.done);
destroy_work_on_stack(&barr.work);
return true;
} else
return false;
}
/*about the work, 这个函数会触发work的执行?
*test_and_set_bit(TTY_FLUSHING, &tty->flags); 设置某值的第几位,并返回原来的值;
*clear/set_bit(TTY_FLUSHING, &tty->flags);
描述read buffer相关的数据结构
*crash> tty_struct | grep buf
struct tty_bufhead buf;
*struct tty_bufhead {
struct work_struct work;
spinlock_t lock;
struct tty_buffer *head;
struct tty_buffer *tail;
struct tty_buffer *free;
int memory_used;
}
struct tty_buffer {
struct tty_buffer *next;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int used;
int size;
int commit;
int read;
unsigned long data[];
}
*/
void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty = container_of(work, struct tty_struct, buf.work);
unsigned long flags;
struct tty_ldisc *disc = tty_ldisc_ref(tty);
if (disc == NULL) /* !TTY_LDISC */
return;
if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
struct tty_buffer *head;
while ((head = tty->buf.head) != NULL) {
int count;
char *char_buf;
unsigned char *flag_buf;
/*根据tty_buffer的commit[还没读出的位置] and read[已经读出的位置]
*如果commit等于read, 则移动tty_buf, 释放tty_buf的内存
*/
count = head->commit - head->read;
if (!count) {
if (head->next == NULL)
break;
tty->buf.head = head->next;
tty_buffer_free(tty, head);
continue;
}
/*此时找到一个commit > read的tty_buffer
*receive_room表示readbuffer中还有多少空间
*/
if (!tty->receive_room)
break;
if (count > tty->receive_room)
count = tty->receive_room;
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
/*该函数肯定要释放receive_room*/
disc->ops->receive_buf(tty, char_buf,flag_buf, count);
}
clear_bit(TTY_FLUSHING, &tty->flags);
}
tty_ldisc_deref(disc);
}
/*描述read buffer的数据成员:read_buffer的基地址,读位置,读到的数据
*crash> tty_struct | grep read_buf
* char *read_buf;
* crash> tty_struct | grep read_head
int read_head;
*对循环 buffer:char *read_buf的操作, 把数据从 tty_buf拷贝到read_buf中
*拷贝数据后调用wake_up_interruptible(&tty->read_wait)唤醒睡眠在该等待队列上的进程
*/
void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
spin_lock_irqsave(&tty->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - tty->read_cnt, N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
}
n_tty_set_room(tty);
if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
}
/*
* Check the remaining room for the input canonicalization
* mode. We don't want to throttle the driver if we're in
* canonical mode and don't have a newline yet!
*/
if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
tty_throttle(tty);
}
/*更改read_cnt后,或者说从tty_buf拷贝数据到read_buf后,read_cnt发生变化,
*调用该函数后更改receiver_room,如果前后的receive_room不等,则调用schedule_work(&tty->buf.work)
*这里可以看出flush_to_ldisc本身也可能flush_to_ldisc,注意这是才能看到flush_to_ldisc函数中
test_and_set_bit(TTY_FLUSHING, &tty->flags)的用途,如果嵌套将直接返回
**/
static void n_tty_set_room(struct tty_struct *tty)
{
/* tty->read_cnt is not read locked ? */
int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
int old_left;
old_left = tty->receive_room;
tty->receive_room = left;
/* Did this open up the receive buffer? We may need to flip */
if (left && !old_left)
schedule_work(&tty->buf.work);
}
/*从read buf拷贝数据到user buf: 使用的数据成员是read_tail
*从read_tail开始处拷贝数据到用户空间
**/
5.1.2 static int copy_from_read_buf(struct tty_struct *tty,
unsigned char __user **b,
size_t *nr)
{
int retval;
size_t n;
unsigned long flags;
retval = 0;
spin_lock_irqsave(&tty->read_lock, flags);
n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
n = min(*nr, n);
spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
/* Turn single EOF into zero-length read */
if (L_EXTPROC(tty) && tty->icanon && n == 1) {
if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
n--;
}
spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
return retval;
}
5.2 数据是怎样到tty_buffer的?
/*在中断处理程序中,根据实际的需求,申请memory即tty_buf, 然后调用tty_flip_buffer_push
*触发flush_to_ldisc
**/
/* tty_prepare_flip_string -make room for characters */
int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
size_t size)
{
int space = tty_buffer_request_room(tty, size);
if (likely(space)) {
struct tty_buffer *tb = tty->buf.tail;
*chars = tb->char_buf_ptr + tb->used;
memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
tb->used += space;
}
return space;
}
/*Queue a push of the terminal flip buffers to the line discipline.
*最终调用flush_to_ldisc,虽然从名字上看应该是copy tty_buf的数据到line discipline的某个变量出,
*而实际上copy的tty的read_buf内,看上去有点令人费解。而如果知道read_buf的分配是在函数n_tty_open
*可理解为tty_buf到n_tty分配的内存。
*tty_buf的内存是在中断处理函数中根据需要分配的,管理形式是list,而
*read_buf是循环buffer的使用方式。
**/
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work);
else
schedule_work(&tty->buf.work);
}
/*假象这种情形:用户程序调用read读取大小为 4字节的数据,此时没有数据。当期进程堵塞在【tty->read_wait】 wait_head上。
*当有数据收到后,在中断处理函数中分配tty_buf,并调用flush_to_ldisc函数把数据从tty_buf拷贝到read_buf中,并调用函数
*wake_up_interruptible(&tty->read_wait)唤醒睡眠的进程,把数据从read_buf拷贝到user buf中。
* flush_to_ldisc->disc->ops->receive_buf();
*/