ARM linxu启动过程分析(四)

1 Linux内核启动第三阶段start_kernel

内核从现在开始就进入C语言部分,内核启动第三阶段从init/main.c文件中的start_kernel()函数开始,到该函数结束。这一阶段对整个系统内存、cache、信号、设备等进行初始化,最后生成init进程后,调用cpu_idle()完成内核启动的第三阶段。Start_kernel()中调用了一系列的初始化函数,以完成kernel本身的设置。这些动作有些是公共的,有的则是需要配置才会执行的。

asmlinkage void __initstart_kernel(void)

{

char * command_line;

extern struct kernel_param__start___param[], __stop___param[];

/*中断是禁止的,作必要的设置之后,使能中断*/

lock_kernel();

/*如果内核配置成支持抢占,那么在这里禁止抢占,将0号进程的init_thread_info.preempt_count1,如果配置成不支持抢占,那么内核全局自旋锁kernel_flag上锁*/

page_address_init();   /*ARM9不支持高端内存(>896MB,一般嵌入式产品也不会用高端内存,所以这里是空函数*/

printk(KERN_NOTICE);

/*linux_banner的内容打印到log_buf缓冲区中去,等到串口或者其它终端初始化之后,在一次性打印到终端上去*/

printk(linux_banner);

setup_arch(&command_line);

/*   setup_arch()原型在arch/arm/kernel/setup.c中,根据处理器硬件平台设置系统;

解析linux命令行参数,设置0号进程的内存描述结构init_mm,系统内存管理初始化,

统计并注册系统各种资源,其它项目的初始化 */

setup_per_cpu_areas();//为系统中的每个cpuper_cpu变量申请空间

/* Mark the boot cpu "online"so that it can call console drivers in

 *printk() and can access its per-cpu storage.      */

smp_prepare_boot_cpu();

/*开启任何中断之前,初始化调度器.Full topology setup happens at smp_init()

 * time- but meanwhile we still have a functioning scheduler.

 初识化每个处理器的可运行进程队列,设置系统初始化进程,即0号进程;*/

sched_init();

preempt_disable();  //禁止抢占

build_all_zonelists();//建立系统内存页区(zone)链表

page_alloc_init();

printk(KERN_NOTICE "Kernelcommand line: %s/n", saved_command_line);

parse_early_param();//解析早期格式的内核参数

parse_args("Bootingkernel", command_line, __start___param,

          __stop___param- __start___param,

          &unknown_bootoption);//解析新格式内核参数

sort_main_extable();/* 排序内核内建的异常表,将__start___ex_table__stop___ex_table之间的*(__ex_table)区中的structexception_table_entry型全局结构变量按insn成员变量值从小到大排序,即将可能导致缺页异常的指令按期指令二进制代码值从小到大排序。*/

trap_init();/* Copy thevectors, stubs and kuser helpers (in entry-armv.S)

 * intothe vector page, mapped at 0xffff0000, and ensure these

 * arevisible to the instruction stream.  */

 /*  * Copy signal return handlers into the vector page, and

 * setsigreturn to be a pointer to these. */

rcu_init();

 /* *Initializes rcu mechanism.  Assumed to be called early.

 * Thatis before local timer(SMP) or jiffie timer (uniproc) is setup.

 * Notethat rcu_qsctr and friends are implicitly

 *initialized due to the choice of ``0'' for RCU_CTR_INVALID. */

init_IRQ();/*中断初始化,初始化系统中支持的最大可能的中断描述结构structirqdesc变量数组irq_desc[NR_IRQS],把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量bad_irq_desc,并初始化该中断的链表表头成员结构变量pend.*/

pidhash_init();/*

 * Thepid hash table is scaled according to the amount of memory in the

 *machine.  From a minimum of 16 slots up to 4096 slots at onegigabyte or

 *more.设置系统中每种pidhash表中的hash链表数的移位值全局变量pidhash_shift,pidhash_shift设置为mini(12);分别为每种hash表的连续hash链表表头结构空间申请内存,把申请到的内存虚拟基址分别传给pid_hash[n](n=0~3),并将每种hash表中的每个hash链表表头结构structhlist_head中的first成员指针设置成NULL.

 */

init_timers();

softirq_init();

time_init();//检查系统定时器描述结构structsys_timer全局变量system_timer是否为空,如果为空将其指向dummy_gettimeoffset()函数。

/*

 * HACKALERT! 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();//初始化系统控制台结构,该函数执行后可调用printk()函数将log_buf

//中符合打印级别要求的系统信息打印到控制台上。

if (panic_later)

        panic(panic_later,panic_param);

profile_init();//对系统剖析做相关初始化,系统剖析用于系统调用

local_irq_enable(); //使能IRQ

#ifdef CONFIG_BLK_DEV_INITRD

if (initrd_start &&!initrd_below_start_ok &&

              initrd_start <min_low_pfn << PAGE_SHIFT) {

        printk(KERN_CRIT"initrd overwritten (0x%08lx < 0x%08lx) - "

           "disablingit./n",initrd_start,min_low_pfn << PAGE_SHIFT);

        initrd_start= 0;

}

#endif

vfs_caches_init_early();

mem_init();

//该函数执行完成之后,就不能再用像alloc_bootmem(),alloc_bootmem_low(),alloc_bootmem_pages()等申请低端内存的函数来申请内存,也就不能再申请大块连续的物理内存了。

kmem_cache_init();//slab分配器的相关初始化,执行高速缓存内存管理

setup_per_cpu_pageset();

numa_policy_init();

if (late_time_init)

        late_time_init();

calibrate_delay();/*

 * Thisis the number of bits of precision for the loops_per_jiffy. Each

 * bittakes on average 1.5/HZ seconds.  This (like the original) is alittle

 *better than 1%

 */

pidmap_init();

pgtable_cache_init();

prio_tree_init();

anon_vma_init();

#ifdef CONFIG_X86

if (efi_enabled)

       efi_enter_virtual_mode();

#endif

fork_init(num_physpages);//执行进程创建相关的初始化

proc_caches_init();

buffer_init();

unnamed_dev_init();

key_init();

security_init();  //security_init - initializes the security framework

vfs_caches_init(num_physpages);

radix_tree_init();

signals_init();

/*调用函数kmem_cache_create("sigqueue",sizeof(struct sigqueue), __alignof__(struct sigqueue), SLAB_PANIC,NULL, NULL); 为信号队列结构structsigqueque创建高速缓存内存描述结构kmem_cache_t变量,名字叫”sigqueue”,不要求其对象按处理器硬件cacheline大小对齐,没有定义其对象的构造和析构函数,将创建的kmem_cache_t结构变量的地址传给全局指针sigqueue_cachep.*/

/* rootfs populating mightneed page-writeback */

page_writeback_init();

#ifdef CONFIG_PROC_FS

proc_root_init();

 //在系统支持proc文件系统即配置了CONFIG_PROC_FS选项时被调用

#endif

cpuset_init();

check_bugs();

acpi_early_init(); /* beforeLAPIC and SMP init */

/* Do the rest non-__init'ed,we're now alive */

rest_init();

/*   创建init()进程,即1号进程,启动调度器,然后系统启动进程即0号进程进入空闲;*/

}

下面来分析几个非常重要的函数:

首先主要分析setup_arch()函数,定义在arch/arm/kernel/setup.c文件中,它完成体系结构相关的初始化,内核移植的过程一般也就到此函数为止了,其余的就只是一些相关的外设驱动。

Setup_arch()中的第一个主要函数为setup_processor(),它只是简单的遍历__proc_info_begin开始的proc_info_list找到相应的表项,并从中得到proc_info结构。注意这些proc_info_list结构定义在arch/arm/mm/proc_arm920.S文件中。

然后就是setup_machine函数,它根据machine_arch_type[机器ID],在__arch_info_begin中找到对应的machine_desc表项。对于FS2410(实际上这里用的是SMDK2410的相关内容,因为二者基本相同,所以在移植的时候没有修改这一部分,用的还是SMDK2410的机器ID)arch/arm/mach-s3c2410/mach-smdk2410.c中定义为:

MACHINE_START(SMDK2410,"SMDK2410") /* @TODO: request a new identifier and switch

                         *to SMDK2410 */

/* Maintainer: Jonas Dietsche*/

.phys_ram    = S3C2410_SDRAM_PA,

.phys_io  =S3C2410_PA_UART,

.io_pg_offst   =(((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

.boot_params =S3C2410_SDRAM_PA + 0x100,

.map_io        = smdk2410_map_io,

.init_irq  =smdk2410_init_irq,

 .init_machine  = sdmk2410_init,

.timer           = &s3c24xx_timer,

MACHINE_END

接着需要解释的就是u-boot给内核传参数了,linux-2.6.14没有通过u-boot引导时的thekernel的第三个参数传递u-boot传递给内核参数的位置,即boot_params,而是通过指定机器描述结构的mdesc->boot_params来制订u-boot传给内核参数的位置,这时需要先就参数进行分析,由parse_tags(tags);来完成,其中tags= phys_to_virt(mdesc->boot_params);

paging_init(&meminfo,mdesc);是一个非常重要的函数尤其在移植内核时,主要完成页表的初始化。----〉调用memtable_init(mi);初始化内存页表----create_mapping(init_maps)创建页目录项荷页表项。

另外在paging_init()函数中还会调用mdesc->map_io();来完成平台相关的IO映射。

接着就是 设置平台相关的指针

init_arch_irq =mdesc->init_irq; //start_kernel接下来的init_IRQ()函数中会掉用该函数完成平台相关的中断初始化。

system_timer = mdesc->timer;//start_kernel接下来的time_init()函数中会掉用该结构体中的内容完成平台相关的定时器初始化。

init_machine =mdesc->init_machine;

setup_arch()的分析到此结束。

这里再简单分析一下reset_init()函数:

static void noinlinerest_init(void)

__releases(kernel_lock)

{

kernel_thread(init, NULL,CLONE_FS | CLONE_SIGHAND);  //启动init线程

numa_default_policy();

unlock_kernel();

preempt_enable_no_resched();

/* The boot idle thread mustexecute schedule()at least one to get things moving: */

schedule();  //启动调度器

cpu_idle();//cpu空闲,实际上就是启动进程进入空闲状态,系统交给调度器来管理

}

再来看一下启动init线程的过程:

pid_t kernel_thread(int(*fn)(void *), void *arg, unsigned long flags)

{

struct pt_regs regs;

memset(&regs, 0,sizeof(regs));

regs.ARM_r1 = (unsignedlong)arg;

regs.ARM_r2 = (unsignedlong)fn;

regs.ARM_r3 = (unsignedlong)do_exit;

regs.ARM_pc = (unsignedlong)kernel_thread_helper;

regs.ARM_cpsr = SVC_MODE;

returndo_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);

//调用do_fork函数创建init内核线程。

}

接着我们来看一下这个init线程的内容,它是init/main.c中的一个函数,代码如下:

static int init(void *unused)

{

lock_kernel();

set_cpus_allowed(current,CPU_MASK_ALL);

…… …… …… …….

/* Do this before initcalls,because some drivers want to access firmware files.      */

populate_rootfs();

do_basic_setup();//很重要的一个函数,做一些基本的设置

…… …… …… …….

/* Ok, we have completed theinitial bootup, and we're essentially up and running. Get rid of theinitmem segments and start the user-mode stuff..  */

free_initmem();

unlock_kernel();

system_state =SYSTEM_RUNNING;

numa_default_policy();

if (sys_open((const char__user *) "/dev/console", O_RDWR, 0) < 0)//打开终端

       printk(KERN_WARNING"Warning: unable to open an initial console./n");

(void) sys_dup(0);   //dup文件描述符1

(void) sys_dup(0);   //dup文件描述符2

…… …… …… …….

run_init_process("/sbin/init"); //启动init进程

run_init_process("/etc/init");  //启动init进程

run_init_process("/bin/init");  //启动init进程

run_init_process("/bin/sh");   //启动shell

panic("No init found. Try passing init= option to kernel.");

}

/*机器已经初始化完成,cpu子系统已经起来正在运行,内存和进程管理已经工作,接下来要做些真正的工作了 */

static void __initdo_basic_setup(void)

{

/* drivers will send hotplugevents */

init_workqueues(); //初始化工作队列

usermodehelper_init();//用户模式help程序

driver_init();  //设备初始化

#ifdef CONFIG_SYSCTL

sysctl_init();

#endif

/* Networking initializationneeds a process context */

sock_init();

do_initcalls(); //非常重要的一个函数,会在这里执行驱动模块加载函数,也就module_init()的函数。

}

do_initcalls()的一部分代码如下:

static void __initdo_initcalls(void)

{

initcall_t *call;

int count = preempt_count();

for (call = __initcall_start;call < __initcall_end; call++)

{

              char *msg;

              (*call)();//真正执行驱动模块加载函数的地方

}

最后一个要分析的问题是:关于设备驱动什么时候调用,以网卡驱动为例来进行分析,这里采用的是CS8900A

驱动里面的模块注册函数module_init(xxx_init);

网卡注册过程:

cs8900_init()---register_netdve(&cs8900_dev)---register_netdevice()

cs8900_dev定义如下:

int cs8900_probe (structnet_device *dev);

static struct net_devicecs8900_dev =

{

        init:cs8900_probe

};

其调用过程:

Register_netdev(structnet_device *dev)----register_netdevice(structnet_device * dev)—dev->init

这样实际在启动过程中会执行cs8900_probe,里面会对网卡进行侦测,注册中断,DMA中断等一些操作。

cs8900_init()会在内核启动的时候被执行:

Start_kernel()---reset_init()---〉起了init内核线程----do_basic_setup()---do_initcalls()

--------for(call=__initcall_start;call<__initcall_end;call++){

(*call)();--------------此处会执行cs8900_init,以及其他驱动程序的module_init函数。

}

Include/linux/init.h中关于initcall的定义,这里简单描述,如果感兴趣可以查阅该文件。

#ifndef MODULE

#ifndef __ASSEMBLY__

#define__define_initcall(level,fn) /

staticinitcall_t __initcall_##fn __attribute_used__ /

__attribute__((__section__(".initcall"level ".init"))) = fn

//Initcall定义了7个级别,我们关系的设备initcall位于级别6

#definecore_initcall(fn)            __define_initcall("1",fn)

#definepostcore_initcall(fn)             __define_initcall("2",fn)

#definearch_initcall(fn)            __define_initcall("3",fn)

#definesubsys_initcall(fn)         __define_initcall("4",fn)

#definefs_initcall(fn)                __define_initcall("5",fn)

#definedevice_initcall(fn)             __define_initcall("6",fn)

#definelate_initcall(fn)             __define_initcall("7",fn)

 

#define__initcall(fn)  device_initcall(fn)

 

#define__exitcall(fn) /

staticexitcall_t __exitcall_##fn __exit_call = fn

#define console_initcall(fn)/

static initcall_t__initcall_##fn /

__attribute_used____attribute__((__section__(".con_initcall.init")))=fn

#define security_initcall(fn)/

static initcall_t__initcall_##fn /

__attribute_used____attribute__((__section__(".security_initcall.init"))) =fn

#define __setup_param(str,unique_id, fn, early)                 /

static char__setup_str_##unique_id[] __initdata = str; /

static structobs_kernel_param __setup_##unique_id   /

       __attribute_used__                          /

       __attribute__((__section__(".init.setup")))    /

       __attribute__((aligned((sizeof(long)))))   /

        ={ __setup_str_##unique_id, fn, early }

#define__setup_null_param(str, unique_id)   __setup_param(str, unique_id, NULL, 0)

#define __setup(str,fn)             __setup_param(str, fn, fn, 0)

#define__obsolete_setup(str)     __setup_null_param(str,__LINE__)

#define early_param(str,fn)             __setup_param(str, fn, fn, 1)

void __initparse_early_param(void);

#endif /* __ASSEMBLY__ */

#definemodule_init(x)      __initcall(x);

#definemodule_exit(x)     __exitcall(x);

#else /* MODULE   定义为模块 */

#definecore_initcall(fn)            module_init(fn)

#definepostcore_initcall(fn)             module_init(fn)

#definearch_initcall(fn)            module_init(fn)

#definesubsys_initcall(fn)         module_init(fn)

#definefs_initcall(fn)                module_init(fn)

#definedevice_initcall(fn)         module_init(fn)

#definelate_initcall(fn)             module_init(fn)

#definesecurity_initcall(fn)       module_init(fn)

 

#definemodule_init(initfn)               /

static inline initcall_t__inittest(void)            /

{ return initfn;}                             /

int init_module(void)__attribute__((alias(#initfn)));

#definemodule_exit(exitfn)                            /

static inline exitcall_t__exittest(void)           /

{ return exitfn;}                             /

void cleanup_module(void)__attribute__((alias(#exitfn)));

#define __setup_param(str,unique_id, fn)       /* nothing */

#define__setup_null_param(str, unique_id)    /* nothing */

#define __setup(str, func)               /* nothing */

#define __obsolete_setup(str)                /* nothing */

#endif



你可能感兴趣的:(thread,timer,struct,Module,assembly,null)