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;
}