在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结构,它所对应的设备号就是当前系统控制台的设备号