/dev/console是什么
如果系统中存在多个tty设备,想象一下,这时内核启动的log应该打印在哪里,这时内核会从tty中选择一个最合适的作为console,当然内核启动参数中也可以明确的去指定那个tty作为内核的console,/dev/console主要是暴露给用户空间使用的,主要用于系统管理员在改终端上登陆用。
/dev/tty又是什么
The file /dev/tty is a character file with major number 5 and minor number 0, usually of mode 0666 and owner.group root.tty. It is a synonym for the controlling terminal of a process, if any.
/dev/tty can represent different controlling terminals, without being a link, because the driver which implements it determines what the calling process’ controlling terminal is, if any.
如果当前进程有控制终端(Controlling Terminal)的话,那么/dev/tty就是当前进程的控制终端的设备特殊文件。
下面从内核源码的角度来分析一下具体的实现:
//./drivers/tty/tty_io.c
int __init tty_init(void)
{
cdev_init(&tty_cdev, &tty_fops);
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
panic("Couldn't register /dev/tty driver\n");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
cdev_init(&console_cdev, &console_fops);
if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
panic("Couldn't register /dev/console driver\n");
consdev = device_create_with_groups(tty_class, NULL,
MKDEV(TTYAUX_MAJOR, 1), NULL,
cons_dev_groups, "console");
if (IS_ERR(consdev))
consdev = NULL;
#ifdef CONFIG_VT
vty_init(&console_fops);
#endif
return 0;
}
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,
};
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,
};
可以看出它们的打开函数都是tty_open,下面详细分析一下tty_open()这个函数的实现:
static int tty_open(struct inode *inode, struct file *filp)
{
dev_t device = inode->i_rdev; //得到设备号
...
tty = tty_open_current_tty(device, filp);
if (!tty)
tty = tty_open_by_driver(device, inode, filp);
...
tty_add_file(tty, filp);
...
}
/**
* tty_open_current_tty - get locked tty of current task
* @device: device number
* @filp: file pointer to tty
* @return: locked tty of the current task iff @device is /dev/tty
*
* Performs a re-open of the current task's controlling tty.
*
* We cannot return driver and index like for the other nodes because
* devpts will not work then. It expects inodes to be from devpts FS.
*/
static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
struct tty_struct *tty;
int retval;
if (device != MKDEV(TTYAUX_MAJOR, 0)) //判断打开的是否是/dev/tty
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_lock(tty);
tty_kref_put(tty); /* safe to drop the kref now */
retval = tty_reopen(tty);
if (retval < 0) {
tty_unlock(tty);
tty = ERR_PTR(retval);
}
return tty;
}
struct tty_struct *get_current_tty(void)
{
struct tty_struct *tty;
unsigned long flags;
spin_lock_irqsave(¤t->sighand->siglock, flags);
tty = tty_kref_get(current->signal->tty); //这个就是当前调用进程的控制终端
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
return tty;
}
/* 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; //将关联的tty放在file的私有数据中保存,这样在后面的tty_read中就能调用对应的tty去执行真正的read动作
priv->file = file;
spin_lock(&tty->files_lock);
list_add(&priv->list, &tty->tty_files);
spin_unlock(&tty->files_lock);
}
可以看出/dev/tty打开的就是当前进程的控制终端,所以不同的session通过
echo hello > /dev/tty
总是能在当前控制终端中显示出来
下面来分析/dev/console的打开过程:
static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
struct file *filp)
{
...
mutex_lock(&tty_mutex);
driver = tty_lookup_driver(device, filp, &index);
if (IS_ERR(driver)) {
mutex_unlock(&tty_mutex);
return ERR_CAST(driver);
}
/* check whether we're reopening an existing tty */
tty = tty_driver_lookup_tty(driver, filp, index);
if (IS_ERR(tty)) {
mutex_unlock(&tty_mutex);
goto out;
}
...
}
static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
int *index)
{
...
switch (device) {
case MKDEV(TTYAUX_MAJOR, 1): {
struct tty_driver *console_driver = console_device(index);
if (console_driver) {
driver = tty_driver_kref_get(console_driver);
if (driver) {
/* Don't let /dev/console block */
filp->f_flags |= O_NONBLOCK;
break;
}
}
return ERR_PTR(-ENODEV);
}
}
...
}
/*
* Return the console tty driver structure and its associated index
*/
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;
}
这个函数实际就是返回当前console使用的tty对应的驱动
这里只是分析了个大概,重点是为了说明上面的理解,所有中间略去了很多细节,感兴趣的可以对着代码详细分析。