在tty core代码tty_io.c中,有如下几行代码
/* * Ok, now we can initialize the rest of the tty devices and can count * on memory allocations, interrupts etc.. */ static 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"); device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console"); #ifdef CONFIG_VT vty_init(&console_fops); #endif return 0; }
这里创建了几个system级 终端设备,他们并不是真实的物理设备,每个都会创建这几个设备节点
/dev/tty
设备号(5, 0)是设备/dev/tty,表示当前进程的“控制终端”,并没有代表任何物理意义上的终端,访问设备(5,0)就是访问当前进程的”控制终端“。每一个进程通常都有一个“控制终端”。如果没有,当打开第一个终端设备时,就把这个终端设备设置为进程的"控制终端“(当然这个新打开的终端必须代表具体的终端设备)。如果我们不想把打开的终端设备作为进程的控制终端,那么可以在系统调用open的参数flags中加入O_NOCTTY标识。
进程的控制终端存放在task_struct.signal->tty中。
个人认为引入/dev/tty (5,0)的意义在于,为应用层提供了访问当前进程“控制终端”的short cut。我们可以在tty_open中看到如下代码
if (device == MKDEV(TTYAUX_MAJOR, 0)) { tty = get_current_tty(); if (!tty) { unlock_kernel(); mutex_unlock(&tty_mutex); return -ENXIO; } driver = tty_driver_kref_get(tty->driver); index = tty->index; filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ /* FIXME: Should we take a driver reference ? */ tty_kref_put(tty); goto got_driver; }
如果打开的设备节点是/dev/tty,那么就操作get_current_tty返回的当前进程“控制终端”
/dev/tty0
主设备号4,次设备号0。表示当前虚拟控制台,在支持 虚拟控制台(console)的内核中有个全局量fg_console,表示当前的“前台控制台”。
/dev/console
主设备号5, 次设备号为1。用于外接控制台,内核在初始化的过程中安装模块时会通过函数register_console,登记用做控制台的终端设备,把一个console数据结构挂到内核中的console_drivers队列中。
如果要打开的终端设备是/dev/console,那么就在console_drivers队列中找到第一个console结构,它所对应的设备号就是当前系统控制台的设备号