嵌入式開發(fā)實(shí)驗(yàn)筆記(二)

實(shí)驗(yàn)筆記2

2017-11-06

概要: 理清u-boot的編譯過程和工作過程, 掌握Linux Kernel的編譯過程和文件結(jié)構(gòu); 根文件系統(tǒng)的過程分析;

(u-boot和kernel的編譯和燒錄過程在實(shí)驗(yàn)筆記1中)

u-boot (啟動(dòng)內(nèi)核)

具體可以參考u-boot中的README文件: ./README

1. Makefile 分析

1. 配置過程: make 100ask24x0_config

具體執(zhí)行的是:

@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
MKCONFIG := $(SRCTREE)/mkconfig
$(@:_config=) -- 100ask24x0

/* 所以上述命令等效于: */
mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0

所以從Makefile中跳轉(zhuǎn)到mkconfig中繼續(xù)執(zhí)行: mkconfig 是一個(gè)腳本文件(所以可以自閱)
結(jié)果 :

  1. 確定開發(fā)板的名稱: BOARD_NAME;
  2. 創(chuàng)建到平臺(tái)/開發(fā)板的頭文件的連接;
  3. 創(chuàng)建頂層Makefile包含的文件include/config.mk;
  4. 創(chuàng)建開發(fā)板相關(guān)的頭文件inclue/config.h;

2. 編譯過程: make

  1. 編譯cpu/$(CPU)/start.S;(不同的CPU, 可能還有其他的選項(xiàng))
  2. 對(duì)于開發(fā)板/平臺(tái)相關(guān)的每個(gè)目錄, 使用每個(gè)目錄各自的Makefile生成相應(yīng)的庫;
  3. 將前兩步編譯生成的 *.o, *.a 文件按照board/$(BOARDDIR)/config.mk 中指定的代碼段起始地址,
    board/$(BOARDDIR)/u-boot.lds 連接腳本進(jìn)行連接;
  4. 將上步生成的ELF格式的u-boot 轉(zhuǎn)換為二進(jìn)制格式;

2. 代碼分析

裸板實(shí)驗(yàn)的流程(回憶)

  1. 初始化
  1. 關(guān)看門狗;
  2. 初始化時(shí)鐘;
  3. 初始化SDRAM;
  1. 把程序從NAND FLASH 拷貝到 SDRAM;
  2. 設(shè)置棧;(后續(xù)需要執(zhí)行C語言程序)
  3. 繼續(xù)執(zhí)行程序;

u-boot 啟動(dòng)流程分析

  1. 硬件相關(guān)初始化:

(裸板實(shí)驗(yàn)的流程)

  1. 設(shè)置棧, sp->內(nèi)存; (然后調(diào)用C函數(shù))
    以上過程可以看cpu/$(CPU)/start.S
具體分析 (硬件相關(guān)初始化) [第一階段]
  1. 進(jìn)入設(shè)為管理模式(SVC);
  2. 關(guān)看門狗;
  3. 屏蔽中斷;
  4. 初始化SDRAM;
  5. 設(shè)置SP;
  6. 時(shí)鐘初始化;
  7. 重定位(程序從FLASH拷到SDRAM)
  8. 清理BSS段(未初始化的數(shù)據(jù))
  9. 調(diào)用start_armboot;
  1. 讀出內(nèi)核
具體分析
  1. 初始化NAND FLASH 和 NOR FLASH;
  2. 能支持FLASH的讀和寫;
  3. 環(huán)境變量初始化; (默認(rèn)的 && FLASH上保存的)
  4. 調(diào)用main_loop;(死循環(huán))
  5. u-boot 命令輸入;
    圖示過程:


    15-4.png
命令實(shí)現(xiàn) run_command()
  1. 輸入字符串(Name);
  2. Name => 動(dòng)作 => 函數(shù);
  3. 命令結(jié)構(gòu)體cmd_tbl_s{name, maxargs, repeatable, func*, usage, help};
  4. find_cmd(), 比較輸入字符串與命令結(jié)構(gòu)體中的Name;

