uClinux cmdline分析以及rootfs挂载

来源:百度文库 编辑:神马文学网 时间:2024/04/29 16:54:49
uClinux cmdline分析以及rootfs挂载2006-12-15 20:14 由start_kernel函数开始 
在arch\armnommu\vmlinux-armv.lds.in里面有如下定义: 
SECTIONS 

. = TEXTADDR; 
.init : { /* Init code and data */ 
_stext = .; 
__init_begin = .; //init段 
*(.text.init) 
__proc_info_begin = .; //proc_info段 
*(.proc.info) 
__proc_info_end = .; 
__arch_info_begin = .; //.arch.info段 
*(.arch.info) 
__arch_info_end = .; 
*(.data.init) 
. = ALIGN(16); 
__setup_start = .; //_setup_init段 
*(.setup.init) 
__setup_end = .; 
__initcall_start = .; 
*(.initcall.init) 
__initcall_end = .; 
__ramdisk_data = .; 
INITRDIMAGE 
__ramdisk_data_end = .; 
. = ALIGN(4096); 
__init_end = .; 


1、init/main.c 
asmlinkage void __init start_kernel(void) 

char * command_line; 
extern char saved_command_line[]; //在arch/armnommu/kernel/setup.c里面定义的,//setup_arch()使用 
/* 
* Interrupts are still disabled. Do necessary setups, then 
* enable them 
*/ 
lock_kernel(); 
printk(linux_banner); 
setup_arch(&command_line); //setup_arch调用parse_cmline生成commandline串,见1.1 
printk("Kernel command line: %s\n", saved_command_line); 
parse_options(command_line);//parse setup_arch生成的commandlie,见1.2 
trap_init(); //接下来是一系列的init 
init_IRQ(); 
sched_init(); 
softirq_init(); 
time_init(); 

/* 
* HACK ALERT! This is early. We're enabling the console before 
* we've done PCI setups etc, and console_init() must be aware of 
* this. But we do want output early, in case something goes wrong. 
*/ 
console_init(); 
#ifdef CONFIG_MODULES 
init_modules(); 
#endif 
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 
mem_init(); 
kmem_cache_sizes_init(); 
pgtable_cache_init(); 

/* 
* For architectures that have highmem, num_mappedpages represents 
* the amount of memory the kernel can use. For other architectures 
* it's the same as the total pages. We need both numbers because 
* some subsystems need to initialize based on how much memory the 
* kernel can use. 
*/ 
if (num_mappedpages == 0) 
num_mappedpages = num_physpages; 

fork_init(num_mappedpages); 
proc_caches_init(); 
vfs_caches_init(num_physpages); 
buffer_init(num_physpages); 
page_cache_init(num_physpages); 
#if defined(CONFIG_ARCH_S390) 
ccwcache_init(); 
#endif 
signals_init(); 
#ifdef CONFIG_PROC_FS 
proc_root_init(); 
#endif 
#if defined(CONFIG_SYSVIPC) 
ipc_init(); 
#endif 
check_bugs(); 
printk("POSIX conformance testing by UNIFIX\n"); 

/* 
* We count on the initial thread going ok 
* Like idlers init is an unlocked kernel thread, which will 
* make syscalls (and thus be locked). 
*/ 
smp_init(); 
rest_init(); //init调用,见2 
}//end start_kernel 

1.1在arch\armnommu\kernel\setup.c中 

setup_arch函数由linux\init\main.c中的start_kernel()调用 
void __init setup_arch(char **cmdline_p) 

struct param_struct *params = NULL; 
struct machine_desc *mdesc; 
char *from = default_command_line; 
//static char default_command_line[COMMAND_LINE_SIZE] __initdata = //CONFIG_CMDLINE; 
int bootmap_size; 
……………………………. 
#ifdef CONFIG_ARCH_EM86XX 
#ifdef CONFIG_SD_USE_BOOTLOADER_MEMCFG 
/* Use the smaller one as size: specified in kernel config, or 
what we detected in the bootloader. */ 
if (is_valid_memcfg(em86xx_memcfg_ptr)) {//loader的memcfg 
unsigned long max_size; 

printk("Found bootloader memory map at 0x%08lx.\n", 
(unsigned long)em86xx_memcfg_ptr); 
max_size = em86xx_memcfg_ptr->dram0_size - 
(em86xx_memcfg_ptr->dram0_fixed_topreserved + 
em86xx_memcfg_ptr->dram0_removable_topreserved); 
max_size &= 0xfff00000; /* Round it to MB boundary */ 

em86xx_kmemsize = ((max_size > em86xx_kmemsize) ? 
em86xx_kmemsize : max_size); 
} else 
printk("Cannot find valid bootloader signature.\n"); 
#else 
/* Use the default value set by kernel configuration */ 
#endif 
#endif 

