1.
核心相關代碼分析init_post()函數
static int noinline init_post(void)
{
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);
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.");
}
/dev/console 這裡的console為在 uboot傳進來的參數bootargs中console=/dev/ttySAC0
也是 核心打開的 第一個裝置
sys_open((const char __user *) "/dev/console", O_RDWR, 0)
(void) sys_dup(0); dup表示複制的意思 打開裝置
(void) sys_dup(0); dup表示複制的意思 打開裝置
上邊三行用于分别定義标準輸入.标準輸出.标準錯誤所指向的裝置
execute_command 這個表示從指令行獲得的參數代表核心将要執行的第一個程式
即 :run_init_process(execute_command);
如果bootargs裡沒有指定第一個執行程式的話就執行預設的程式中的一個
即 :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.");
該句通過上邊的console所定義的裝置輸出
2. 關于uboot傳遞的 ”init= str“ 在核心裡處理的相關代碼如下
static int __init init_setup(char *str)
{
unsigned int i;
execute_command = str;
/*
* In case LILO is going to boot us with default command line,
* it prepends "auto" before the whole cmdline which makes
* the shell think it should execute a script with such name.
* So we ignore all arguments entered _before_ init=... [MJ]
*/
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("init=", init_setup);
其中
__setup("init=", init_setup);
就是在 檢查bootargs環境變量裡邊是否有init= str時
如果有則 将str指派給execute_command
3.busybox建構根檔案系統
當我們編譯 busybox時會在 bin目錄生成一個應用程式 busybos
這個busybos是 ls cp。。等指令的組合 而bin目錄下的每一個指令均是到 busybox的連結
即:
# ls -al /bin/cp /bin/ls
lrwxrwxrwx 1 1000 1000 7 Jan 6 2010 /bin/cp -> busybox
lrwxrwxrwx 1 1000 1000 7 Jan 6 2010 /bin/ls -> busybox
bin目錄下的ls cp 等指令執行時都相當于執行 busybox程式
而 busybos 會根據指令的不同進行不同的操作
例如執行ls 相當于執行busybox ls
執行cp 相當于執行busybox cp
對于核心執行的預設的第一個程式 /sbin/init 它也是都busybox的一個連接配接
4.分析busybox源碼 分析/sbin/init
busybox源碼裡 每一個指令均對應一個 .C 檔案 如 ls.c cp.c init.c
而每個.C檔案裡都有一個main函數 當我們在終端執行不同的指令時都會調用不同的main函數
這些函數都被編譯生成busybos程式 根檔案系統/bin /sbin裡的指令均是執行busybox程式
詳細分析/sbin/init
核心啟動的第一個程式 而我們的最終目的是啟動我們自己寫的使用者程式
是以有一個配置檔案(/etc/inittab) 在這個配置檔案裡指定了 後續執行哪個應用程式
init程式的工作
:讀取配置檔案 解析配置檔案 (根據配置檔案)執行使用者程式
對應的函數 parse_inittab(); (位于main函數裡)
内容如下
:
。。。
file = fopen(INITTAB, "r");打開一個檔案 INITTAB
INITTAB 宏定義為 "/etc/inittab" 是以可得該配置檔案為 /etc/inittab
inittab的格式 Format for each entry: <id>:<runlevels>:<action>:<process>
例
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
tty2::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
分析 id項 用作終端 printf scanf stdio err..
runlevel 省略
action 何時執行 可取為 sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown.
process 對應的想要執行的程式
後邊的 if 語句用于說明 沒有/etc/inittable時怎麼執行
當沒有/etc/table時執行下邊代碼
if (file == NULL) {
/* 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, "");
return;
#if ENABLE_FEATURE_USE_INITTAB
}
上邊代碼就是将生成預設的init_action結構體 并将其加入init_action_list連結清單
new_init_action(a->action, command, id); 函數分析
分析該函數 代碼如下
建立init_action 結構
struct init_action *new_action, *a, *last;
将init_action 結構放入 init_action_list 連結清單
如果該連結清單裡有一個init_action 結構體 且 command id均與傳入的command與id相同
則 覆寫 否則 将該結構體加入 init_action_list連結清單
接着這main函數中執行
run_actions(SYSINIT); 執行 init_action_list 連結清單裡 action為SYSINIT的proces
waitfor(a, 0); 等待執行完畢
run(a);建立 process 子程序
wpid = waitpid(runpid, &status, 0); 等待子程序執行結束
delete_init_action(a); 從 init_action_list 連結清單裡删除該 action 的結構體
run_actions(WAIT); 執行 init_action_list 連結清單裡 action為WAIT的proces
同 SYSINIT
run_actions(ONCE); 執行 init_action_list 連結清單裡 action為ONCE的proces
run(a);
delete_init_action(a);
while(1)
{
/* run the respawn stuff */
run_actions(RESPAWN); 執行 init_action_list 連結清單裡 action為 RESPAWN 的proces
/* run the askfirst stuff */
run_actions(ASKFIRST); 執行 init_action_list 連結清單裡 action為ASKFIRST 的proces
wpid = wait(NULL);
while (wpid > 0) {
a->pid = 0;
}
}
run_actions(action);函數分析
總結
最小根檔案系統需要的内容
/dev/console /dev/null(當不設定程式的id時 程式的标準輸入,标準輸出,标準錯誤,将會定位到/dev/null裡)
/etc/inittab(配置檔案裡指定了 process 我們要執行的使用者process就在這裡)
庫 (init的很多函數都依靠 c庫)
/sbin/init程式(指向busybox指令)
5.建構根檔案系統
編譯busybox
配置編譯工具 在頂層的makefile裡配置
Building:
=========
The BusyBox build process is similar to the Linux kernel build:
make menuconfig # This creates a file called ".config"
make # This creates the "busybox" executable
make CONFIG_PREFIX= xxx install # or make CONFIG_PREFIX=/path/from/root install
安裝到指定的目錄下
得到如下結果
book@book-desktop:/work/nfs_root/my_fs$ ls
bin linuxrc sbin usr
得到/sbin/init程式
book@book-desktop:/work/nfs_root/my_fs/sbin$ ls init -l
lrwxrwxrwx 1 book book 14 2013-08-02 23:28 init -> ../bin/busybox
建立裝置 console null
mknod console c 5 1 建立字元裝置 console 主裝置号5 次裝置号1 c表示字元裝置
mknod null c 1 3
建立 /etc/inittab
mkdir etc
cd etc/
vi inittab
添加console::askfirst:-/bin/sh
安裝c庫
mkdir lib
cd /work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib
cp *.so* /work/nfs_root/my_fs/lib -d
*.so*為是以的動态庫
.a 為靜态庫 這裡隻拷貝靜态庫 因為在 make menuconfig 配置時配置的動态庫
-d 表示連接配接檔案仍拷貝成連接配接檔案 不加-d 時 連接配接檔案将會變成真實内容 照成根檔案系統過大
至此最小根檔案系統作成