在我的fedora11系统(linux2.6.31)下,这三个设备的具体情况如下:
- [root@localhost dev]# ls -l tty
-
crw-rw-rw- 1 root tty 5, 0 2012-01-30 17:26 tty
-
[root@localhost dev]# ls -l tty0
-
crw--w---- 1 root root 4, 0 2012-01-30 17:26 tty0
-
[root@localhost dev]# ls -l console
-
crw------- 1 root root 5, 1 2012-01-30 17:26 console
1./dev/tty表示控制终端
如果当前进程有控制终端(Controlling
Terminal)的话,那么/dev/tty就是当前进程的控制终端的设备特殊文件。可以使用命令”ps
–ax”来查看进程与哪个控制终端相连。对于你登录的shell,/dev/tty就是你使用的终端,设备号是(5,0)。使用命令”tty”可以查看它具体对应哪个实际终端设备。
控制终端可以是Xwindows模式下的伪终端(/dev/pts/*),也可以是控制台虚拟终端(/dev/tty*)
- if (device == MKDEV(5, 0)) {
-
tty = get_current_tty();
-
if (!tty) {
-
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;
-
}
"driver"对应到驱动,“index"对应到具体的设备,我们从中也可以看出
/dev/tty有些类似于到实际所使用终端设备的一个链接。
2./dev/tty0则是当前所使用虚拟终端的一个别名
- # tty(查看当前TTY)
-
/dev/tty1
-
#echo "test tty0" > /dev/tty0
-
test tty0
内核实现如下:
- if (device == MKDEV(4, 0)) {
-
extern struct tty_driver *console_driver;
-
driver = tty_driver_kref_get(console_driver);
-
index = fg_console;
-
noctty = 1;
-
goto got_driver;
-
}
3./dev/console即控制台,是与操作系统交互的设备,系统将一些信息直接输出到控制台上
内核实现如下:
- if (device == MKDEV(5, 1)) {
-
struct tty_driver *console_driver = console_device(&index);//如何通过这一句得到tty_driver结构呢,这个结构是哪里来的?我们稍候会提到
-
if (console_driver) {
-
driver = tty_driver_kref_get(console_driver);
-
if (driver) {
-
/* Don't let /dev/console block */
-
filp->f_flags |= O_NONBLOCK;
-
noctty = 1;
-
goto got_driver;
-
}
-
}
-
mutex_unlock(&tty_mutex);
-
return -ENODEV;
-
}
通过/dev/console如何对应到具体的设备呢?
在8250.c里,有如下代码:
- static struct console serial8250_console = {
-
.name = "ttyS",
-
.write = serial8250_console_write,
-
.device = uart_console_device,
-
.setup = serial8250_console_setup,
-
.early_setup = serial8250_console_early_setup,
-
.flags = CON_PRINTBUFFER,
-
.index = -1,
-
.data = &serial8250_reg,
-
};
-
-
static int __init serial8250_console_init(void)
-
{
-
if (nr_uarts > UART_NR)
-
nr_uarts = UART_NR;
-
-
serial8250_isa_init_ports();
-
register_console(&serial8250_console);
-
return 0;
-
}
register_console会将serial8250_console添加到console_drivers的尾部。
而
console_device函数遍历console_drivers以得到一个有效的console,得到有效的console以后(在这个例子里,当然会得到serial8250_console),然后会调用他的device方法以得到相应的tty_driver结构,serial8250_console
结构的device方法为uart_console_device,uart_console_device
的内容如下:
- struct tty_driver *uart_console_device(struct console *co, int *index)
-
{
-
struct uart_driver *p = co->data;
-
*index = co->index;
-
return p->tty_driver;
-
}
serial8250_reg
是个uart_driver的结构体,内容如下:
- static struct uart_driver serial8250_reg = {
-
.owner = THIS_MODULE,
-
.driver_name = "serial",
-
.dev_name = "ttyS",
-
.major = TTY_MAJOR,
-
.minor = 64,
-
.cons = SERIAL8250_CONSOLE,
-
};
注册uart_driver 的时候会分配相应的tty_driver结构
这样通过/dev/console和具体的uart驱动联系起来,通过co->index,又和具体的uart设备联系了起来,这样,内核通过/dev/console就可以操作串口,进行内核信息的输出了。
内核使用/dev/console的地方为kernel初始化的时候:
start_kernel->rest_init->kernel_init->init_post
- static noinline int init_post(void)
__releases(kernel_lock)
{ - ...
- if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
-
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
-
-
(void) sys_dup(0);
-
(void) sys_dup(0);
- ...
将控制台设置为输入,后续的两个sys_dup(0),则复制标准输入为标准输出和标准错误输出。
这是为调用init进程做准备,之后内核将通过kernel_execve()调用init进程,这样,init进程的输入、输出和错误输出就全部重定向到/dev/console对应的uart设备了.
因为用户应用程序全部为init进程的子或孙进程,所以,默认情况下,用户应用程序的输入、输出和错误输出也全部重定向到了/dev/console对应的uart设备了。
阅读(3260) | 评论(0) | 转发(0) |