由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 |
联系客服