Linux 初始化过程:
从/init/main.c中的start_kernel()开始:
该函数中,主要做了下列事情:
(1)先是
boot_cpu_init();
初始化了启动相关的内容后
然后
printk(KERN_NOTICE);
printk(linux_banner);
打印内核信息,比如我这里的:
Linux version 2.6.22.2 (crifan@localhost.localdomain) (gcc version 4.3.2 (GCC) ) #89 Fri May 15 11:25:09 CST 2009
(2)
其后主要初始化了中断,时钟等:
init_IRQ();
init_timers();
softirq_init();
(3)
再打印出命令行:
printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);
如此处的:
Kernel command line: root=/dev/mtdblock2 rw init=/linuxrc console=ttyS0,115200 mem=64M rootfstype=jffs2
(4)
后来才是调用
console_init();
去初始化Uart的,就是调用uart的probe函数。
(5)
先预初始化:
cpuset_init_early();
(6)
初始化内存:
mem_init();
(7)
初始化buffer:
buffer_init();
再初始化cpu(组):
cpuset_init();
(8)
用check_bugs();
去测试CPU的相关情况:
CPU: Testing write buffer coherency: ok
(9)
最后调用:
rest_init();
去启动线程
kernel_init
{
。。。
do_basic_setup();
。。。
init_post();
}
其中调用
do_basic_setup();
{
。。。
driver_init();
。。。
do_initcalls();
。。。
}
去初始化Linux kernel相关的内容
其中
driver_init()
{
/* These are the core pieces */
devices_init();
buses_init();
classes_init();
firmware_init();
hypervisor_init();
/* These are also core pieces, but must come after the
* core core pieces.
*/
platform_bus_init();
system_bus_init();
cpu_dev_init();
memory_dev_init();
attribute_container_init();
}
初始化真正的系统的
device,bus,class等等内容,
然后的
do_initcalls()
{
//从__Start 开始 到 __End 结束,
//以此调用之前内核中相关的__init前缀的那些初始化函数
}
其中,这些用__init标明的函数,可以在system.map中看到对应的信息,
包括对应的变量和其对应的地址
c001d164 t __initcall_ptrace_break_init1
c001d164 T __initcall_start
c001d168 t __initcall_consistent_init1
c001d16c t __initcall_sysctl_init1
c001d170 t __initcall_init_jiffies_clocksource1
c001d174 t __initcall_pm_init1
c001d178 t __initcall_ksysfs_init1
。。。。。。。。。。。。。。。
c001d194 t __initcall_sock_init1
c001d198 t __initcall_netlink_proto_init1
c001d19c t __initcall_kobject_uevent_init2
c001d1a0 t __initcall_amba_init2
c001d1a4 t __initcall_tty_class_init2
c001d1a8 t __initcall_vtconsole_class_init2
c001d1ac t __initcall_customize_machine3
。。。。。。。。。。。
c001d1b0 t __initcall_clk_init3
c001d1b4 t __initcall_dma_init3
c001d1bc t __initcall_topology_init4
c001d1c0 t __initcall_param_sysfs_init4
。。。。。。。。。。。。。
c001d200 t __initcall_init_pipe_fs5
c001d204 t __initcall_eventpoll_init5
。。。。。。。。。。。。。。
c001d24c t __initcall_init6
c001d250 t __initcall_kallsyms_init6
c001d254 t __initcall_utsname_sysctl_init6
c001d258 t __initcall_init_per_zone_pages_min6
c001d25c t __initcall_pdflush_init6
。。。。。。。。。。
c001d2e8 t __initcall_loop_init6
c001d2ec t __initcall_net_olddevs_init6
c001d2f0 t __initcall_loopback_init6
c001d2f4 t __initcall_init_mtd6
c001d2f8 t __initcall_afs_parser_init6
c001d2fc t __initcall_init_mtdchar6
c001d300 t __initcall_init_mtdblock6
c001d304 t __initcall_init_nftl6
。。。。。。。。。。。。
c001d388 t __initcall_ip_auto_config7
。。。。。。。。。。。
可以看出,这些函数经过系统处理后,
加了前缀__initcall_和后缀的数字,看起来是表示启动的阶段。
也就是加了和函数对应的一个个变量(标示)。
当然,也可以找到函数对应的首地址,比如
变量
c001d1b0 t __initcall_clk_init3
对应的函数clk_init放在c000e624 :
c000e624 t clk_init
然后,调用
init_post()
{
1.打开console:
sys_open("/dev/console", O_RDWR, 0)
2.运行命令:
run_init_process(ramdisk_execute_command);
和
run_init_process(execute_command);
3.运行系统初始化文件:
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
}
最后,我们才进入我们熟悉的嵌入式Linux环境,
才能输入用户名和密码(如果有的话),比如:
Welcome to Embedded Linux
(none) login: root
Dec 31 17:00:11 login[704]: root login on 'ttyS0'
# cd /
。。。
【附录】
关于系统架构中,如何把函数和系统联系起来
注:我这里是arm架构的板子
在arch/arm/kernel/setup.c 的
void __init setup_arch(char **cmdline_p)
{
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
}
而mdesc即使machine description,
对应的是你在arch/arm/mach-XXX/mach-XXXX.c
中定义的,类似于如下的内容:
MACHINE_START(XXXX, "XXXX")
/* Maintainer: Austriamicrosystems Ltd */
.phys_io = XXXX,
.io_pg_offst = XXXX,
.boot_params = XXXX,
.map_io = XXXX_map_io,
.init_irq = XXXX_init_irq,
.timer = &XXXX_timer,
.init_machine = XXXX_init,
MACHINE_END
其中XXX代表你的板子(架构)名字。
移植日志:
1)初始化LCD控制器
Arch/arm/mach-s3c2410/Mach-smdk2410.c
static struct s3c2410fb_mach_info s3c2410fb_info _initdata = {
.fixed_syncs = 0,
.regs = {
.lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
S3C2410_LCDCON1_TFT |
// S3C2410_LCDCON1_ENVID |
S3C2410_LCDCON1_CLKVAL(1),
.lcdcon2 = S3C2410_LCDCON2_VBPD(25) | S3C2410_LCDCON2_VFPD(5)
| S3C2410_LCDCON2_VSPW(1),
.lcdcon3 = S3C2410_LCDCON3_HBPD(67) | S3C2410_LCDCON3_HFPD(40),
.lcdcon4 = S3C2410_LCDCON4_HSPW(31) | S3C2410_LCDCON4_MVAL(13),
.lcdcon5 = S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_HWSWP |
S3C2410_LCDCON5_PWREN|S3C2410_LCDCON5_INVVLINE|S3C2410_LCDCON5_INVV
FRAME ,
},
.lpcsel = 0,// ((0xCE6) & ~7) | 1<<4,
.gpccon= 0xaaaaaaaa,
.gpccon_mask= 0xffffffff,
.gpcup= 0xffffffff,
.gpcup_mask= 0xffffffff,
.gpdcon= 0xaaaaaaaa,
.gpdup = 0xFFFFFFFF,
.gpdup_mask= 0xffffffff,
.width = 640,
.height = 480,
.yres = {
.min = 480,
.max = 480,
.defval = 480,
},
.xres = {
.min = 640,
.max = 640,
.defval = 640,
},
.bpp = {
.min = 16,
.max = 16,
.defval = 16,
},
};
2)添加信息到设备
static void __init smdk2410_lcd_init(void)
{
set_s3c2410fb_info(&s3c2410fb_info); //devs.c中实现的加入控制器信息到device设备
}
3)在系统初始化中增加对 lcd 的初始化
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,
.timer = &s3c24xx_timer,
.init_machine =smdk2410_lcd_init;
MACHINE_END
4)make menuconfig 中还要加入 S3C2410 frambuffer support 选项。
Device Driver
Ghipics support
具体显示设备支持选项、Framebuffer终端配置选项和Framebuffer Logo配置选项
s3c2410 LCD framebuffer support
4)最后编译执行,就能看到小企鹅了。