Chinaunix首页 | 论坛 | 博客
  • 博客访问: 960036
  • 博文数量: 173
  • 博客积分: 3436
  • 博客等级: 中校
  • 技术积分: 1886
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-07 09:29
文章分类

全部博文(173)

文章存档

2016年(6)

2015年(10)

2014年(14)

2013年(8)

2012年(36)

2011年(63)

2010年(19)

2009年(17)

分类: LINUX

2011-02-22 21:35:56

Linux initrd

Author:    jimmy.li

Date:      2007-06-12
-----------------------------------------
Linux initrd
  如果linux嵌入式系统采用initrd作为实际根文件系统可用之前的初始根文件系统,那么一般在bootloader引导过程中,除了会把kernel从flash中加载到RAM,同时也会把initrd映像从flash中加载到RAM中。那么kernel在加载这个initrd根文件系统时要去打开/dev/ram设备,然后要read里面的数据,以判断这个映象是什么文件系统(minix? ext2?),这就需要内核知道initrd映像放置在内存中的位置和其大小。
    本文重点不在分析内核是如何挂载initrd的,而是在于分析内核挂载initrd前,如何获取initrd映像在内存中的位置等相关数据结构信息。

  在
fixup_xxx(xxx为你的平台名,添加新平台时由FIXUP宏指定)通过调用setup_ramdisksetup_initrd函数对initrd文件被放置在内存中的位置及大小进行指定

static void __init

fixup_xxx(struct machine_desc *desc, struct param_struct *params,

                   char **cmdline, struct meminfo *mi)

{

         SET_BANK (0, 0xa0000000, 64*1024*1024);

         mi->nr_banks      = 1;

 

         setup_ramdisk (1, 0, 0, 2*1024);

         setup_initrd (__phys_to_virt(0xa1000000), 1*1024*1024);

         ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);

}

 

  setup_ramdisk()定义于arch/arm/kernel/setup.c中:

void __init

setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz)

{

#ifdef CONFIG_BLK_DEV_RAM

         extern int rd_size, rd_image_start, rd_prompt, rd_doload;

 

         rd_image_start = image_start;

         rd_prompt = prompt;

         rd_doload = doload;

 

         if (rd_sz)

                   rd_size = rd_sz;

#endif

}

 

  其中的rd_doloadrd_image_startrd_prompt变量定义于init/do_mounts.c

int __initdata rd_doload;            /* 1 = load RAM disk, 0 = don't load */

int __initdata rd_image_start;           /* starting block # of image */

int __initdata rd_prompt = 1;         /* 1 = prompt for RAM disk, 0 = don't prompt */

 

   rd_size定义于ramdisk的驱动代码里(drivers/block/rd.c

         int rd_size = CONFIG_BLK_DEV_RAM_SIZE;          /* Size of the RAM disks */

 

  同样Setup_ramdisk()也是定义于arch/arm/kernel/setup.c中:

/*

 * initial ram disk

 */

void __init setup_initrd(unsigned int start, unsigned int size)

{

#ifdef CONFIG_BLK_DEV_INITRD

         if (start == 0)

                   size = 0;

         phys_initrd_start = __virt_to_phys(start);

         phys_initrd_size = size;

#endif

}

 

 

    其中phys_initrd_startphys_initrd_size定义于arch/arm/kernel/setup.c中:

unsigned long phys_initrd_start __initdata = 0;

unsigned long phys_initrd_size __initdata = 0;


phys_initrd_start表示initrd的起始位转置,它是一个物理地址;phys_initr_size表示initrd的大小,单位是字节。

   内核对/dev/ram设备进行read时,会调用initrd_read函数。

static ssize_t initrd_read(struct file *file, char *buf,

                           size_t count, loff_t *ppos)

{

        int left;

 

        left = initrd_end - initrd_start - *ppos;

        if (count > left) count = left;

        if (count == 0) return 0;

        if (copy_to_user(buf, (char *)initrd_start + *ppos, count))

                return -EFAULT;

        *ppos += count;

        return count;

}


其中initrd_startinitrd_enddrivers/block/rd.c中被定义:

unsigned long initrd_start,  initrd_end;


Initrd_start表示initrd映像在内存中的起始位置,而initrd_end则表示initrd映像在内存中的结束位置,这两个地址都是虚拟地址。看起来Initrd_start与前面的phys_inird_start应该是有联系的,只不过一个是虚拟地址,一个是物理地址,它们在arch/arm/mm/init.c中的bootmem_init中建立了联系:

/*

 * Initialise the bootmem allocator for all nodes.  This is called

 * early during the architecture specific initialisation.

 */

void __init bootmem_init(struct meminfo *mi)

{

   ….

#ifdef CONFIG_BLK_DEV_INITRD

        if (phys_initrd_size && initrd_node >= 0) {

                reserve_bootmem_node(NODE_DATA(initrd_node), phys_initrd_start,

                                     phys_initrd_size);

                initrd_start = __phys_to_virt(phys_initrd_start);

                initrd_end = initrd_start + phys_initrd_size;

        }

#endif

 

        if (map_pg != bootmap_pfn + bootmap_pages)

                BUG();

 

}

 

 

加载 initrd 是通过调用initrd_load函数(init/do_mounts.c)来实现的。initrd_load函数调用了rd_load_image函数(init/do_mounts.c),它通过调用identify_ramdisk_image函数(init/do_mounts.c)来确定要加载哪个 RAM 磁盘。这个函数会检查映像文件的 magic 号来确定它是 minuxetc2romfscramfs gzip 格式。在返回到 initrd_load_image 之前,它还会调用crd_load函数(init/do_mounts.c),这个函数负责为 RAM 磁盘分配空间,并计算循环冗余校验码(CRC),然后对 RAM 磁盘映像进行解压,并将其加载到内存中。现在,我们在一个适合挂载的块设备中就有了这个 initrd 映像。

阅读(1154) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~