linux中对/dev/console和/dev/tty的理解

/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(&current->sighand->siglock, flags);
    tty = tty_kref_get(current->signal->tty);  //这个就是当前调用进程的控制终端
    spin_unlock_irqrestore(&current->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对应的驱动

这里只是分析了个大概,重点是为了说明上面的理解,所有中间略去了很多细节,感兴趣的可以对着代码详细分析。

发布了85 篇原创文章 · 获赞 26 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/whuzm08/article/details/104008310
Dev