主要内容通过学习dev目录的创建来了解内核启动过程中的一些详细信息。Dev目录是存放设备节点的目录,通过我们分析dev目录可以进一步了解内核。 我们主要从两点讲述 1:dev目录的创建过程 2:设备节点的创建过程 历史主要介绍dev目录的发展史。 静态dev目录最初的dev目录是静态,所有的设备节点是手动、事先创建的。这样随着设备发展,这个目录会越来越大。我们不知道系统需要哪里设备,所以必须要将可能的设备事先全部创建。 DevfsDevfs和procfs、sysfs文件系统一样,都是内核文件系统。因为有其自身缺点,所以目前不再说使用。 Tmpfs和udev目前的dev是个tmpfs文件系统,它是个用户文件系统,与内核没有一点关系。在设备插拔时,由udev工具根据事先的规则动态添加和删除设备节点,即dev目录下的文件。 Dev创建过程Dev的创建过程比较复杂,我们一步步讲解。 第一步initrd是个文件系统,是通过mkinitrd工具创建的,是内核起始过载的第一个根文件系统。 通过gunzip、cpio打开这个文件: bin dev etc init lib proc sbin sys sysroot 打开dev目录: console null ram ram1 systty tty0 tty10 tty12 tty3 tty5 tty7 tty9 ttyS1 zero mapper ptmx ram0 rtc tty tty1 tty11 tty2 tty4 tty6 tty8 ttyS0 ttyS2 Initrd目录下面的dev目录是静态创建了,并包含了许多常见的设备节点,这些设备节点在内核启动时需要的。这是dev目录的第一版。在执行到第二步之前,系统使用该目录下的设备节点。 第二步在initrd中下面有个脚本文件init: #!/bin/nash
mount -t proc /proc /proc setquiet echo Mounting proc filesystem echo Mounting sysfs filesystem mount -t sysfs /sys /sys echo Creating /dev mount -o mode=0755 -t tmpfs /dev /dev mkdir /dev/pts mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts mkdir /dev/shm mkdir /dev/mapper echo Creating initial device nodes mknod /dev/null c 1 3 mknod /dev/zero c 1 5 mknod /dev/systty c 4 0 mknod /dev/tty c 5 0 mknod /dev/console c 5 1 mknod /dev/ptmx c 5 2 mknod /dev/rtc c 10 135 mknod /dev/tty0 c 4 0 mknod /dev/tty1 c 4 1 mknod /dev/tty2 c 4 2 mknod /dev/tty3 c 4 3 mknod /dev/tty4 c 4 4 mknod /dev/tty5 c 4 5 mknod /dev/tty6 c 4 6 mknod /dev/tty7 c 4 7 mknod /dev/tty8 c 4 8 mknod /dev/tty9 c 4 9 mknod /dev/tty10 c 4 10 mknod /dev/tty11 c 4 11 mknod /dev/tty12 c 4 12 mknod /dev/ttyS0 c 4 64 mknod /dev/ttyS1 c 4 65 mknod /dev/ttyS2 c 4 66 mknod /dev/ttyS3 c 4 67 echo Setting up hotplug. hotplug echo Creating block device nodes. mkblkdevs echo "Loading ehci-hcd.ko module" insmod /lib/ehci-hcd.ko echo "Loading ohci-hcd.ko module" insmod /lib/ohci-hcd.ko echo "Loading uhci-hcd.ko module" insmod /lib/uhci-hcd.ko mount -t usbfs /proc/bus/usb /proc/bus/usb echo "Loading jbd.ko module" insmod /lib/jbd.ko echo "Loading ext3.ko module" insmod /lib/ext3.ko echo "Loading scsi_mod.ko module" insmod /lib/scsi_mod.ko echo "Loading sd_mod.ko module" insmod /lib/sd_mod.ko echo "Loading libata.ko module" insmod /lib/libata.ko echo "Loading sata_sil.ko module" insmod /lib/sata_sil.ko echo "Loading ata_piix.ko module" insmod /lib/ata_piix.ko echo "Loading ahci.ko module" insmod /lib/ahci.ko echo "Loading usb-storage.ko module" insmod /lib/usb-storage.ko echo Waiting for driver initialization. stabilized /proc/bus/usb/devices echo "Loading dm-mem-cache.ko module" insmod /lib/dm-mem-cache.ko echo "Loading dm-mod.ko module" insmod /lib/dm-mod.ko echo "Loading dm-log.ko module" insmod /lib/dm-log.ko echo "Loading dm-region_hash.ko module" insmod /lib/dm-region_hash.ko echo "Loading dm-message.ko module" insmod /lib/dm-message.ko echo "Loading dm-raid45.ko module" insmod /lib/dm-raid45.ko echo Waiting for driver initialization. stabilized --hash --interval 1000 /proc/scsi/scsi mkblkdevs echo Scanning and configuring dmraid supported devices echo Creating root device. mkrootdev -t ext3 -o defaults,ro hda2 echo Mounting root filesystem. mount /sysroot echo Setting up other filesystems. setuproot echo Switching to new root and running init. switchroot
该文件是用nash执行的,nash的源码在mkinitrd源码包中。 mount -o mode=0755 -t tmpfs /dev /dev 在执行init脚本时,会重新挂载dev,这时的dev就是个tmpfs文件系统了。挂载后dev目录下是空的,所以后面就会创建许多基本设备文件,这些设备节点和第一步的dev目录下的设备节点基本相同。 同时在init脚本中,会加载各种块设备文件和文件系统驱动。这时系统就会有许多块设备,但是系统是如果在dev下面创建设备节点的? 系统是通过nash的命令:mkblkdevs来创建设备节点。该命令是根据proc文件系统下的partitions文件创建设备节点的。 但是我们知道当前的根文件系统并不是系统启动后的真正根文件系统,那么等我们的根文件系统启动后,我们发现dev目录仍然是tmpfs。那么就会有两个问题: 1:我们根文件系统下dev目录是何时挂载的? 2:在内核启动过程中发现的块设备节点,又是如何在dev目录显示。 第三步在init脚本中有个setuproot命令,我们分析nash中的setuproot发现: if (mount("/dev", "./dev", NULL, MS_BIND, NULL) < 0) eprintf("setuproot: moving /dev failed: %m\n"); 这句话是至关重要的。它将原来的dev挂载到系统启动的根文件系统的dev目录下面。但是我们知道/dev是个目录,是不能挂载的。那么系统是如果成功的呢? 注意到参数MS_BIND。能够挂载成功,是靠该参数。 --bind Remount a subtree somewhere else (so that its contents are available in both places). 可以简单解释下: 我 们知道块设备可以挂载到多个目录,其内容不变。但是由于在第二步挂载的tmpfs /dev是tmpfs,所以我们不能象其他文件系统一样,再次挂载到其 他目录下面。如果我们再次mount -t tmpfs /dev/ ./dev那么我们原来的设备节点都不会存在,对于那些在init中静态创建的设备 节点是可以,但是对于那些块设备节点就不能再次创建,这样这些设备都会丢失。 为了解决这个问题,我们采用参数--bind。 下面看一个例子: Mount -t tmpfs A B Mount --bind B C 这样相当于块设备的多次挂载。这时C目录和B目录是相同。这也正是我们需要的。 此时挂载成功后,tmpfs /dev就会挂载在/dev(init文件系统)、./dev(真正的根文件系统),同时保留了原理tmpfs /dev目录下的设备节点。 第四步在switchroot命令中: const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; for (; umounts[i] != NULL; i++) { qprintf("unmounting old %s\n", umounts[i]); if (umount2(umounts[i], MNT_DETACH) < 0) { eprintf("ERROR unmounting old %s: %m\n",umounts[i]); eprintf("forcing unmount of %s\n", umounts[i]); umount2(umounts[i], MNT_FORCE); } } 系统会将在init中的/dev卸载,所以在系统启动中会打印: Unmounting old /dev Unmounting old /proc Unmounting old /sys 但是在执行这一步时,我们已经在第三步将/dev重新挂载到./dev下面,所以不会导致设备节点丢失。 同时init(第一个用户进程)启动,系统进入用户态。也可以这么说在进入init时,在启动时发现的块设备节点都已经创建了。 第五步Init(第一个用户进程)执行脚本rc.sysinit: /sbin/start_udev 启动udev,自此udev这个用户空间程序,负责位dev目录下面创建或者删除设备节点。 Start_udev是个脚本文件,其启动udevd。 Dev目录Dev目录下面有许多有意思的文件,我们简单分析下。 Disk该目录下面是关于系统中的块设备的符号链接。按照不同的标准分类。 by-id符号链接,通过分析这个可以得到块设备的一些信息。 lrwxrwxrwx 1 root root 9 Jul 21 10:18 ata-InnoDisk_Corp._-_iCF4000_4GB_20080814AA2A70000050 -> ../../hda lrwxrwxrwx 1 root root 10 Jul 21 10:18 ata-InnoDisk_Corp._-_iCF4000_4GB_20080814AA2A70000050-part1 -> ../../hda1 lrwxrwxrwx 1 root root 10 Jul 21 10:18 ata-InnoDisk_Corp._-_iCF4000_4GB_20080814AA2A70000050-part2 -> ../../hda2 lrwxrwxrwx 1 root root 9 Jul 21 10:18 scsi-SATA_ST3320310CS_5TX03LHM -> ../../sdd lrwxrwxrwx 1 root root 9 Jul 21 10:18 scsi-SATA_ST3320310CS_5TX03LQK -> ../../sdc lrwxrwxrwx 1 root root 9 Jul 21 10:18 scsi-SATA_ST3320310CS_5TX05RDH -> ../../sdb lrwxrwxrwx 1 root root 9 Jul 21 10:18 scsi-SATA_ST3320310CS_5TX070WQ -> ../../sda lrwxrwxrwx 1 root root 10 Jul 21 10:18 scsi-SATA_ST3320310CS_5TX070WQ-part1 -> ../../sda1 lrwxrwxrwx 1 root root 9 Jul 21 10:18 scsi-SATA_ST3320620AS_6QF4SSS2 -> ../../sdg lrwxrwxrwx 1 root root 9 Jul 21 10:18 scsi-SATA_ST3320620SV_5QF7F5F0 -> ../../sde lrwxrwxrwx 1 root root 9 Jul 21 10:18 scsi-SATA_ST3320620SV_9QFBE47N -> ../../sdf lrwxrwxrwx 1 root root 9 Jul 21 10:18 scsi-SATA_ST3320620SV_9QFBE7FS -> ../../sdh lrwxrwxrwx 1 root root 10 Jul 21 10:18 scsi-SATA_ST3320620SV_9QFBE7FS-part1 -> ../../sdh1 by-label使用label的块设备。 lrwxrwxrwx 1 root root 10 Jul 21 10:18 boot -> ../../hda1 by-path块设备的父设备,比较有用。 lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:00:1f.1-ide-0:0 -> ../../hda lrwxrwxrwx 1 root root 10 Jul 21 10:18 pci-0000:00:1f.1-ide-0:0-part1 -> ../../hda1 lrwxrwxrwx 1 root root 10 Jul 21 10:18 pci-0000:00:1f.1-ide-0:0-part2 -> ../../hda2 lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:01:01.0-scsi-0:0:0:0 -> ../../sda lrwxrwxrwx 1 root root 10 Jul 21 10:18 pci-0000:01:01.0-scsi-0:0:0:0-part1 -> ../../sda1 lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:01:01.0-scsi-1:0:0:0 -> ../../sdb lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:01:01.0-scsi-2:0:0:0 -> ../../sdc lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:01:01.0-scsi-3:0:0:0 -> ../../sdd lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:01:02.0-scsi-0:0:0:0 -> ../../sde lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:01:02.0-scsi-1:0:0:0 -> ../../sdf lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:01:02.0-scsi-2:0:0:0 -> ../../sdg lrwxrwxrwx 1 root root 9 Jul 21 10:18 pci-0000:01:02.0-scsi-3:0:0:0 -> ../../sdh lrwxrwxrwx 1 root root 10 Jul 21 10:18 pci-0000:01:02.0-scsi-3:0:0:0-part1 -> ../../sdh1 by-uuid如果块设备或者分区,有文件系统,那么就会出现在该目录下面。 lrwxrwxrwx 1 root root 10 Jul 21 10:18 46c68b18-4954-4738-9738-a91bbe18df0e -> ../../hda1 lrwxrwxrwx 1 root root 10 Jul 21 10:18 8750fc53-2c73-d7b3-e941-f85db95f5be6 -> ../../sdh1 lrwxrwxrwx 1 root root 10 Jul 21 10:18 999edb78-d3f6-42cf-87ed-616099860da8 -> ../../hda2 lrwxrwxrwx 1 root root 10 Jul 21 10:18 a91da518-770d-4ae0-81eb-26583e2de0cd -> ../../sda1 lrwxrwxrwx 1 root root 9 Jul 21 10:18 d5254e53-34e2-ee36-d5c9-839a431774e6 -> ../../sdc Mapper使用LVM创建的块设备。 |
|