u-boot.lds => _u_boot_cmd_start; _u_boot_cmd_end;
.u_boot_cmd -- #define struct_Section __attribute__((unused,section (".u_boot_cmd")))
<Command.h>(include)
#definn U_BOOT_CMD (name, maxargs, rep, cmd, usage, help)
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
comman/Makefile

讀出內(nèi)核的命令 nand read.jffs2(這個(gè)后綴不需要頁對(duì)齊) 0x30007FC0 kernel (mtd可見分區(qū))

命令等效于 nand read.jffs2 0x30007FC0 0x00060000 0x00200000

Flash 存儲(chǔ)的內(nèi)核是 uImage => image_header + kernel;

image_header => {ih_load(加載地址), ih_ep(入口地址)} => kernel可以存放在內(nèi)存的任意地方

bootm 命令 => 根據(jù)image_header移動(dòng)kernel到合適的地方(加載地址) => 啟動(dòng)kernel(do_bootm_linux)

do_bootm_linux => 設(shè)置kernel啟動(dòng)參數(shù) => 跳到入口地址啟動(dòng)kernel(theKernel()函數(shù)

u-boot和kernel數(shù)據(jù)交互: 在說定的地址以約定的格式, 存放數(shù)據(jù)。(地址: 0x30000100, 格式 struct tag)

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
/* 
 * bd->bi_arch_number -- 機(jī)器ID, 用于確定kernel是否支持這款板子(芯片);
 * bd->bi_boot_params -- 入口地址
 */
// armlinux.c
setup_start_tag(bd);
setup_memory_tag (bd);
setup_command_line (bd, commandline);
setup_end_tag (bd);

內(nèi)核 (啟動(dòng)應(yīng)用程序)

配置

  1. 結(jié)果是生成了.config文件;
  2. .config里面的配置項(xiàng)有以下文件引用:
  • C源碼;(宏來源于 ./include/linux/autoconf.h)
  • 子目錄Makefile;

obj-y += xxx.o -- xxx.c最終被編譯進(jìn)內(nèi)核里
obj-m += yyy.o -- yyy.c最終被編譯成一個(gè)模塊

  • ./include/config/auto.conf
  • ./include/linux/autoconf.h (基本所有宏定義為1)
  1. make uImage 時(shí)發(fā)生了下列事情:
  • .config => autoconf.h;
  • .config => auto.conf

分析(./Documentation/kbuild/makefiles.txt)

  1. 字目錄下的Makefile:
  • obj-y += yyy.o
  • obj-m += xxx.o
  1. make uImage => ./arch/arm/Makefile;
  2. uImage <= vmlinux <= $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
  • vmlinux-init <= $(head-y) $(init-y)
  • vmlinux-main <= $(core-y) $(libs-y) $(drivers-y) $(net-y)
  • vmlinux-lds <= arch/$(ARCH)/kernel/vmlinux.lds

啟動(dòng)

  1. 處理u-boot傳遞的參數(shù); (arch/arm/kernel/head.S)
  1. 判斷是否支持這個(gè)CPU;
  2. 判斷是否支持這個(gè)單板; <= u-boot啟動(dòng)內(nèi)核時(shí)傳入的參數(shù)(機(jī)器ID);
  3. 建立頁表;
  4. 使能MMU;
  5. 跳轉(zhuǎn) start_kernel;
  1. 掛接根文件系統(tǒng);
  2. 最終目的, 運(yùn)行應(yīng)用程序;
    Linux內(nèi)核啟動(dòng)過程圖解:


    16-7(1).png
16-7(2).png
/* 內(nèi)核的啟動(dòng)流程 (函數(shù)調(diào)用) */
start_kernel
    setup_arch              // 解析u-boot傳入的啟動(dòng)參數(shù)
    setup_command_line      // 解析u_boot傳入的啟動(dòng)參數(shù)
    parse_early_param
        do_early_param
            From __setup_start To __setup_end 調(diào)用early函數(shù)
    unknown_bootoption
        obsolete checksetup
            From __setup_start To __setup_end 調(diào)用Non early函數(shù)
    rest_init
        kernel_init; (創(chuàng)建kernel thread)
            prepare_namespace
                mount_root   // 掛接根文件系統(tǒng)
            init_post
                // 執(zhí)行應(yīng)用程序 (打開 /dev/console)

PS:
分區(qū)的代碼在內(nèi)核中已經(jīng)寫定, 位置在: ./arch/arm/plat-s3c24x0/common-smdk.c

struct mtd_partition smdk_default nand_part[]={};

根文件系統(tǒng) (應(yīng)用程序的基礎(chǔ)[平臺(tái)])

  1. kernel how to start the first application:
  1. open ("/dev/console"); (console -- 終端)
  2. sys_dup(0); sys_dup(0); (復(fù)制文件, 參考系統(tǒng)調(diào)用dup())
  3. run_init_process(); (可以啟動(dòng)自己的定義, 沒指定就是默認(rèn)) // 參見: ./init/main.c

busybox -- Linux 一些基礎(chǔ)命令的集合;

  1. init_main 作用:
  1. 讀取配置文件
  2. 解析配置文件
  3. 執(zhí)行用戶(程序)
busy -> init_main
            parse_inittab
                fopen("INITTAB", "r"); //打開配置文件 /etc/inittab 
                //(inittab的說明文檔在./examples/inittab)
                new_init_action ();
            run_actions(SYSINIT);
                waitfor (a, 0);           // 執(zhí)行應(yīng)用程序, 等待它執(zhí)行完畢
                    run (a);              // 創(chuàng)建子進(jìn)程process
                    waitpid(runpid, &status, 0);  // 等待它結(jié)束
                delete_init_action (a);   // 在init_action_list鏈表里刪除
            run_actions(WAIT);
                waitfor (a, 0);           // 執(zhí)行應(yīng)用程序, 等待它執(zhí)行完畢
                    run (a);              // 創(chuàng)建子進(jìn)程process
                    waitpid(runpid, &status, 0);  // 等待它結(jié)束
                delete_init_action (a);   // 在init_action_list鏈表里刪除
            run_actions(ONCE);
                run (a);              // 創(chuàng)建子進(jìn)程process
                delete_init_action (a);   // 在init_action_list鏈表里刪除
            while (1) {
                run_actions(RESPAWN);
                    if (a->pid == 0) {
                        a->pid = run (a);
                    }
                run_actions(ASKFIRST);
                    if (a->pid == 0) {
                        a->pid = run (a);
                    }
                wpid = wait(NULL);
                while (wpid > 0){
                    a->pid = 0;
                }
            }
/* inittab 的格式:
 * <id>:<runlevels>:<action>:<process>
 * id => /dev/id, 用于終端: stdin, stdout, stderr: printf, scanf, err;
 * runlevels => 應(yīng)用程序優(yōu)先級(jí)(暫時(shí)忽略)
 * action => 執(zhí)行時(shí)機(jī)
 * process => 應(yīng)用程序或程序
 * 從默認(rèn)的 new_init_action 中推出的配置文件格式:
 *  ::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
 */
    static void new_init_action (int action, const char *command, const char *cons);
/* 1. 創(chuàng)建一個(gè)init_action 結(jié)構(gòu);
 * 2. 把這個(gè)結(jié)構(gòu)放入init_action_list 鏈表
 */

最小根文件系統(tǒng)所需的要素

  1. /dev/console ; /dev/null(當(dāng)程序不指向標(biāo)準(zhǔn)輸入,輸出苗分,錯(cuò)誤時(shí)可以指向NULL)
  2. /etc/inittab
  3. 配置文件指定的程序
  4. 應(yīng)用程序: init => busybox
  5. C庫(若把BusyBox編譯成靜態(tài)的時(shí)候, 不需要C庫)

制作, 燒寫根文件系統(tǒng); 使用NFS, 編譯使用驅(qū)動(dòng)文件

1. 編譯, 配置BusyBox[實(shí)驗(yàn)版本: 1.7.0]

  1. 解壓busybox:

    tar xjf busybox-x.x.x.tar.bz2
    cd busybox-x.x.x
    
  2. 修改Makefile: (新版BusyBox可以在make menuconfig 設(shè)置交叉編譯)

    CROSS_COMPILE ?= arm-linux-
    
  3. make menuconfig:

    Makefile:405: *** mixed implicit and normal rules:deprecated syntax
    Makefile:1242: *** mixed implicit and normal rules:deprecated syntax
    

    出現(xiàn)上述情況可參考編譯linux kernel 時(shí)的解決方案;

  4. make & make install:

make install :
 make CONFIG_PREFIX=/path/from/root install
  1. Complete

2. 構(gòu)建最小根文件系統(tǒng)

  1. 創(chuàng)建/dev/console, /dev/null

    cd path/to/fs_mini
    mkdir dev
    cd dev
    sudo mknod console c 5 1
    sudo mknod null c 1 3
    
  2. 構(gòu)造一個(gè)/etc/inittab

    cd path/to/fs_mini
    mkdir etc
    vi(vim) etc/inittab
     console::askfirst:-/bin/sh
    
  3. C庫

    mkdir -p /path/to/fs_mini/lib
    cd /path/to/gcc-3.4.5-glibc-2.3.6/arm-linux/lib
    cp *.so* /path/to/fs_mini/lib -d (-d 保留鏈接文件)
    
  4. 進(jìn)一步完善:

    創(chuàng)建 proc

    cd /path/to/fs_mini
    mkdir proc
    echo "::sysinit:/etc/init.d/rcS" >> etc/inittab
    mkdir etc/init.d; vim etc/init.d/rcS
       mount -t proc none /proc (掛載proc虛擬文件系統(tǒng),用于收集當(dāng)前正在運(yùn)行的程序的信息)
       mount -a (讀取/etc/fstab, 根據(jù)文件內(nèi)容掛載)
    chmod +x etc/init.d/rcS
    vim etc/fstab
       #參考PC機(jī)上的/etc/fstab文件掛載proc
       #device   mount-point  type    options    dump    fsck   order
       proc         /proc     proc    defaults    0       0
    
  5. 創(chuàng)建Dev目錄: mdev (udev簡(jiǎn)化版本)[使用方法: 參考文檔 -- ./docs/mdev.txt]

    1. 修改etc/fstab

      #device   mount-point  type    options    dump    fsck   order
      sysfs       /sys       sysfs   defaults   0        0
      tmpfs       /dev       tmpfs   defaults   0        0
      
    2. 修改etc/init.d/rcS

      mount -a
      mkdir /dev/pts
      mount -t devpts devpts /dev/pts
      echo /sbin/mdev > /proc/sys/kernel/hotplug
      mdev -s
      
  6. Complete

制作映像文件

制作yaffs2映像文件
cd /path/to/tools
tar xjf yaffs_source_util_larger_small_page_nand.tar.bz2
cd Development_util_ok
cd yaffs2/utils
make
sudo cp mkyaffs2image /usr/local/bin
sudo chmod +x /usr/local/bin/mkyaffs2image
cd path/to/fs_mini; cd ..
mkyaffs2image fs_mini fs_mini.yaffs2
制作jffs2映像文件
# 安裝zlib壓縮庫
cd /path/to/zlib-x.x.x.tar.gz
tar xzf zlib-x.x.x.tar.gz ; cd zlib-x.x.x
./configure --shared --prefix=/usr
make ; sudo make install
# 安裝mkfs.jffs2 壓縮工具
cd /path/to/mtd-utils-x.x.x.tar.bz2
tar xjf mtd-utils-x.x.x.tar.bz2 ; cd mtd-utils-x.x.x/util
make && sudo make install
# 制作jffs2鏡像文件
cd /path/to/fs_mini; cd ..
mkfs.jffs2 -n -s 2048 -e 128KiB -d fs_mini -o fs_mini.jffs2
# -s -- 一頁的大小
# -e -- 可擦除塊的大小
# 燒寫jffs2文件系統(tǒng)鏡像時(shí)需要強(qiáng)制指定文件系統(tǒng)類型
set bootargs noinitrd root=/dev/mtdblock3 rootfstype=jffs2 ....(其余選項(xiàng)不變)

nfs的使用 (參考內(nèi)核文檔: ./Documentation/nfsroot.txt)

  1. 安裝Ubuntu的NFS的服務(wù)
sudo apt-get install nfs-kernel-server (推薦安裝了一個(gè): open-iscsi watchdog)
\\ 以防萬一都裝上吧;
  1. 設(shè)置/etc/exports:
  1. 確定你需要掛載的目錄: /path/to/dir;
  2. 修改目錄權(quán)限:
sudo chmod -R /path/to/dir
  1. 進(jìn)入文件/etc/exports輸入:
/path/to/dir  *(rw,sync,no_root_squash)
# 至于選項(xiàng)參考網(wǎng)站 [nfs安裝與設(shè)置](http://www.cnblogs.com/mchina/archive/2013/01/03/2840040.html)
  1. 重啟nfs-kernel-server服務(wù):
sudo /etc/init.d/nfs-kernel-server restart
OR
sudo service nfs-kernel-server restart
  1. 在本機(jī)試掛載:
sudo mount -t nfs <ipaddr>:<path-to-dir> <target-dir>
  1. 進(jìn)入開發(fā)板進(jìn)行掛載:
mkdir /mnt
mount -t nfs <ipaddr>:<path-to-dir> <target-dir>>
  1. 解決開機(jī)掛載nfs的問題: (進(jìn)入u-boot啟動(dòng)設(shè)置界面[在等待時(shí), 按<Space>進(jìn)入])
set noinitrd root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf> init=/linuxrc console=ttySAC0
eg:
# kernel 2.22.6
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.1.25:/home/yjh/WorkSpace/nfs_root/first_fs ip=192.168.1.17:192.168.1.25:192.168.1.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
# kernel 3.4.2
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.0.25:/home/yjh/nfs_root/new_first_mini ip=192.168.0.17:192.168.0.25:192.168.0.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200
// 具體設(shè)置以及參數(shù)意義可以參考: ./Documentation/nfsroot.txt
save
  1. 完成
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市史煎,隨后出現(xiàn)的幾起案子阔籽,更是在濱河造成了極大的恐慌揩尸,老刑警劉巖所禀,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件方面,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡色徘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門操禀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來褂策,“玉大人,你說我怎么就攤上這事颓屑〗锛牛” “怎么了?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵揪惦,是天一觀的道長(zhǎng)遍搞。 經(jīng)常有香客問我,道長(zhǎng)器腋,這世上最難降的妖魔是什么溪猿? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮纫塌,結(jié)果婚禮上诊县,老公的妹妹穿的比我還像新娘。我一直安慰自己措左,他們只是感情好依痊,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怎披,像睡著了一般胸嘁。 火紅的嫁衣襯著肌膚如雪瓶摆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天性宏,我揣著相機(jī)與錄音赏壹,去河邊找鬼。 笑死衔沼,一個(gè)胖子當(dāng)著我的面吹牛蝌借,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播指蚁,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼菩佑,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了凝化?” 一聲冷哼從身側(cè)響起稍坯,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搓劫,沒想到半個(gè)月后瞧哟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枪向,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年勤揩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秘蛔。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陨亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出深员,到底是詐尸還是另有隱情负蠕,我是刑警寧澤,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布倦畅,位于F島的核電站遮糖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏叠赐。R本人自食惡果不足惜欲账,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望燎悍。 院中可真熱鬧敬惦,春花似錦、人聲如沸谈山。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至畴椰,卻和暖如春臊诊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背斜脂。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工抓艳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帚戳。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓玷或,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親片任。 傳聞我的和親對(duì)象是個(gè)殘疾皇子偏友,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容