ROOT_DEV = MKDEV(0, 255); //ROOT_DEV的第一次设定 

setup_processor();// 
mdesc = setup_architecture(machine_arch_type); 
//setup_architecture查找并返回mdesc结构,见1.1.1 
machine_name = mdesc->name; 

if (mdesc->soft_reboot) 
reboot_setup("s");//补丁函数 

if (mdesc->param_offset)//如果param_offset有效,执行如下语句 
params = (struct param_struct*) //params指针,这里因为有效,且为BOOT_PARAM 
(phys_to_virt(mdesc->param_offset)); 

/* 
* Do the machine-specific fixups before we parse the 
* parameters or tags. 
*/ 
if (mdesc->fixup) 
mdesc->fixup(mdesc, params, &from, &meminfo); 

if (params) { 
struct tag *tag = (struct tag *)params;//把前面的到的params指针转换位tag指针,用于解析 

/* 
* Is the first tag the CORE tag? This differentiates 
* between the tag list and the parameter table. 
*/ 
#ifdef CONFIG_SD //如果定义CONFIG_SD则用parse_tag分析,否则用param_param分析 
if (tag->hdr.tag == ATAG_CORE || tag->hdr.tag == ATAG_CMDLINE || tag->hdr.tag == ATAG_NONE) 
#else 
if (tag->hdr.tag == ATAG_CORE) 
#endif 
parse_tags(mdesc->tagtable, mdesc->tagsize, tag); 
else 
parse_params(params); 


if (meminfo.nr_banks == 0) { 
meminfo.nr_banks = 1; 
meminfo.bank[0].start = PAGE_OFFSET;//PHYS_OFFSET; 
meminfo.bank[0].size = MEM_SIZE; 
#ifdef CONFIG_NUMA_EM86XX 
if ((is_valid_memcfg(em86xx_memcfg_ptr)) && 
(em86xx_memcfg_ptr->dram1_size > 0)) { 
++meminfo.nr_banks; 
++numnodes; 
meminfo.bank[1].node = 1; 
meminfo.bank[1].start = DRAM_BASE_2; 
meminfo.bank[1].size = em86xx_memcfg_ptr->dram1_size; 

#endif 


init_mm.start_code = (unsigned long) &_text; 
init_mm.end_code = (unsigned long) &_etext; 
#ifndef CONFIG_RAM_ATTACHED_ROMFS 
init_mm.end_data = (unsigned long) &_edata; 
init_mm.brk = (unsigned long) &_end; 
#else 
init_mm.end_data = (unsigned long) _ramstart; 
init_mm.brk = (unsigned long) _ramstart; 
#endif 

memcpy(saved_command_line, from, COMMAND_LINE_SIZE);//copy ”from” to save_commland_line 
saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; 
parse_cmdline(&meminfo, cmdline_p, from); 
//解析”from”设置meminfo和cmdline_p. “from”为CONFIG_CMDLINE, cmdline_p为指向 
//全局数组char command_line[512]首地址的指针,见1.1.2 
#ifdef CONFIG_ARCH_EM86XX 
/* Record the memory size used by kernel */ 
if (is_valid_memcfg(em86xx_memcfg_ptr)) { 
em86xx_memcfg_ptr->kernel_end += em86xx_kmemsize; 
gen_memcfg_checksum(em86xx_memcfg_ptr); /* Update checksum */ 

#endif 

#if 1 
bootmem_init(&meminfo); 
#else 
bootmap_size= init_bootmem_node( 
NODE_DATA(0), 
memory_start >> PAGE_SHIFT, 
PAGE_OFFSET >> PAGE_SHIFT, 
END_MEM >> PAGE_SHIFT); 

free_bootmem(memory_start, END_MEM - memory_start); 
reserve_bootmem(memory_start, bootmap_size); 
#endif 
paging_init(&meminfo, mdesc); // mem_map is set up here! 
request_standard_resources(&meminfo, mdesc); 

/* 
* Set up various architecture-specific pointers 
*/ 
init_arch_irq = mdesc->init_irq; 

#ifdef CONFIG_BLK_DEV_INITRD 
// let the kernel know where the initrd exists 
#ifdef CONFIG_SD_INITRD_EMBED 

extern int __ramdisk_data, __ramdisk_data_end; 
initrd_start = (unsigned long) &__ramdisk_data; 
initrd_end = (unsigned long) &__ramdisk_data_end - (unsigned long) &__ramdisk_data; 

#else 
initrd_start = CONFIG_SD_INITRD_START; 
initrd_end = CONFIG_SD_INITRD_START + CONFIG_SD_INITRD_SIZE; 
#endif 
#endif 

#ifdef CONFIG_VT 
#if defined(CONFIG_VGA_CONSOLE) 
conswitchp = &vga_con; 
#elif defined(CONFIG_DUMMY_CONSOLE) 
conswitchp = &dummy_con; 
#endif 
#endif 
}//end setup_arch() 

1.1.1 setup_architecture会返回一个machine_desc 
static struct machine_desc * __init setup_architecture(unsigned int nr) 
{//根据nr值来查找相应得machine结构 
extern struct machine_desc __arch_info_begin, __arch_info_end; 
//在arm_info段查找 
………………………. 
return list; 


在arm\armnommu\mach-em86xx\arch.c里面有如下的machine_desc 
在arch\armnommu\mach-em86xx\arch.c中 
MACHINE_START(EM86XX, "EM86XX") 
MAINTAINER("Ho Lee - Sigma Designs, Inc") 
BOOT_MEM(PHYS_OFFSET, PHYS_OFFSET, PHYS_OFFSET) 
// phys_ram, phys_io, virt_io 
// phys_io, virt_io have no meaning 
// but if virt_io is less than 0x00040000, kernel doesn't boot 
// (refer __lookup_architecture_type() at head-armv.S 
// r7 = (virt_io >> 1 
INITIRQ(em86xx_init_irq) 
FIXUP(em86xx_fixup) 
BOOT_PARAMS(DRAM_BASE + 0x200) 
MACHINE_END 

该MACHINE_START的原形为: 
/asm-arm/mach/arch.h中的: 

#define MACHINE_START(_type,_name) \ 
const struct machine_desc __mach_desc_##_type \ //MACHINE_START生成一个machine_desc结构 
__attribute__((__section__(".arch.info"))) = { \//这里是vmlinux.lds.in中arch.info段内容 
nr: MACH_TYPE_##_type, \ 
name: _name, 
#define MAINTAINER(n) 
#define BOOT_MEM(_pram,_pio,_vio) \ 
phys_ram: _pram, \ 
phys_io: _pio, \ 
virt_io: _vio, 
#define BOOT_PARAMS(_params) \ //BOOT_PARAMS地址,为param_offset 
param_offset: _params, 
#define VIDEO(_start,_end) \ 
video_start: _start, \ 
video_end: _end, 
#define DISABLE_PARPORT(_n) \ 
reserve_lp##_n: 1, 
#define BROKEN_HLT /* unused */ 
#define SOFT_REBOOT \ 
soft_reboot: 1, 
#define FIXUP(_func) \ 
fixup: _func, 
#define MAPIO(_func) \ 
map_io: _func, 
#define INITIRQ(_func) \ 
init_irq: _func, 
#define MACHINE_END \ 
}; 
#endif 


1.1.2 Setup_arch()函数中的parse_tag()函数中,有一个tag header与相应函数的关系表 
static const struct tagtable core_tagtable[] __init = { 
{ ATAG_CORE, parse_tag_core }, 
{ ATAG_MEM, parse_tag_mem32 }, 
{ ATAG_VIDEOTEXT, parse_tag_videotext }, 
{ ATAG_RAMDISK, parse_tag_ramdisk }, 
{ ATAG_INITRD, parse_tag_initrd }, 
{ ATAG_SERIAL, parse_tag_serialnr }, 
{ ATAG_REVISION, parse_tag_revision }, 
{ ATAG_CMDLINE, parse_tag_cmdline } 
}; 

static int __init 
parse_tag(const struct tagtable *tbl, int size, const struct tag *t) 

int i; 

for (i = 0; i < size; i++, tbl++) 
if (t->hdr.tag == tbl->tag) {//根据hdr.tag的类型调用相应的parse函数 
tbl->parse(t); 
break; 


return i < size; 

因为在bootloader里面设定为ATAG_CMDLINE,所以会调用相应得parse_tag_cmdline函数 
static int __init parse_tag_cmdline(const struct tag *tag) 

strncpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); 
//把在setup_arch里面找到tag结构的cmdline复制给全局的default_cmd_line 
default_command_line[COMMAND_LINE_SIZE - 1] = '\0'; 
return 0; 



1.2 parse_options函数 
static void __init parse_options(char *line) //(init/main.c) 

…………………… //先“解析”一些特殊字符串,比如带有”init=……”格式的字符串; 

if (checksetup(line)) //根据解析line(这里可以理解为parse_tag_cmdline生成的//default_cmd_line)里面的信息,见1.2.1 

……………………………… 


1.2.1 
static int __init checksetup(char *line) // (init/main.c)checksetup(char *line)会根据函数parse_options分////析的字符串情况分别调用相应的由宏__setup建立的函数 

struct kernel_param *p; 

p = &__setup_start; //所有由__setup宏定义的函数起始地址 见1.2.1.1 
do { 
int n = strlen(p->str); 
if (!strncmp(line,p->str,n)) { 
if (p->setup_func(line+n)) //调用__setup建立的相应函数 
return 1; 
………………………… 

1.2.1.1 
__setup宏(include\linux\init.h)定义如下 
#define __setup(str, fn) \ 
static char __setup_str_##fn[] __initdata = str; \ 
static struct kernel_param __setup_##fn __attribute__((unused)) __initsetup = { __setup_str_##fn, fn } 
#define __initsetup __attribute__ ((unused,__section__ (".setup.init")))//setup.init段 
实例:如果cmdline为”root=……”,则将执行__setup(“root=”,root_dev_setup);// (init/do_mounts.c里面) 
static int __init root_dev_setup(char *line) 

int i; 
char ch; 

ROOT_DEV = name_to_kdev_t(line);//根据字符串解析ROOT_DEV 
memset (root_device_name, 0, sizeof root_device_name); 
if (strncmp (line, "/dev/", 5) == 0) line += 5; 
for (i = 0; i < sizeof root_device_name - 1; ++i) 

ch = line[i]; 
if ( isspace (ch) || (ch == ',') || (ch == '\0') ) break; 
root_device_name[i] = ch; 

return 1; 


__setup("root=", root_dev_setup); //如果line的起始字符为”root=”则调用root_dev_setup 

2、在start_kernel最后会启动init,调用相关的init段 
Inculde\linux\init.h中相关段的定义 
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 
#ifndef NO_TEXT_SECTIONS 
#define __init __attribute__ ((__section__ (".text.init"))) 
#define __exit __attribute__ ((unused, __section__(".text.exit"))) 
#else 
#define __init 
#define __exit __attribute__ ((unused)) 
#endif 
#define __initdata __attribute__ ((__section__ (".data.init"))) 
#define __exitdata __attribute__ ((unused, __section__ (".data.exit"))) 
#define __initsetup __attribute__ ((unused,__section__ (".setup.init"))) 
#define __init_call __attribute__ ((unused,__section__ (".initcall.init"))) 
#define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit"))) 

/* For assembly routines */ 
#define __INIT .section ".text.init","ax" 
#define __FINIT .previous 
#define __INITDATA .section ".data.init","aw" 
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 
如static int __init root_dev_setup(char *line)make时会连接到text.init段 

3、init段中的rootfs初始化 
static void __init mount_root(void) 

#ifdef CONFIG_ROOT_NFS 
if (MAJOR(ROOT_DEV) == NFS_MAJOR 
&& MINOR(ROOT_DEV) == NFS_MINOR) { 
if (mount_nfs_root()) { 
sys_chdir("/root"); 
ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev; 
printk("VFS: Mounted root (nfs filesystem).\n"); 
return; 

printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n"); 
ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0); 

#endif 
devfs_make_root(root_device_name); 
create_dev("/dev/root", ROOT_DEV, root_device_name);//建立目录“dev/root”,将在root_dev_setup()中建立/的ROOTDEV“挂载”到目录/dev/root下面 
#ifdef CONFIG_BLK_DEV_FD 
if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { 
/* rd_doload is 2 for a dual initrd/ramload setup */ 
if (rd_doload==2) { 
if (rd_load_disk(1)) { 
ROOT_DEV = MKDEV(RAMDISK_MAJOR, 1); 
create_dev("/dev/root", ROOT_DEV, NULL); 

} else 
change_floppy("root floppy"); 
mount_block_root("/dev/root", root_mountflags);//将目录”dev/root”挂载为根文件系统 

根据《解析 Linux 中的 VFS 文件系统机制》,在执行static void __init mount_root(void)之前,目录”/dev”,”/root”已经建立好了,create_dev是vfs文件系统操作的封装 


static void __init mount_block_root(char *name, int flags) 

char *fs_names = __getname(); 
char *p; 

get_fs_names(fs_names); 
retry: 
for (p = fs_names; *p; p += strlen(p)+1) { 
int err = sys_mount(name, "/root", p, flags, root_mount_data); //将name mount到”/root”下面 
switch (err) { 
case 0: 
goto out; 
case -EACCES: 
flags |= MS_RDONLY; 
goto retry; 
case -EINVAL: 
#ifdef CONFIG_SD 
// by Ho Lee 01/20/2003 
// newly introducted mount_block_root() function has some problem 
// when this code tries to mount ISO9660 filesystem and it fails, 
// it return -ENOENT. without this code, kernel panics 
case -ENOENT: 
#endif 
continue; 

/* 
* Allow the user to distinguish between failed open 
* and bad superblock on root device. 
*/ 
printk ("VFS: Cannot open root device \"%s\" or %s\n", 
root_device_name, kdevname (ROOT_DEV)); 
printk ("Please append a correct \"root=\" boot option\n"); 
panic("VFS: Unable to mount root fs on %s", 
kdevname(ROOT_DEV)); 

panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV)); 
out: 
putname(fs_names); 
************************************************************************在 Linux 下,设定一个进程的当前工作目录是通过系统调用 sys_chdir() 进行的。初始化期间,Linux 在将 hda1 上的 ext2 文件系统安装到了 "/root" 上后,通过调用 sys_chdir("/root") 将当前进程,也就是 init_task 进程的当前工作目录(pwd)设定为 ext2 文件系统的根目录。因为以后 Linux 世界中的所有进程都由这个 init_task 进程派生出来,无一例外地要继承该进程的根目录,如果是这样,意味着用户进程从根目录搜索某一目录时,实际上是从 VFS 的根目录开始的,而事实上却是从 ext2 的根文件开始搜索的。这个矛盾的解决是靠了在调用完 mount_root() 函数后,系统调用的下面函数: 
sys_chdir("/root"); 
ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev; 
printk("VFS: Mounted root (%s filesystem)%s.\n", 
current->fs->pwdmnt->mnt_sb->s_type->name, 
(current->fs->pwdmnt->mnt_sb->s_flags & MS_RDONLY) ? " readonly" : ""); 


4、rootfs挂载实例 
Rootfs挂载实例:挂载硬盘第二分区(romfs) 
在make linux-config里面 
1) System setup 里面定义kernel_cmdlin root=/dev/hda; 
2) 在do-mounts.c里面的, 
static struct dev_name_struct { 
const char *name; 
const int num; 
} root_dev_names[] __initdata = { 
{ "nfs", MKDEV(NFS_MAJOR, NFS_MINOR) }, 
//{ "hda", 0x0300 }, 
//modified by yangm 
{ "hda", 0x0302 },//partition 0 change to partition 2 
{ "hdb", 0x0340 }, 
{ "loop", 0x0700 }, 
{ "hdc", 0x1600 }, 
{ "hdd", 0x1640 }, 
{ "hde", 0x2100 }, 
{ "hdf", 0x2140 }, 
{ "hdg", 0x2200 }, 
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 

上面的表项将用于 name_to_kdev_t解析,这里root=/dev/had解析得到的ROOTDEV=0x0302 
static int __init root_dev_setup(char *line) 

//add by yangm printk 
。。。。。。。。。。。。。。。。。。。。。。。。。。。 
printk("root_dev_setup() executed command line is %s\n",line); 
ROOT_DEV = name_to_kdev_t(line); 
。。。。。。。。。。。。。。。。。。。。。。。。。。。。 
return 1; 


kdev_t __init name_to_kdev_t(char *line) 

int base = 0, offs; 
char *end; 

if (strncmp(line,"/dev/",5) == 0) { 
struct dev_name_struct *dev = root_dev_names; 
line += 5; 
do { 
int len = strlen(dev->name); //这里为“hda” 
if (strncmp(line,dev->name,len) == 0) { 
line += len; 
base = dev->num; 
break; 

dev++; 
} while (dev->name); 

offs = simple_strtoul(line, &end, base?10:16); 
if (*end) 
offs = 0; 
return to_kdev_t(base + offs);//返回”hda”对应得”0x0302” 



因为是先执行setup.init段,在后面的blkmem.c编译好的blkmem.o驱动程序里面blk_init()函数设定的ROOT_DEV将覆盖上面建立的ROO_DEV值,注掉里面有关ROOT_DEV的设定 

然后make linux,将其 
Boot>ide romfs 0 0 
然后 
Boot>ide