您的位置:首页 > 运维架构 > Linux

Arm linux 内核移植及系统初始化过程分析

2011-01-24 16:18 836 查看
本文主要介绍内核

移植

过程中涉及文件

的分布及其用途,以及简单介绍系统

的初始化过程。整个arm

linux

内核的启动可分为三个阶段:第一阶段主要是进行cpu和体系结构的检查、cpu本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基
础设施进行初始化;最后则是更高层次的初始化,如根设备和外部设备的初始化。了解系统的初始化过程,有益于更好地移植内核。

1. 内核移植2. 涉及文件分布介绍

2.1. 内核移植2.2. 涉及的头文件

/linux-2.6.18.8/include

[root@localhost include]# tree -L 1

.

|-- Kbuild

|-- acpi

|-- asm -> asm-arm

|-- asm-alpha

|-- asm-arm ------------------------------->(1)

|-- asm-sparc

|-- asm-sparc64

|-- config

|-- keys

|-- linux ------------------------------->(2)

|-- math-emu

|-- media

|-- mtd

|-- net

|-- pcmcia

|-- rdma

|-- rxrpc

|-- scsi

|-- sound

`-- video

内核移植过程中涉及到的头文件包括处理器相关的头文件(1)和处理器无关的头文件(2)。

2.3. 内核移植2.4. 涉及的源文件

/linux-2.6.18.8/arch/arm

[root@localhost arm]# tree -L 1

.

|-- Kconfig

|-- Kconfig-nommu

|-- Kconfig.debug

|-- Makefile

|-- boot ------------------------------->(2)

|-- common

|-- configs

|-- kernel ------------------------------->(3)

|-- lib

|-- mach-at91rm9200

……

|-- mach-omap1

|-- mach-omap2

|-- mach-realview

|-- mach-rpc

|-- mach-s3c2410 ------------------------------->(4)

|-- mach-sa1100

|-- mach-versatile

|-- mm ------------------------------->(5)

|-- nwfpe

|-- oprofile

|-- plat-omap

|-- tools ------------------------------->(1)

`-- vfp

(1)

/linux-2.6.18.8/arch/arm/tools

[root@localhost tools]# tree -L 1

.

|-- Makefile

|-- gen-mach-types

`-- mach-types

Mach-types 文件定义了不同系统平台的系统平台号。移植linux内核到新的平台上需要对新的平台登记系统平台号。

Mach-types文件格式如下:

# machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number

s3c2410 ARCH_S3C2410 S3C2410 182

smdk2410 ARCH_SMDK2410 SMDK2410 193

之所以需要这些信息,是因为脚本文件linux/arch/arm/tools/gen-mach-types需要linux/arch/tools
/mach-types来产生linux/include/asm-arm/mach-types.h文件,该文件中设置

了一些宏定义,需要这些宏定义来
为目标系统选择合适的代码。

(2)

linux-2.6.18.8/arch/arm/boot/compressed

[root@localhost compressed]# tree -L 1

.

|-- Makefile

|-- Makefile.debug

|-- big-endian.S

|-- head-at91rm9200.S

|-- head.S

|-- ll_char_wr.S

|-- misc.c

|-- ofw-shark.c

|-- piggy.S

`-- vmlinux.lds.in

Head.s
是内核映像的入口代码,是自引导程序。自引导程序包含一些初始化程序,这些程序都是体系结构相关的。在对系统作完初始化设置工作后,调用misc.c文件
中的decompress_kernel()函数

解压缩内核映像到指定的位置,然后跳转到kernel的入口地址。

Vmlinux.lds.in用来生成内核映像的内存配置文件。

(3)

linux-2.6.18.8/arch/arm/kernel

[root@localhost kernel]# tree -L 1

.

|-- Makefile

|-- apm.c

|-- armksyms.c

|-- arthur.c

|-- asm-offsets.c

|-- bios32.c

|-- calls.S

|-- dma.c

|-- ecard.c

|-- entry-armv.S

