linux下/dev/tty, /dev/tty0, /dev/console区别

16956阅读 0评论2012-01-30 jinxinxin163
分类:LINUX

在我的fedora11系统(linux2.6.31)下,这三个设备的具体情况如下:
  1. [root@localhost dev]# ls -l tty
  2. crw-rw-rw- 1 root tty 5, 0 2012-01-30 17:26 tty
  3. [root@localhost dev]# ls -l tty0
  4. crw--w---- 1 root root 4, 0 2012-01-30 17:26 tty0
  5. [root@localhost dev]# ls -l console
  6. 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*)
  1. if (device == MKDEV(5, 0)) {
  2.         tty = get_current_tty();
  3.         if (!tty) {
  4.             mutex_unlock(&tty_mutex);
  5.             return -ENXIO;
  6.         }
  7.         driver = tty_driver_kref_get(tty->driver);
  8.         index = tty->index;
  9.         filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
  10.         /* noctty = 1; */
  11.         /* FIXME: Should we take a driver reference ? */
  12.         tty_kref_put(tty);
  13.         goto got_driver;
  14.     }
"driver"对应到驱动,“index"对应到具体的设备,我们从中也可以看出/dev/tty有些类似于到实际所使用终端设备的一个链接
2./dev/tty0则是当前所使用虚拟终端的一个别名
  1. # tty(查看当前TTY)
  2. /dev/tty1
  3. #echo "test tty0" > /dev/tty0
  4. test tty0
内核实现如下:
  1. if (device == MKDEV(4, 0)) {
  2.         extern struct tty_driver *console_driver;
  3.         driver = tty_driver_kref_get(console_driver);
  4.         index = fg_console;
  5.         noctty = 1;
  6.         goto got_driver;
  7.     }

3./dev/console即控制台,是与操作系统交互的设备,系统将一些信息直接输出到控制台上
内核实现如下:
  1. if (device == MKDEV(5, 1)) {
  2.         struct tty_driver *console_driver = console_device(&index);//如何通过这一句得到tty_driver结构呢,这个结构是哪里来的?我们稍候会提到
  3.         if (console_driver) {
  4.             driver = tty_driver_kref_get(console_driver);
  5.             if (driver) {
  6.                 /* Don't let /dev/console block */
  7.                 filp->f_flags |= O_NONBLOCK;
  8.                 noctty = 1;
  9.                 goto got_driver;
  10.             }
  11.         }
  12.         mutex_unlock(&tty_mutex);
  13.         return -ENODEV;
  14.     }
通过/dev/console如何对应到具体的设备呢?
在8250.c里,有如下代码:
  1. static struct console serial8250_console = {
  2.     .name        = "ttyS",
  3.     .write        = serial8250_console_write,
  4.     .device        = uart_console_device,
  5.     .setup        = serial8250_console_setup,
  6.     .early_setup    = serial8250_console_early_setup,
  7.     .flags        = CON_PRINTBUFFER,
  8.     .index        = -1,
  9.     .data        = &serial8250_reg,
  10. };

  11. static int __init serial8250_console_init(void)
  12. {
  13.     if (nr_uarts > UART_NR)
  14.         nr_uarts = UART_NR;

  15.     serial8250_isa_init_ports();
  16.     register_console(&serial8250_console);
  17.     return 0;
  18. }
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的内容如下:
  1. struct tty_driver *uart_console_device(struct console *co, int *index)
  2. {
  3.     struct uart_driver *p = co->data;
  4.     *index = co->index;
  5.     return p->tty_driver;
  6. }
serial8250_reg是个uart_driver的结构体,内容如下:
  1. static struct uart_driver serial8250_reg = {
  2.     .owner            = THIS_MODULE,
  3.     .driver_name        = "serial",
  4.     .dev_name        = "ttyS",
  5.     .major            = TTY_MAJOR,
  6.     .minor            = 64,
  7.     .cons            = SERIAL8250_CONSOLE,
  8. };
注册uart_driver 的时候会分配相应的tty_driver结构
这样通过/dev/console和具体的uart驱动联系起来,通过co->index,又和具体的uart设备联系了起来,这样,内核通过/dev/console就可以操作串口,进行内核信息的输出了。
内核使用/dev/console的地方为kernel初始化的时候:
start_kernel->rest_init->kernel_init->init_post
  1. static noinline int init_post(void)
        __releases(kernel_lock)
    {
  2. ...
  3. if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
  4.         printk(KERN_WARNING "Warning: unable to open an initial console.\n");

  5.     (void) sys_dup(0);
  6.     (void) sys_dup(0);
  7. ...
将控制台设置为输入,后续的两个sys_dup(0),则复制标准输入为标准输出和标准错误输出。
这是为调用init进程做准备,之后内核将通过kernel_execve()调用init进程,这样,init进程的输入、输出和错误输出就全部重定向到/dev/console对应的uart设备了.
因为用户应用程序全部为init进程的子或孙进程,所以,默认情况下,用户应用程序的输入、输出和错误输出也全部重定向到了/dev/console对应的uart设备了。





上一篇:exit并未关闭标准I/O流
下一篇:fc11 syslog配置及syslog函数使用原理