u-boot、kernel和filesystem 執行過程分析
標題:
Uboot -kerne-root 啟動流程
內容:
※uboot啟動流程
※Kernel啟動流程
※Root啟動流程
※構建根檔案系統
/*********************************
*u-boot: u-boot2012.04.01
*kernel: linux-2.6.22
*busybox: busybox-1.1.6
*********************************/
一、uboot啟動流程
1.初始化硬體 2.把核心從NAND讀到SDRAM 3.設定核心啟動引數 4.跳轉執行核心 執行第一個程式碼:/cpu/arm920t/start.S 第一階段(start.S) reset: set the cpu to SVC32 mode //管理模式 turn off the watchdog //關看門狗 mask all IRQs //遮蔽中斷 cpu_init_crit flush v4 I/D caches //關閉caches disable MMU stuff and caches //關閉MMU lowlevel_init //配置SDRAM Relocate //重定位 stack_setup: //設定堆疊 clear_bss: //清楚bss段 start_armboot //跳轉執行第二階段
第二階段(board.c)
start_armboot (void) init_sequence cpu_init, /* basic cpu dependent setup */ board_init, /* basic board dependent setup */ interrupt_init, /* set up exceptions */ env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ flash_init 初始化NOR nand_init() 初始化NAND env_relocate () 將環境變數讀入指定位置 cs8900_get_enetaddr 初始化網路裝置 main_loop () 死迴圈 s = getenv ("bootdelay") bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; s = getenv ("bootcmd"); Bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0 //環境變數 run_command (s, 0); continue;
Uboot命令的實現
①串列埠輸入命令(字串)
②動作(函式),命令對應於名字
因而根據命令找到對應函式,來執行。Uboot裡面有一個結構體
包含1.名
2.函式
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
char *usage; /* Usage message (short) */
#ifdef CFG_LONGHELP
char *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
此時run_command函式根據名字匹配該結構體
Run_command
命令的提取
while (*str) {
/*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*/
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes;
if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-1) != '\\')) /* and NOT escaped */
break;
}
parse_line() //解析命令
Cmdtp=find_cmd() //匹配命令 cmdtp就是指向上面的結構體cmd_tbl_s
for (cmdtp = &__u_boot_cmd_start;
cmdtp != &__u_boot_cmd_end;
cmdtp++)
以上:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
比如啟動命令:
Bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0以bootm 0x30007fc0為例
U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
#ifdef CONFIG_OF_FLAT_TREE
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the of the\n"
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
"\tuse a '-' for the second argument. If you do not pass a third\n"
"\ta bd_info struct will be passed instead\n"
#endif
);
U_boot_cmd的定義:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
以上可以展開為
cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd")))
= {bootm, CFG_MAXARGS, 1, do_bootm, "bootm - boot application image from memory\n", help}
以上解釋為:bootm命令定義了__u_boot_cmd_bootm結構體,該結構體型別是cmd_tbl_t,並且強制為.u_boot_cmd段屬性
Uboot啟動核心
核心啟動依賴nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0
nand read.jffs2 0x30007fc0 kernel把核心讀到0x30007fc0
從哪裡讀? --kernel分割槽在配置檔案裡面寫死了smdk2410.h
讀到哪裡? --0x30007fc0
Nand命令分析:
"nand read[.jffs2] - addr off|partition size\n"
bootm 0x30007fc0從bootm 0x30007fc0啟動它
Bootm命令分析:
do_bootm
image_header_t *hdr = &header;
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
//如果當前地址不是入口地址,移到載入地址0x30008000,由於頭部佔用64位元組,所以此時地址是0x30007fc0
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify); //啟動核心
setup_start_tag (bd);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
setup_end_tag (bd);
printf ("\nStarting kernel ...\n\n");
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
二、Kernel啟動流程
處理uboot傳入的引數
①判斷是否支援這個CPU
②判斷是否支援這個單板
③建立頁表
④使能MMU
⑤跳到start_kernel函式
核心執行的第一個程式碼檔案:/arch/arm/kernel/head.S 和 /arch/arm/boot/bootp/init.S
Start_kernel分析
1.輸出核心版本資訊
2.Setup_arch($command_line)
3.Setup_command_line(command_line)
呼叫關係:
Start_kernel
printk(linux_banner); //輸出版本資訊
setup_arch(&command_line); //匹配ID,arch-type
setup_processor();
list = lookup_processor_type(processor_id);
mdesc = setup_machine(machine_arch_type);
parse_cmdline(cmdline_p, from); //解析命令
setup_command_line(command_line);
strcpy (saved_command_line, boot_command_line);
Rest_init
Kernel_thread() //執行緒
// kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
kernel_init()
Prepare_namespace()
Mount_root()
Init_post()
Run_init_process() //執行檔案系統第一個程式
...
三、Root啟動流程
init過程
1.讀取配置檔案
2.解析配置檔案
3.執行客戶程式
配置檔案:1.指定程式;2.何時執行
busybox呼叫過程
busybox -> init_main
parse_inittab
file = fopen(INITTAB, "r");//開啟配置檔案/etc/inittab
(沒有配置檔案時使用預設配置檔案)
new_init_action()
#1.建立init_action結構,填充
#2.把這個結構放入init_action_list連結串列裡面
run_actions(SYSINIT);
waitfor(a, 0); //執行程式,等待執行完畢
run(a); //系統呼叫,建立precess子程序
waitpid(runpid, &status, 0) //等待結束
delete_init_action(a);//在init_action_list裡面刪掉應用程式
run_actions(WAIT);
waitfor(a, 0);
delete_init_action(a);
run_actions(ONCE);
run(a);//不會等待子程序結束
delete_init_action(a);//在init_action_list裡面刪
while (1) {
run_actions(RESPAWN);
run(a)
run_actions(ASKFIRST);
run(a)
列印:"\nPlease press Enter to activate this console. ";
等待回車
建立子程序
wpid = wait(NULL);//等待子程序退出
while (wpid > 0) {
a->pid = 0;//退出後,設定pid=0
}
}
init_action_list裡面有id,action,process等等
static void new_init_action(int action, const char *command, const char *cons)
struct init_action *new_action, *a, *last;
init_action裡面有:
struct init_action *next;
int action;
pid_t pid; //程序號
char command[INIT_BUFFS_SIZE]; //應用程式
char terminal[CONSOLE_NAME_SIZE]; //終端
new_init_action做了什麼
1.建立init_action結構,填充
2.把這個結構放入init_action_list連結串列裡面
現在假設沒有配置檔案,根據
/* No inittab file -- set up some default behavior */
#endif
/* Reboot on Ctrl-Alt-Del */
new_init_action(CTRLALTDEL, "reboot", "");
/* Umount all filesystems on halt/reboot */
new_init_action(SHUTDOWN, "umount -a -r", "");
/* Swapoff on halt/reboot */
if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
/* Prepare to restart init when a HUP is received */
new_init_action(RESTART, "init", "");
/* Askfirst shell on tty1-4 */
new_init_action(ASKFIRST, bb_default_login_shell, "");
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/* sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
來反推出預設配置項
#inittab格式:(檔案在example/inittab檔案)
#<id>:<runlevels>:<action>:<process>
#<id> :/dev/id,最終用作終端:stdin,stdout,stderrer:printf,scanf,err
#<runlevels> :忽略
#<action> :何時執行的
<action>: Valid actions include: sysinit, respawn, askfirst, wait, once,
restart, ctrlaltdel, and shutdown.
#<process> :應用程式或者指令碼
new_init_action(ASKFIRST, -/bin/sh, /dev/tty2);
new_init_action(CTRLALTDEL, "reboot", "");
id=null
runlevels=null
action=ctrlaltdel
那麼上面就是:
::ctrlaltdel:reboot
同理可知其他的配置項是:
::shutdown:umount -a -r
::restart:init
::askfirst:-/bin/sh
tty2:askfirst:-/bin/sh
tty3:askfirst:-/bin/sh
tty4:askfirst:-/bin/sh
::sysinit:/etc/init.d/rcS
其實:
main裡面
signal(SIGHUP, exec_signal);
signal(SIGQUIT, exec_signal);
signal(SIGUSR1, shutdown_signal);
signal(SIGUSR2, shutdown_signal);
signal(SIGINT, ctrlaltdel_signal);
signal(SIGTERM, shutdown_signal);
signal(SIGCONT, cont_handler);
signal(SIGSTOP, stop_handler);
signal(SIGTSTP, stop_handler);
當用戶按下 ctrl + alt +del時會產生一個訊號,然後執行ctrlaltdel_signal函式
static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
{
run_actions(CTRLALTDEL);
}
總結:應用程式所需要的檔案
1./dev/console /dev/null
2.配置檔案/etc/inittab
3.配置檔案裡面指定的應用程式
4.庫檔案
5.init本身,即busybox
以上就是最小根檔案系統所需要的項