| Profilo di 宗文方寸之间FotoBlogElenchi | Guida |
|
|
09 agosto Linux的核心启动流程(FC5)下面以RedHat Fedora Core5(Linux 2.6.15-1.2054_FC5)为基础说说Linux的启动流程。
打开计算机电源后,第一个执行的程序是ROM BIOS,该程序根据设置选择一个引导设备,比如软盘、硬盘、光盘或者USB盘等,然后读入引导设备上的一小段程序(称为BootLoader,常见的有lilo,grub等)。
BootLoader会读入相关的引导选单并执行。一般的引导选单上会指定从哪个设备的哪个分区读入操作系统内核,给操作系统传入哪些命令行参数(cmdline)、初始的RAMDISK(initrd)等等。BootLoader将系统核心以及initrd读入内存并传递好cmdline/initrd后就结束了自己的使命,控制权转移到Linux kernel。
我采用的BootLoader是grub,被安装在第一个SCSI盘的第一个分区上,其上的grub.conf内容如下(#后的注释是我加的,下同):
default=0
timeout=5 splashimage=(hd0,0)/grub/splash.xpm.gz title Fedora Core (2.6.15-1.2054_FC5) root (hd0,0) # 指定操作系统内核(Kernel)文件所在的磁盘和分区 kernel /vmlinuz-2.6.15-1.2054_FC5 ro root=LABEL=/ # 指定操作系统的文件名称以及cmdline initrd /initrd-2.6.15-1.2054_FC5.img # 指定initrd title MiniLinux-USB (2.6.15-1.2054_FC5) root (hd1,0) kernel /boot/vmlinuz-2.6.15-1.2054_FC5 ro root=/dev/ram0 initrd /boot/initrd-minilinux-usb.img 操作系统内核(Kernel)会执行各种必要的初始化如内存页表、进程表等等数据结构,初始化被编译到Kernel的内核模块以及设备驱动等等,很多初始化都需要分析cmdline以便在多个策略中选择。
接下来的启动流程分为两个分支:
1) 如果BootLoader指定了initrd,则解压initrd指定的初始RAM DISK映像到/dev/ram0并挂装/dev/ram0作为根文件系统,然后执行/init。
/init的目的一般是装载最终的根文件系统所必须的文件系统内核模块,建立一些设备特别文件并挂装最终的根文件系统(如果不是/dev/ram0)。 我安装的RedHat FC5的initrd-2.6.15-1.2054_FC5.img文件是个gzip压缩过的cpio文件,在启动完成后位于/boot目录下(因为启动分区被安装在/boot目录下, 参见下篇“linux的应用启动流程”一文)。我们可以执行下述命令提取其内容
mkdir -p xxx
cd xxx gunzip -c /boot/initrd-2.6.15-1.2054_FC5.img | cpio -idv 我们可以看到下面有很多目录和文件:
bin/ dev/ etc/ init* lib/ proc/ sbin@ sys/ sysroot/ ./bin:
insmod* modprobe@ nash* ./dev:
console null ram@ ram1 systty tty0 tty10 tty12 tty3 tty5 tty7 tty9 ttyS1 ttyS3 mapper/ ptmx ram0 rtc tty tty1 tty11 tty2 tty4 tty6 tty8 ttyS0 ttyS2 zero ./dev/mapper:
./etc:
./lib:
BusLogic.ko ext3.ko jbd.ko scsi_mod.ko sd_mod.ko ./proc:
./sys:
./sysroot:
其中/init内容如下:
#!/bin/nash #由/bin/nash解释执行
mount -t proc /proc /proc #挂装proc文件系统
setquiet #安静模式 echo Mounting proc filesystem echo Mounting sysfs filesystem mount -t sysfs /sys /sys #挂装sysfs文件系统 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 # 利用sysfs文件系统自动创建当前发现的块设备的特别文件,关于sysfs将是另一篇文章的内容了 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 BusLogic.ko module" insmod /lib/BusLogic.ko # 前面几个insmod装载接下来挂装最终的根文件系统所必需的驱动程序模块 mkblkdevs # 创建刚刚这些驱动程序发现并注册到sysfs文件系统中的块设备的特别文件 echo Creating root device. mkrootdev -t ext3 -o defaults,ro /dev/root # 分析kernel cmdline寻找根文件设备并创建一个特别文件指向该设备(如果是以“LABEL=”开始则自动寻找分区表上对应该LABEL的分区)并加入到/etc/fstab中 echo Mounting root filesystem. mount /sysroot #将最终的根文件系统临时挂装到/sysroot下 echo Setting up other filesystems. setuproot # 设置proc,sys等文件系统到/sysroot下 echo Switching to new root and running init. switchroot # 将/sysroot切换为最终的根文件系统,并执行其上的/init,见后 2) 如果BootLoader没有指定initrd,直接挂装cmdline上指定的root设备作为根文件系统,接下来按照下面的顺序寻找init程序并建立第一个进程:
run_init_process("/sbin/init"); // 找到就建立第一个进程(pid=0),不会返回了,下面三行同此
run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh"); panic("No init found. Try passing init= option to kernel."); // 寻找不到,打印错误信息,系统挂起 不管是哪个分支,到此为止,核心启动流程结束,后面由最终的根文件系统上的init控制转入应用启动流程,这个待下篇文章再说吧。
15 luglio Linux的核心启动流程前段时间自己裁剪了一个Linux玩玩,顺便写点东西吧。
下面以RedHat Fedora Core4(Linux 2.6)为基础说说Linux的启动流程。
打开计算机电源后,第一个执行的程序是ROM BIOS,该程序根据设置选择一个引导设备,比如软盘、硬盘、光盘或者USB盘等,然后读入引导设备上的一小段程序(称为BootLoader,常见的有lilo,grub等)。
BootLoader会读入相关的引导选单并执行。一般的引导选单上会指定从那个设备的那个分区上读入操作系统,给操作系统传入哪些命令行参数(cmdline)、初始的RAMDISK(initrd)等等。BootLoader将系统核心以及initrd内存读入并传递好cmdline/initrd后就结束了自己的使命,控制权转移到Linux kernel。
我采用的BootLoader是grub,被安装在第一个SCSI盘的第一个分区上,其上的grub.conf内容如下(#后的注释是我加的,下同):
default=0
timeout=5 splashimage=(hd0,0)/grub/splash.xpm.gz title Fedora Core (2.6.11-1.1369_FC4) root (hd0,0) # 指定操作系统所在的磁盘和分区 kernel /vmlinuz-2.6.11-1.1369_FC4 ro root=LABEL=/ # 指定操作系统的文件名称以及cmdline initrd /initrd-2.6.11-1.1369_FC4.img # 指定initrd title MiniLinux-USB (2.6.11-1.1369_FC4) root (hd1,0) kernel /boot/vmlinuz-2.6.11-1.1369_FC4 ro root=/dev/ram0 initrd /boot/initrd-minilinux-usb.img Kernel会执行各种必要的初始化如内存页表,内核的数据结构,被编译到kernel的内核模块以及设备驱动等等,很多初始化都需要分析cmdline以便在多个策略中选择。
下面的启动流程分为两个分支:
1) 如果BootLoader指定了initrd,则解压initrd指定的初始RAM DISK映像到/dev/ram0并挂装/dev/ram0作为根文件系统,然后执行/init。
/init的目的一般是装载根文件系统所必须的文件系统内核模块,建立一些设备特别文件并挂装最终的根文件系统(如果不是/dev/ram0)。 我安装的RedHat FC4的initrd-2.6.11-1.1369_FC4.img文件是个gzip压缩过的cpio文件,在启动完成后位于/boot目录下(因为启动分区被以下篇“linux的应用启动流程”一文描述的方式安装在/boot目录下了)。我们可以执行下述命令提取其内容
mkdir -p xxx
cd xxx gunzip -c /boot/initrd-2.6.11-1.1369_FC4.img | cpio -idv 我们可以看到下面有很多目录和文件:
bin/ dev/ etc/ init* lib/ loopfs/ proc/ sbin@ sys/ sysroot/
./bin:
hotplug@ insmod* modprobe@ nash* udev* udevstart* ./dev:
console null ram systty tty1 tty2 tty3 tty4 ./etc:
udev/ ./etc/udev:
udev.conf ./lib:
BusLogic.ko* ext3.ko* jbd.ko* scsi_mod.ko* sd_mod.ko* ./loopfs:
./proc: ./sys: ./sysroot: 其中/init内容如下:
#!/bin/nash
mount -t proc /proc /proc setquiet echo Mounted /proc filesystem echo Mounting sysfs mount -t sysfs /sys /sys echo Creating /dev mount -o mode=0755 -t tmpfs /dev /dev mknod /dev/console c 5 1 mknod /dev/null c 1 3 mknod /dev/zero c 1 5 mkdir /dev/pts mkdir /dev/shm echo Starting udev /sbin/udevstart # 利用sysfs文件系统自动创建存在的设备的特别文件,关于sysfs将是另一篇文章的内容了 echo -n "/sbin/hotplug" > /proc/sys/kernel/hotplug # 通知内核当有热插拔设备插上或拔下时的要调用的用户层处理程序 echo "Loading scsi_mod.ko module" insmod /lib/scsi_mod.ko echo "Loading sd_mod.ko module" insmod /lib/sd_mod.ko echo "Loading BusLogic.ko module" insmod /lib/BusLogic.ko echo "Loading jbd.ko module" insmod /lib/jbd.ko echo "Loading ext3.ko module" insmod /lib/ext3.ko # 前面几个insmod装载接下来装载最终根文件系统需要的驱动程序 /sbin/udevstart # 自动创建由刚刚这些设备驱动程序注册到sysfs里面的设备的特别文件 echo Creating root device mkrootdev /dev/root # 分析kernel cmdline寻找根文件设备并创建一个特别文件指向该设备(如果是LABEL=开始自动寻找分区表上该LABEL的分区) echo Mounting root filesystem mount -o defaults --ro -t ext3 /dev/root /sysroot echo Switching to new root switchroot --movedev /sysroot # 将/sysroot作为最终的根文件系统,并执行其上的/init,见后 2) 如果BootLoader没有指定initrd,直接挂装cmdline上指定的root设备作为根文件系统,接下来按照下面的顺序寻找init程序并建立第一个进程:
run_init_process("/sbin/init"); // 找到就建立第一个进程(pid=0),不会返回了,下面三行同此
run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh"); panic("No init found. Try passing init= option to kernel."); // 寻找不到,打印错误信息,系统挂起 不管是哪个分支,到此为止,核心启动流程结束,后面由最终的根文件系统上的init控制转入应用启动流程,这个待下篇文章再说吧。 12 luglio linux系统下select和poll的实现机理1.用户层应用程序调用select()
2.核心层调用sys_select() ------> do_select()
最终调用文件描述符fd对应的struct file类型变量的struct file_operations *f_op的poll函数。
poll指向的函数返回当前可否读写的信息。 1)如果当前可读写,返回读写信息。 2)如果当前不可读写,则阻塞进程,并等待驱动程序唤醒,重新调用poll函数,或超时返回。 核心层的相关函数(select.c):
do_select( ... ) { poll_table *wait; ... for (;;) { set_current_state(TASK_INTERRUPTIBLE); for (i = 0 ; i < n; i++) { unsigned long mask; struct file *file; ... file = fget(i); mask = POLLNVAL; mask = file->f_op->poll(file, wait); if ((mask & POLLIN_SET) && ISSET(bit, __IN(fds,off))) retval++; if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(fds,off))) { retval++; } if (retval || !__timeout || signal_pending(current)) break; __timeout = schedule_timeout(__timeout); // 此处阻塞,等待驱动wake_up_interruptible } current->state = TASK_RUNNING; } 3.驱动需要实现poll函数。
当驱动发现有数据可以读写时,通知核心层,核心层重新调用poll指向的函数查询信息。 例如:
static unsigned int test_poll(struct file *file, poll_table * wait) { poll_wait(file, &queue, wait); // 此处将当前进程加入到等待队列中,但并不阻塞 return POLLIN |POLLRDNORM |...; } static void test_...(...)
{ wake_up_interruptible(&queue->proc_list); } static void test_init(...) { init_waitqueue_head(&queue->proc_list); } |
|
|