tty_driver-write
1. echo sdf > /dev/ttyS0
tty_cdev_add
给 tty driver 分配字符设备,设置字符设备的文件操作函数指针指向 tty_fops ,后者的写函数为 tty_write
。
static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
unsigned int index, unsigned int count)
{
...
driver->cdevs[index]->ops = &tty_fops;
...
}
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
.show_fdinfo = tty_show_fdinfo,
};
tty_write
只是做一些检查,保证 line discipline 的写函数需要的组件可用,真正的操作由 do_tty_write
调用 ld->ops->write
完成。
static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
if (tty_paranoia_check(tty, file_inode(file), "tty_write"))
return -EIO;
// tty->ops->write 在 n_tty_write 中使用
if (!tty || !tty->ops->write || tty_io_error(tty))
return -EIO;
/* Short term debug to catch buggy drivers */
// tty->ops->write_room 在 n_tty_write-> process_output_block 中使用
if (tty->ops->write_room == NULL)
tty_err(tty, "missing write_room method\n");
ld = tty_ldisc_ref_wait(tty);
if (!ld)
return hung_up_tty_write(file, buf, count, ppos);
if (!ld->ops->write)
ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}
ld->ops->write
函数在《 tty_driver-line_discipline 》一文介绍。
2. echo sdf > /dev/tty
根据 tty_init
函数, /dev/tty 的文件操作函数指针和 /dev/ttyS0 相同,因此写 /dev/tty 的操作执行的函数和写 /dev/ttyS0 相同。
3. echo sdf > /dev/tty1
/dev/ttyn 设备的文件操作指针也是 tty_fops ,因此向其写入数据时,同样会调用 tty_write
函数。
tty_write
调用 line discipline 的写函数完成输出操作,最终调用 con_write
输出字符串。
4. echo sdf > /dev/console
/dev/console 使用的文件操作函数指针为:
static const struct file_operations console_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = redirected_tty_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
};
因此写操作调用的函数为 redirected_tty_write
:函数首先获取变量 struct file *redirect ,如果文件指针存在,调用 vfs_write
将 buf 中内容输出到 redirect 文件;否则调用 tty_write
完成输出操作。
ssize_t redirected_tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct file *p = NULL;
spin_lock(&redirect_lock);
if (redirect)
p = get_file(redirect);
spin_unlock(&redirect_lock);
if (p) {
ssize_t res;
res = vfs_write(p, buf, count, &p->f_pos);
fput(p);
return res;
}
return tty_write(file, buf, count, ppos);
}
4.1. 控制台重定向 redirect
redirect 定义在 drivers/tty/tty_io.c 中,修改 direct 的函数只有 tioccons
,用于相应 tty ioctl 的 TIOCCONS 命令。
根据 man tty_ioctl
的输出,这个命令的功能为:
重定向控制台输出,将原本要输出到 /dev/console 或者 /dev/tty0 的内容重定向到给定的终端。如果终端是一个伪终端的主设备,将其发送到从设备。
2.6.10 之前内核只要输出没有重定向,任何用户都可以执行这个操作; 2.6.10 版本开始只有 CAP_SYS_ADMIN 进程可以执行这个操作。如果输出已经进行了重定向,返回 EBUSY ;可以通过传入 /dev/console 或者 /dev/tty0 结束重定向。
static struct file *redirect;
static int tioccons(struct file *file)
{
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
// 传入的文件指针是 /dev/console 或者 /dev/tty0 ,结束重定向
if (file->f_op->write == redirected_tty_write) {
struct file *f;
spin_lock(&redirect_lock);
f = redirect;
redirect = NULL;
spin_unlock(&redirect_lock);
if (f)
fput(f);
return 0;
}
spin_lock(&redirect_lock);
// 如果已经设置了重定向,返回 EBUSY
if (redirect) {
spin_unlock(&redirect_lock);
return -EBUSY;
}
// 设置重定向
redirect = get_file(file);
spin_unlock(&redirect_lock);
return 0;
}
作者:glob
出处:http://www.cnblogs.com/adera/
欢迎访问我的个人博客:https://blog.globs.site/
本文版权归作者和博客园共有,转载请注明出处。