console 的定位

首先在init/main.c的init函数中建立了系统的stdin和stdout
 if (open("/dev/null", O_RDWR, 0) < 0)
  printk("Warning: unable to open an initial console./n");
 (void) dup(0);
 (void) dup(0);
打开的/dev/null设备,这也是导致busybox开始printf不能显示的原因。
而且显然在busybox中重新建立了stdin和stdout,于是我在busybox源码的
init/init.c的init_main函数中找到了以下代码
 /* Figure out where the default console should be */
 console_init();
 /* Close whatever files are open, and reset the console. */
 close(0);
 close(1);
 close(2);
 if (device_open(console, O_RDWR | O_NOCTTY) == 0) {
  set_term(0);
  close(0);
 } //这里好像没有建立stdin和stdout啊,反正肯定有新建的
改成打开/dev/console设备,就可以在busybox中添加打印语句察看流程了。
只是不知道哪里出了问题,我这里用reboot重启就会死掉,所以还是改回/dev/null了,
等有空再仔细看看busybox了,这里先介绍内核是怎样把console定位到ttyS0的,
因为我们真正的控制台设备是ttyS0。
找到drivers/char/mem.c中的chr_dev_init函数,是用__initcall(chr_dev_init)注册使其
在初始化的时候运行的。该函数注册了大部分的字符设备,null,mem,console,tty等等
它tty_init()(drivers/char/tty_io.c)来注册tty和console等设备,代码如下
 memset(&dev_tty_driver, 0, sizeof(struct tty_driver));
 dev_tty_driver.magic = TTY_DRIVER_MAGIC;
 dev_tty_driver.driver_name = "/dev/tty";
 dev_tty_driver.name = dev_tty_driver.driver_name + 5;
 dev_tty_driver.name_base = 0;
 dev_tty_driver.major = TTYAUX_MAJOR;
 dev_tty_driver.minor_start = 0;
 dev_tty_driver.num = 1;
 dev_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
 dev_tty_driver.subtype = SYSTEM_TYPE_TTY;
 
 if (tty_register_driver(&dev_tty_driver))
  panic("Couldn't register /dev/tty driver/n");

 dev_syscons_driver = dev_tty_driver;
 dev_syscons_driver.driver_name = "/dev/console";
 dev_syscons_driver.name = dev_syscons_driver.driver_name + 5;
 dev_syscons_driver.major = TTYAUX_MAJOR;
 dev_syscons_driver.minor_start = 1;
 dev_syscons_driver.type = TTY_DRIVER_TYPE_SYSTEM;
 dev_syscons_driver.subtype = SYSTEM_TYPE_SYSCONS;

 if (tty_register_driver(&dev_syscons_driver))
  panic("Couldn't register /dev/console driver/n");

tty      设备   major=5  minor=0
consle  设备 major=5  minor=1

看一下它调用的tty_register_driver函数
int tty_register_driver(struct tty_driver *driver)
{
 int error;
        int i;

 if (driver->flags & TTY_DRIVER_INSTALLED)
  return 0;

 error = devfs_register_chrdev(driver->major, driver->name, &tty_fops);
 if (error < 0)
  return error;
 else if(driver->major == 0)
  driver->major = error;

 if (!driver->put_char)
  driver->put_char = tty_default_put_char;
 
 driver->prev = 0;
 driver->next = tty_drivers;
 if (tty_drivers) tty_drivers->prev = driver;
 tty_drivers = driver;
 
 if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) {
  for(i = 0; i < driver->num; i++)
      tty_register_devfs(driver, 0, driver->minor_start + i);
 }
 proc_tty_register_driver(driver);
 return error;
}

使用了devfs_register_chrdev来注册设备,而下来又用tty_register_devfs又一次注册设备
还是先来看看这两个函数吧
int devfs_register_chrdev (unsigned int major, const char *name,
      struct file_operations *fops)
{
    if (boot_options & OPTION_ONLY) return 0;
    return register_chrdev (major, name, fops);

这个函数很简单,其实就是调用了register_chrdev来注册设备,而用register_chrdev来注册
设备是需要使用mknod来建立节点的,看看register_chrdev函数,其实只是对一个全局的
chrdevs结构赋值而已。
void tty_register_devfs (struct tty_driver *driver, unsigned int flags, unsigned minor)
{
#ifdef CONFIG_DEVFS_FS
 umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
 kdev_t device = MKDEV (driver->major, minor);
 int idx = minor - driver->minor_start;
 char buf[32];

 switch (device) {
  case TTY_DEV:
  case PTMX_DEV:
   mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
   break;
  default:
   if (driver->major == PTY_MASTER_MAJOR)
    mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
   break;
 }
 if ( (minor <  driver->minor_start) ||
      (minor >= driver->minor_start + driver->num) ) {
  printk(KERN_ERR "Attempt to register invalid minor number "
         "with devfs (%d:%d)./n", (int)driver->major,(int)minor);
  return;
 }
#  ifdef CONFIG_UNIX98_PTYS
 if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) &&
      (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) )
  flags |= DEVFS_FL_CURRENT_OWNER;
#  endif
 sprintf(buf, driver->name, idx + driver->name_base);
 devfs_register (NULL, buf, flags | DEVFS_FL_DEFAULT,
   driver->major, minor, mode, &tty_fops, NULL);
#endif /* CONFIG_DEVFS_FS */
}
对于tty_register_devfs函数则需要定义CONFIG_DEVFS_FS宏才有意义,这意味着内核支持
devfs设备文件系统。使用devfs_register注册设备同时在/dev建立相应的节点。
只要没有使用mknod建立节点,就不会和devfs_register产生冲突,而我实际使用的都是
devfs_register注册产生的设备。

关键要考虑的还是tty_fops这个结构中的设备方法
static struct file_operations tty_fops = {
 llseek:  no_llseek,
 read:  tty_read,
 write:  tty_write,
 poll:  tty_poll,
 ioctl:  tty_ioctl,
 open:  tty_open,
 release: tty_release,
 fasync:  tty_fasync,
};

看看tty_open方法,在其中能看到对不同设备的处理方法,重点还是看console的
        device = inode->i_rdev;
 if (device == SYSCONS_DEV) {
  struct console *c = console_drivers;
  while(c && !c->device)
   c = c->next;
  if (!c)
                        return -ENODEV;
                device = c->device(c);
  filp->f_flags |= O_NONBLOCK; /* Don't let /dev/console block */
  noctty = 1;
 }
 retval = init_dev(device, &tty);
 filp->private_data = tty;
 if (tty->driver.open)
  retval = tty->driver.open(tty, filp);
console_drivers就是前一片文章介绍过的全局变量,存放着真正控制台ttyS0的信息,在这里并没有把file结构中的f_pos重新赋值,而是让filp->private_data赋值为tty结构然后通过tty->driver.open去打开真正的控制台设备。同时在tty_read,tty_write方法中也是通过这个tty结构实现对真正控制台设备的读写的。如果定义了CONFIG_VT宏,就是make menuconfig,在Character devices中选中Virtual terminal,而且关闭串口控制台,这样就会把显示器还有键盘做为默认的控制台设备,在console drivers下可以看到VGA text console被选中。希望以后可以加上usb键盘和液晶屏作为控制台吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值