|-- entry-common.S

|-- entry-header.S

|-- fiq.c

|-- head-common.S

|-- head-nommu.S

|-- head.S

|-- init_task.c

|-- io.c

|-- irq.c

|-- isa.c

|-- module.c

|-- process.c

|-- ptrace.c

|-- ptrace.h

|-- semaphore.c

|-- setup.c

|-- smp.c

|-- sys_arm.c

|-- time.c

|-- traps.c

`-- vmlinux.lds.S

内核入口处也是由一段汇编语言实现的,由head.s和head-common.s两个文件组成。

Head.s 是内核的入口文件, 在head.s的末尾处 #include "head-common.S"。
经过一系列的初始化后,跳转到linux-2.6.18.8/init/main.c中的start_kernel()函数中,开始内核的基本初始化过
程。

/linux-2.6.18.8/init

[root@localhost init]# tree

.

|-- Kconfig

|-- Makefile

|-- calibrate.c

|-- do_mounts.c

|-- do_mounts.h

|-- do_mounts_initrd.c

|-- do_mounts_md.c

|-- do_mounts_rd.c

|-- initramfs.c

|-- main.c

`-- version.c

(4)

/linux-2.6.18.8/arch/arm/mach-s3c2410

[root@localhost mach-s3c2410]# tree -L 1

.

|-- Kconfig

|-- Makefile

|-- Makefile.boot

|-- bast-irq.c

|-- bast.h

|-- clock.c

|-- clock.h

|-- common-smdk.c

|-- common-smdk.h

|-- cpu.c

|-- cpu.h

|-- devs.c

|-- devs.h

|-- dma.c

|-- gpio.c

|-- irq.c

|-- irq.h

|-- mach-anubis.c

|-- mach-smdk2410.c

|-- pm-simtec.c

|-- pm.c

|-- pm.h

|-- s3c2400-gpio.c

|-- s3c2400.h

|-- s3c2410-clock.c

|-- s3c2410-gpio.c

|-- s3c2410.c

|-- s3c2410.h

|-- sleep.S

|-- time.c

|-- usb-simtec.c

