Chinaunix首页 | 论坛 | 博客
  • 博客访问: 247484
  • 博文数量: 43
  • 博客积分: 1878
  • 博客等级: 上尉
  • 技术积分: 457
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-02 11:08
文章分类

全部博文(43)

文章存档

2011年(3)

2010年(40)

分类: LINUX

2011-12-01 19:07:55

linux2.6内核initrd处理的源代码分析

上 面简要介绍了Linux2.4内核和2.6内核的initrd的处理流程,为了使读者对于Linux2.6内核的initrd的处理有一个更加深入的认 识,下面将对Linuxe2.6内核初始化部分同initrd密切相关的代码给予一个比较细致的分析,为了讲述方便,进一步明确几个代码分析中使用的概 念:


rootfs: 一个基于内存的文件系统,是linux在初始化时加载的第一个文件系统,关于它的进一步介绍可以参考文献[4]。

initramfs: initramfs同本文的主题关系不是很大,但是代码中涉及到了initramfs,为了更好的理解代码,这里对其进行简单的介绍。Initramfs 是在 kernel 2.5中引入的技术,实际上它的含义就是:在内核镜像中附加一个cpio包,这个cpio包中包含了一个小型的文件系统,当内核启动时,内核将这个 cpio包解开,并且将其中包含的文件系统释放到rootfs中,内核中的一部分初始化代码会放到这个文件系统中,作为用户层进程来执行。这样带来的明显 的好处是精简了内核的初始化代码,而且使得内核的初始化过程更容易定制。Linux 2.6.12内核的 initramfs还没有什么实质性的东西,一个包含完整功能的initramfs的实现可能还需要一个缓慢的过程。对于initramfs的进一步了解 可以参考文献[1][2][3]。

cpio-initrd: 前面已经定义过,指linux2.6内核使用的cpio格式的initrd。

image-initrd: 前面已经定义过,专指传统的文件镜像格式的initrd。

realfs: 用户最终使用的真正的文件系统。

内 核的初始化代码位于 init/main.c 中的 static int init(void * unused)函数中。同initrd的处理相关部分函数调用层次如下图,笔者按照这个层次对每一个函数都给予了比较详细的分析,为了更好的说明,下面列 出的代码中删除了同本文主题不相关的部分:



图2 initrd相关代码的调用层次关系图


init函数是内核所有初始化代码的入口,代码如下,其中只保留了同initrd相关部分的代码。


  1. static int init(void * unused){
  2. [1]    populate_rootfs();
  3.     
  4. [2]    if (sys_access((const char __user *) "/init", 0) == 0)
  5.         execute_command = "/init";
  6.     else
  7.         prepare_namespace();

  8. [3]    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
  9.         printk(KERN_WARNING "Warning: unable to open an initial console.n");

  10.     (void) sys_dup(0);
  11.     (void) sys_dup(0);

  12. [4]    if (execute_command)
  13.         run_init_process(execute_command);

  14.     run_init_process("/sbin/init");
  15.     run_init_process("/etc/init");
  16.     run_init_process("/bin/init");
  17.     run_init_process("/bin/sh");
  18.     panic("No init found. Try passing init= option to kernel.");
  19. }


代码[1]:populate_rootfs函数负责加载initramfs和cpio-initrd,对于populate_rootfs函数的细节后面会讲到。

代码[2]:如果rootfs的根目录下中包含/init进程,则赋予execute_command,在init函数的末尾会被执行。否则执行prepare_namespace函数,initrd是在该函数中被加载的。

代码[3]:将控制台设置为标准输入,后续的两个sys_dup(0),则复制标准输入为标准输出和标准错误输出。

代 码[4]:如果rootfs中存在init进程,就将后续的处理工作交给该init进程。其实这段代码的含义是如果加载了cpio-initrd则交给 cpio-initrd中的/init处理,否则会执行realfs中的init.读者可能会问:如果加载了cpio-initrd, 那么realfs中的init进程不是没有机会运行了吗?确实,如果加载了cpio-initrd,那么内核就不负责执行realfs的init进程了, 而是将这个执行任务交给了cpio-initrd的init进程。解开fedora core4的initrd文件,会发现根目录的下的init文件是一个脚本,在该脚本的最后一行有这样一段代码:



……….. switchroot --movedev /sysroot

就是switchroot语句负责加载realfs,以及执行realfs的init进程。

对cpio-initrd的处理

对cpio-initrd的处理位于populate_rootfs函数中。


  1. void __init populate_rootfs(void){
  2. [1] char *err = unpack_to_rootfs(__initramfs_start,
  3.              __initramfs_end - __initramfs_start, 0);
  4. [2]    if (initrd_start) {
  5. [3]        err = unpack_to_rootfs((char *)initrd_start,
  6.             initrd_end - initrd_start, 1);
  7.     
  8. [4]        if (!err) {
  9.             printk(" it isn");
  10.             unpack_to_rootfs((char *)initrd_start,
  11.                 initrd_end - initrd_start, 0);
  12.             free_initrd_mem(initrd_start, initrd_end);
  13.             return;
  14.         }

  15. [5]        fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700);
  16.         if (fd >= 0) {
  17.             sys_write(fd, (char *)initrd_start,
  18.                     initrd_end - initrd_start);
  19.             sys_close(fd);
  20.             free_initrd_mem(initrd_start, initrd_end);
  21.         }
  22. }


代码[1]:加载initramfs, initramfs位于地址__initramfs_start处,是内核在编译过程中生成的,initramfs的是作为内核的一部分而存在的,不是 boot loader加载的。前面提到了现在initramfs没有任何实质内容。

代码[2]:判断是否加载了initrd.无论哪种格式的initrd,都会被boot loader加载到地址initrd_start处。

代码[3]:判断加载的是不是cpio-initrd.实际上 unpack_to_rootfs有两个功能一个是释放cpio包,另一个就是判断是不是cpio包, 这是通过最后一个参数来区分的, 0:释放 1:查看。

代码[4]:如果是cpio-initrd则将其内容释放出来到rootfs中。

代码[5]:如果不是cpio-initrd,则认为是一个image-initrd,将其内容保存到/initrd.image中。在后面的image-initrd的处理代码中会读取/initrd.image.

对image-initrd的处理在prepare_namespace函数里,包含了对image-initrd进行处理的代码,相关代码如下:

  1. void __init prepare_namespace(void){
  2. [1]    if (initrd_load())
  3.         goto out;

  4. out:
  5.         umount_devfs("/dev");
  6. [2]        sys_mount(".", "/", NULL, MS_MOVE, NULL);
  7.         sys_chroot(".");
  8.         security_sb_post_mountroot();
  9.         mount_devfs_fs ();
  10. }

代码[1]:执行initrd_load函数,将initrd载入,如果载入成功的话initrd_load函数会将realfs的根设置为当前目录。

代码[2]:将当前目录即realfs的根mount为Linux VFS的根。initrd_load函数执行完后,将真正的文件系统的根设置为当前目录。

阅读(2105) | 评论(0) | 转发(0) |
0

上一篇:C语言逻辑运算,常用寄存器操作

下一篇:没有了

给主人留下些什么吧!~~