`-- usb-simtec.h

这个目录中的文件都是板级相关的,其中比较重要是如下几个:

linux/arch/arm/mach-s3c2410/cpu.c

linux/arch/arm/mach-s3c2410/common-smdk.c

linux/arch/arm/mach-s3c2410/devs.c

linux/arch/arm/mach-s3c2410/mach-smdk2410.c

linux/arch/arm/mach-s3c2410/Makefile.boot

linux/arch/arm/mach-s3c2410/s3c2410.c

3. 处理器和设备4.

这里主要介绍处理器和设备的描述和操作过程。设备描述在linux/arch/arm/mach-s3c2410/devs.c和linux/arch
/arm/mach-s3c2410/common-smdk.c中实现。最后以nand flash为例具体介绍。

4.1. 处理器、设备4.2. 描述

设备描述主要两个结构体完成:struct resource和struct platform_device。

先来看看着两个结构体的定义:

struct resource {

resource_size_t start;

resource_size_t end;

const char *name;

unsigned long flags;

struct resource *parent, *sibling, *child;

};

Resource结构体主要是描述了设备在系统中的起止地址、名称、标志以及为了链式描述方便指向本结构体类型的指针。Resource定义的实例

将被添
加到platform_device结构体对象中去。

struct platform_device {

const char * name;

u32 id;

struct device dev;

u32 num_resources;

struct resource * resource;

};

Platform_device结构体包括结构体的名称、ID号、平台相关的信息、设备的数目以及上面定义的resource信息。
Platform_device结构对象将被直接通过设备操作函数注册导系统中去。具体注册和注销过程在下一节介绍。

4.3. 处理器、设备4.4. 操作

(1) int platform_device_register(struct platform_device * pdev); 注册设备

(2) void platform_device_unregister(struct platform_device * pdev); 注销设备

(3) int platform_add_devices(struct platform_device **devs, int
num);添加设备,通过调用上面两个函数实现。

4.5. 添加Nand flash设备4.6.

下面以nand flash 设备的描述为例,具体介绍下设备的描述和注册过程。

// resource结构体实例s3c_nand_resource 对nand flash 控制器描述,包括控制器的起止地址和标志。

static struct resource s3c_nand_resource[] = {

[0] = {

.start = S3C2410_PA_NAND,

.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,

.flags = IORESOURCE_MEM,

}

};

//platform_device结构体实例s3c_device_nand定义了设备的名称、ID号并把resource对象作为其成员之一。

struct platform_device s3c_device_nand = {

.name = "s3c2410-nand",

.id = -1,

.num_resources = ARRAY_SIZE(s3c_nand_resource),

.resource = s3c_nand_resource,

};

// nand flash 的分区情况,由mtd_partition结构体定义。

static struct mtd_partition smdk_default_nand_part[] = {

[0] = {

.name = "Boot Agent",

.size = SZ_16K,

.offset = 0,

},

[1] = {

.name = "S3C2410 flash partition 1",

.offset = 0,

.size = SZ_2M,

},

[2] = {

.name = "S3C2410 flash partition 2",

.offset = SZ_4M,

.size = SZ_4M,

},

[3] = {

.name = "S3C2410 flash partition 3",

.offset = SZ_8M,

.size = SZ_2M,

},

[4] = {

.name = "S3C2410 flash partition 4",

.offset = SZ_1M * 10,

.size = SZ_4M,

},

[5] = {

.name = "S3C2410 flash partition 5",

.offset = SZ_1M * 14,

.size = SZ_1M * 10,

},

[6] = {

.name = "S3C2410 flash partition 6",

.offset = SZ_1M * 24,

.size = SZ_1M * 24,

},

[7] = {

.name = "S3C2410 flash partition 7",

.offset = SZ_1M * 48,

.size = SZ_16M,

}

};

static struct s3c2410_nand_set smdk_nand_sets[] = {

[0] = {

.name = "NAND",

.nr_chips = 1,

.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),

.partitions = smdk_default_nand_part,

},

};

/* choose a set of timings which should suit most 512Mbit

* chips and beyond.

*/

static struct s3c2410_platform_nand smdk_nand_info = {

.tacls = 20,

.twrph0 = 60,

.twrph1 = 20,

.nr_sets = ARRAY_SIZE(smdk_nand_sets),

.sets = smdk_nand_sets,

};

/* devices we initialise */

// 最后将nand flash 设备加入到系统即将注册的设备集合中。

static struct platform_device __initdata *smdk_devs[] = {

&s3c_device_nand,

&smdk_led4,

&smdk_led5,

&smdk_led6,

&smdk_led7,

};

然后通过smdk_machine_init()函数,调用设备添加函数platform_add_devices(smdk_devs,
ARRAY_SIZE(smdk_devs)) 完成设备的注册。具体过程参见系统初始化的相关部分。

5. 系统初始化

5.1. 系统初始化的主干线

Start_kernel() èsetup_arch() èreset_init() è kernel_thread(init …) è
init() è do_basic_setup() èdriver_init() è do_initcall()

Start_kernel()函数负责初始化内核各个子系统,最后调用reset_init(),启动一个叫做init的内核线程,继续初始化。
Start_kernel()函数在init/main.c中实现。

asmlinkage void __init start_kernel(void)

{

char * command_line;

extern struct kernel_param __start___param[], __stop___param[];

smp_setup_processor_id();

/*

* Need to run as early as possible, to initialize the

* lockdep hash:

*/

lockdep_init();

local_irq_disable();

early_boot_irqs_off();

early_init_irq_lock_class();

/*

* Interrupts are still disabled. Do necessary setups, then

* enable them

*/

lock_kernel();

boot_cpu_init();

page_address_init();

printk(KERN_NOTICE);

printk(linux_banner);

setup_arch(&command_line);

//setup processor and machine and destinate some pointers for
do_initcalls() functions

// for example init_machine pointer is initialized with
smdk_machine_init() function , and //init_machine() function is called
by customize_machine(), and the function is processed by
//arch_initcall(fn). Therefore smdk_machine_init() is issured. by
edwin

setup_per_cpu_areas();

smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */

/*

* Set up the scheduler prior starting any interrupts (such as the

* timer interrupt). Full topology setup happens at smp_init()

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

*/

sched_init();

/*

* Disable preemption - early bootup scheduling is extremely

* fragile until we cpu_idle() for the first time.

*/

preempt_disable();

build_all_zonelists();

page_alloc_init();

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

parse_early_param();

parse_args("Booting kernel", command_line, __start___param,

__stop___param - __start___param,

&unknown_bootoption);

sort_main_extable();

unwind_init();

trap_init();

rcu_init();

init_IRQ();

pidhash_init();

init_timers();

hrtimers_init();

softirq_init();

timekeeping_init();

time_init();

profile_init();

if (!irqs_disabled())

printk("start_kernel(): bug: interrupts were enabled early/n");

early_boot_irqs_on();

local_irq_enable();

/*

* 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();

if (panic_later)

panic(panic_later, panic_param);

lockdep_info();

/*

* Need to run this when irqs are enabled, because it wants

* to self-test [hard/soft]-irqs on/off lock inversion bugs

* too:

*/

locking_selftest();

#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) - "

"disabling it./n",initrd_start,min_low_pfn <<
PAGE_SHIFT);

initrd_start = 0;

}

#endif

vfs_caches_init_early();

cpuset_init_early();

mem_init();

kmem_cache_init();

setup_per_cpu_pageset();

numa_policy_init();

if (late_time_init)

late_time_init();

calibrate_delay();

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();

vfs_caches_init(num_physpages);

radix_tree_init();

signals_init();

/* rootfs populating might need page-writeback */

page_writeback_init();

#ifdef CONFIG_PROC_FS

proc_root_init();

#endif

cpuset_init();

taskstats_init_early();

delayacct_init();

check_bugs();

acpi_early_init(); /* before LAPIC and SMP init */

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

rest_init();

}

分析start_kernel()源码, 其中setup_arch() 和
reset_init()是两个比较关键的函数。下面将具体分析这两个函数。

5.2. setup_arch()函数分析

首先我们来分析下setup_arch()函数。

Setup_arch()函数主要工作是安装cpu和machine,并为start_kernel()后面的初始化函数指针指定值。

其中setup_processor()函数调用linux/arch/arm/kernel/head_common.S
中的lookup_processor_type函数查询处理器的型号并安装。

Setup_machine()函数调用inux/arch/arm/kernel/head_common.S
中的lookup_machine_type(__machine_arch_type)函数根据体系结构号__machine_arch_type,在
__arch_info_begin和__arch_info_end段空间

查询体系结构。问题是__machine_arch_type是在什么时候赋
的初值?__arch_info_begin和__arch_info_end段空间到底放的是什么内容?

__machine_arch_type是一个全局变量,在linux/boot/decompress/misc.c的解压缩函数中得以赋值。

decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg
free_mem_ptr_end_p, int arch_id)

{

__machine_arch_type = arch_id;

}

__arch_info_begin和__arch_info_end段空间到底放的内容由链接器决定,存放是.arch.info.init段的内容。
这个段是通过段属性__attribute__指定的。Grep一下.arch.info.init
得到./include/asm/mach/arch.h:53:
__attribute__((__section__(".arch.info.init"))) = { /
在linux/include/asm-arm/mach/arch.h 中发现MACHINE_START宏定义。

#define MACHINE_START(_type,_name) /

static const struct machine_desc __mach_desc_##_type /

__attribute_used__ /

__attribute__((__section__(".arch.info.init"))) = { /

.nr = MACH_TYPE_##_type, /

.name = _name,

#define MACHINE_END /

};

inux/arch/arm/mach-s3c2410/mach-smdk2410.c中对.arch.info.init段的初始化如下。

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

* to SMDK2410 */

/* Maintainer: Jonas Dietsche */

.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 = s3c24xx_init_irq,

.init_machine = smdk_machine_init,

.timer = &s3c24xx_timer,

MACHINE_END

由此可见在.arch.info.init段内存放了__desc_mach_desc_SMDK2410结构体。初始化了相应的初始化函数指针。问题又
来了, 这些初始化指针函数是什么时候被调用的呢?

分析发现,不一而同。

如s3c24xx_init_irq()函数是通过start_kernel()里的init_IRQ()函数调用init_arch_irq()实现
的。因为在MACHINE_START结构体中 .init_irq =
s3c24xx_init_irq,而在setup_arch()函数中init_arch_irq = mdesc->init_irq,
所以调用init_arch_irq()就相当于调用了s3c24xx_init_irq()。

又如smdk_machine_init()函数的初始化。在MACHINE_START结构体中,函数指针赋值,.init_machine =

smdk_machine_init。而init_machine()函数被linux/arch/arm/kernel/setup.c文件中的
customize_machine()函数调用并被arch_initcall(Fn)宏处
理,arch_initcall(customize_machine)。
被arch_initcall(Fn)宏处理过函数将linux/init/main.c

do_initcalls()函数调用。 具体参看下边的部分。

void __init setup_arch(char **cmdline_p)

{

struct tag *tags = (struct tag *)&init_tags;

struct machine_desc *mdesc;

char *from = default_command_line;

setup_processor();

mdesc = setup_machine(machine_arch_type);//machine_arch_type
=SMDK2410 by edwin

machine_name = mdesc->name;

if (mdesc->soft_reboot)

reboot_setup("s");

if (mdesc->boot_params)

tags = phys_to_virt(mdesc->boot_params);

/*

* If we have the old style. parameters, convert them to

* a tag list.

*/

if (tags->hdr.tag != ATAG_CORE)

convert_to_tag_list(tags);

if (tags->hdr.tag != ATAG_CORE)

tags = (struct tag *)&init_tags;

if (mdesc->fixup)

mdesc->fixup(mdesc, tags, &from, &meminfo);

if (tags->hdr.tag == ATAG_CORE) {

if (meminfo.nr_banks != 0)

squash_mem_tags(tags);

parse_tags(tags);

}

init_mm.start_code = (unsigned long) &_text;

init_mm.end_code = (unsigned long) &_etext;

init_mm.end_data = (unsigned long) &_edata;

init_mm.brk = (unsigned long) &_end;

memcpy(saved_command_line, from, COMMAND_LINE_SIZE);

saved_command_line[COMMAND_LINE_SIZE-1] = '/0';

parse_cmdline(cmdline_p, from);

paging_init(&meminfo, mdesc);

request_standard_resources(&meminfo, mdesc);

#ifdef CONFIG_SMP

smp_init_cpus();

#endif

cpu_init();

/*

* Set up various architecture-specific pointers

*/

init_arch_irq = mdesc->init_irq;

system_timer = mdesc->timer;

init_machine = mdesc->init_machine;

#ifdef CONFIG_VT

#if defined(CONFIG_VGA_CONSOLE)

conswitchp = &vga_con;

#elif defined(CONFIG_DUMMY_CONSOLE)

conswitchp = &dummy_con;

#endif

#endif

}

5.3. rest_init()函数分析

下面我们来分析下rest_init()函数。

Start_kernel()函数负责初始化内核各子系统,最后调用reset_init(),启动一个叫做init的内核线程,继续初始化。在init
内核线程中,将执行下列init()函数的程序。Init()函数负责完成根文件系统的挂接、初始化设备驱动程序和启动用户空间的init进程等重要工
作。

static void noinline rest_init(void)

__releases(kernel_lock)

{

kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);

numa_default_policy();

unlock_kernel();

/*

* The boot idle thread must execute schedule()

* at least one to get things moving:

*/

preempt_enable_no_resched();

schedule();

preempt_disable();

/* Call into cpu_idle with preempt disabled */

cpu_idle();

}

static int init(void * unused)

{

lock_kernel();

/*

* init can run on any cpu.

*/

set_cpus_allowed(current, CPU_MASK_ALL);

/*

* Tell the world that we're going to be the grim

* reaper of innocent orphaned children.

*

* We don't want people to have to make incorrect

* assumptions about where in the task array this

* can be found.

*/

child_reaper = current;

smp_prepare_cpus(max_cpus);

do_pre_smp_initcalls();

smp_init();

sched_init_smp();

cpuset_init_smp();

/*

* Do this before initcalls, because some drivers want to access

* firmware files.

*/

populate_rootfs(); //挂接根文件系统

do_basic_setup(); //初始化设备驱动程序

/*

* check if there is an early userspace init. If yes, let it do all

* the work //启动用户空间的init进程

*/

if (!ramdisk_execute_command)

ramdisk_execute_command = "/init";

if (sys_access((const char __user *) ramdisk_execute_command, 0) !=
0) {

ramdisk_execute_command = NULL;

prepare_namespace();

}

/*

* Ok, we have completed the initial bootup, and

* we're essentially up and running. Get rid of the

* initmem segments and start the user-mode stuff..

*/

free_initmem();

unlock_kernel();

mark_rodata_ro();

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);

(void) sys_dup(0);

if (ramdisk_execute_command) {

run_init_process(ramdisk_execute_command);

printk(KERN_WARNING "Failed to execute %s/n",

ramdisk_execute_command);

}

/*

* We try each of these until one succeeds.

*

* The Bourne shell

can be used instead of init if we are

* trying to recover a really broken machine.

*/

if (execute_command) {

run_init_process(execute_command);

printk(KERN_WARNING "Failed to execute %s. Attempting "

"defaults.../n", execute_command);

}

run_init_process("/sbin/init");

run_init_process("/etc/init");

run_init_process("/bin/init");

run_init_process("/bin/sh");

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

}

5.3.1. 挂接根文件系统

Linux
/init/ramfs.c

void __init populate_rootfs(void)

{

char *err = unpack_to_rootfs(__initramfs_start,

__initramfs_end - __initramfs_start, 0);

if (err)

panic(err);

#ifdef CONFIG_BLK_DEV_INITRD

if (initrd_start) {

#ifdef CONFIG_BLK_DEV_RAM

int fd;

printk(KERN_INFO "checking if image is initramfs...");

err = unpack_to_rootfs((char *)initrd_start,

initrd_end - initrd_start, 1);

if (!err) {

printk(" it is/n");

unpack_to_rootfs((char *)initrd_start,

initrd_end - initrd_start, 0);

free_initrd();

return;

}

printk("it isn't (%s); looks like an initrd/n", err);

fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);

if (fd >= 0) {

sys_write(fd, (char *)initrd_start,

initrd_end - initrd_start);

sys_close(fd);

free_initrd();

}

#else

printk(KERN_INFO "Unpacking initramfs...");

err = unpack_to_rootfs((char *)initrd_start,

initrd_end - initrd_start, 0);

if (err)

panic(err);

printk(" done/n");

free_initrd();

#endif

}

#endif

}

5.3.2. 初始化设备5.3.3. 驱动程序

linux/init/main.c

static void __init do_basic_setup(void)

{

/* drivers will send hotplug events */

init_workqueues();

usermodehelper_init();

driver_init(); /* 初始化驱动程序模型。调用驱动初始化函数初始化子系统。 */

#ifdef CONFIG_SYSCTL

sysctl_init();

#endif

do_initcalls();

}

linux/init/main.c

extern initcall_t __initcall_start[], __initcall_end[];

static void __init do_initcalls(void)

{

initcall_t *call;

int count = preempt_count();

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

char *msg = NULL;

char msgbuf[40];

int result;

if (initcall_debug) {

printk("Calling initcall 0x%p", *call);

print_fn_descriptor_symbol(": %s()",

(unsigned long) *call);

printk("/n");

}

result = (*call)();

……

……

……

}

/* Make sure there is no pending stuff from the initcall sequence */

flush_scheduled_work();

}

分析上面一段代码可以看出,设备的初始化是通过do_basic_setup()函数调用do_initcalls()函数,实现
__initcall_start,
__initcall_end段之间的指针函数执行的。而到底是那些驱动函数怎么会被集中到这个段内的呢?我们知道系统内存空间的分配是由链接器ld读取
链接脚本文件决定。链接器将同样属性的文件组织到相同的段里面去,如所有的.text段都被放在一起。在链接脚本里面可以获得某块内存空间的具体地址。我
们来看下linux-2.6.18.8/arch/arm/kernel/vmlinux.lds.S文件。由于文件过长,只贴出和
__initcall_start, __initcall_end相关的部分。

__initcall_start = .;

*(.initcall1.init)

*(.initcall2.init)

*(.initcall3.init)

*(.initcall4.init)

*(.initcall5.init)

*(.initcall6.init)

*(.initcall7.init)

__initcall_end = .;

从脚本文件中我们可以看出, 在__initcall_start,
__initcall_end之间放置的是属行为(.initcall*.init)的函数数据

。在linux/include/linux/init.h文件中可以知道,(.initcall*.init)属性是由
__define_initcall(level, fn)宏设定的。

#define __define_initcall(level,fn) /

static initcall_t __initcall_##fn __attribute_used__ /

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

#define core_initcall(fn) __define_initcall("1",fn)

#define postcore_initcall(fn) __define_initcall("2",fn)

#define arch_initcall(fn) __define_initcall("3",fn)

#define subsys_initcall(fn) __define_initcall("4",fn)

#define fs_initcall(fn) __define_initcall("5",fn)

#define device_initcall(fn) __define_initcall("6",fn)

#define late_initcall(fn) __define_initcall("7",fn)

#define __initcall(fn) device_initcall(fn)

由此可以判断,所有的设备驱动函数都必然通过*_initcall(fn)宏的处理。以此为入口,可以查询所有的设备驱动。

core_initcall(fn)

static int __init consistent_init(void)
linux/arch/arm/mm/consistent.c

static int __init v6_userpage_init(void)
linux/arch/arm/mm/copypage-v6.c

static int __init init_dma(void) linux/arch/arm/kernel/dma.c

static int __init s3c2410_core_init(void)
linux/arch/arm/mach-s3c2410/s3c2410.c

postcore_initcall(fn)

static int ecard_bus_init(void)
linux/arch/arm/kernel/ecard.c

arch_initcall(fn)

static __init int bast_irq_init(void)
linux/arch/arm/mach-s3c2410/bast-irq.c

static int __init s3c_arch_init(void)
linux/arch/arm/mach-s3c2410/cpu.c

static __init int pm_simtec_init(void)
linux/arch/arm/mach-s3c2410/pm-simtec.c

static int __init customize_machine(void)
linux/arch/arm/kernel/setup.c

subsys_initcall(fn)

static int __init ecard_init(void)
linux/arch/arm/kernel/ecard.c

int __init scoop_init(void)
linux/arch/arm/common/scoop.c

static int __init topology_init(void)
linux/arch/arm/kernel/setup.c

fs_initcall(fn)

static int __init alignment_init(void)
linux/arch/arm/mm/alignment.c

device_initcall(fn)

static int __init leds_init(void)
linux/arch/arm/kernel/time.c

static int __init timer_init_sysfs(void)
linux/arch/arm/kernel/time.c

late_initcall(fn)

static int __init crunch_init(void) arch/arm/kernel/crunch.c

static int __init arm_mrc_hook_init(void)
linux/arch/arm/kernel/traps.c

5.3.4. 启动用户空间的程序

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: