vivi開發筆記(十七):vivi與Linux kernel的參數傳遞情景分析(下)

下面進入Linux kernel部分,分析與bootloader參數傳遞對應的部分。
 
    移植Linux需要很大的工作量,其中之一就是HAL層的編寫。在具體實現上,HAL層以arch目錄的形式存在。顯然,該層需要與bootloader 有一定的約定,否則就不能很好的支持。其實,這個地方應該思考一個問題,就是說,boot loader可以做到Linux kernel裏面,但是這樣帶來的問題就是可移植性和靈活性都大爲降低。而且,bootloader的功能並非操作系統的核心範疇,Linux的核心應該始終關注操作系統的核心功能上,將其性能達到最優。所以,bootloader分離出來單獨設計,是有一定的道理的。bootloader現在除了完成基本功能外,慢慢地變得“肥胖”了。在高性能bootloader設計中,可能會把調試內核等的一些功能集成進來,這樣在內核移植尚未完成階段, bootloader可以充當調試器的作用。功能趨於完善,也慢慢趨於複雜。廢話不說,進入正題。
 
三、Linux kernel接受參數分析
 
    這部分主要分析如下問題:
 
    ·Linux kernel支持壓縮映象和非壓縮映象兩種方式啓動,那麼這兩種流程和函數入口有何不同?
    ·如何使用非壓縮映象?做一下測試。
    ·zImage是如何生成的?其格式如何?
    ·啓動之後,Linux kernel如何接收參數?
 
    這裏不具體區分每個問題,按照理解和開發的思路來進行。
 
 1、思考:前面做的基本實驗中,並沒有採用壓縮映象。因爲程序規模太小,壓縮帶來的時間開銷反而降低了性能。但是對Linux kernel來說,映象還是比較大的,往往採用了壓縮。但是,同樣有需求希望Linux kernel小一些,不採用壓縮方式來提高內核啓動的速度,對時間要求比較苛刻。那麼,這樣就出現了兩種情況:壓縮映象和非壓縮映象。由此帶來的問題就在於:如果是壓縮映象,那麼必須首先解壓縮,然後跳轉到解壓縮之後的代碼處執行;如果是非壓縮映象,那麼直接執行。Linux必須對這兩種機制提供支持,這裏就需要從整體上來看一下生成的映象類型了。
 
    因爲vivi的Makefile都是直接來源於Linux,前面對vivi的Makefile已經分析清楚了,這裏看Linux的Makefile就容易多了,大同小異,而且還有豐富的文檔支持。
 
(1)非壓縮映象
 
$make vmlinux
 

[armlinux@lqm linux-2.4.18]$ ls -l vmlinux
-rwxrwxr-x 1 armlinux armlinux 1799697 Sep 11 14:06 vmlinux
[armlinux@lqm linux-2.4.18]$ file vmlinux
vmlinux: ELF 32-bit LSB executable, ARM, version 1 (ARM), statically linked, not stripped

 
    這裏生成的是vmlinux,是ELF文件格式。這個文件是不能燒寫存儲介質的,如果想了解ELF文件格式,需要參考專門的文章。當然,這裏,如果想要使用非壓縮映象,可以使用arm-linux-objcopy把上述ELF格式的vmlinux轉化爲二進制格式的vmlinux.bin,這樣就可以直接燒寫了。
 
    於是我做了如下的修改,在Makefile中增加了:
 

vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
        $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
                --start-group \
                $(CORE_FILES) \
                $(DRIVERS) \
                $(NETWORKS) \
                $(LIBS) \
                --end-group \
                -o vmlinux
        $(NM) vmlinux | grep -'\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
        $(OBJCOPY) -O binary -.comment -.stab -.stabstr -S vmlinux vmlinux.bin

 
    同時在clean file的列表中增加vmlinux.bin。這樣就可以生成vmlinux.bin了,前面的基礎實驗都講過了。然後燒寫vmlinux.bin到nand flash的kernel分區,引導啓動,正常,而且不會出現解壓縮提示:
 

NOW, Booting Linux......
VIVI has completed the mission of 
From now on, Linux kernel takes charge of all
 
Linux version 2.4.18-rmk7-pxa1 (armlinux@lqm) (gcc version 2.95.3 20010315 (release)) #2 Tue Sep 11 14:06:14 CST 2007

 
    可見,可以通過非壓縮映象格式啓動。
 
(2)壓縮映象
 
    下面看看壓縮映象是如何得到的。頂層的Makefile沒有壓縮映象的生成,顯然就在包含的子Makefile中。容易查知在arch/arm/下的Makefile,可見:
 

bzImage zImage zinstall Image bootpImage install: vmlinux
        @$(MAKEBOOT) $@

 
    也就是說,有bzImage、zImage幾種。其中arch/boot下有:
 

SYSTEM =$(TOPDIR)/vmlinux

Image: $(CONFIGURE) $(SYSTEM)
        $(OBJCOPY) -O binary -.note -.comment -S $(SYSTEM) $@

bzImage: zImage

zImage: $(CONFIGURE) compressed/vmlinux
        $(OBJCOPY) -O binary -.note -.comment -S compressed/vmlinux $@
        @echo " ^_^ The kernel image file is:" $(shell /bin/pwd)/$@

 
    這裏發現如果採用make Image,則生成的非壓縮映象的二進制格式,可以直接燒寫,可見前面第一步的工作是浪費了,Linux內核還是很完善的,提供了這種方式,所以,如果想要生成非壓縮二進制映象,那麼就要使用make Image。
 
    另外,這裏提供了兩種壓縮的映象,其實就是一種,這裏能夠看到的就是如果採用make zImage或者make bzImage,就要把compressed/vmlinux處理爲二進制格式,可以下載使用。下面就看compressed/vmlinux是什麼。進入compressed文件夾,看看Makefile:
 

vmlinux:    $(HEAD) $(OBJS) piggy.o vmlinux.lds
        $(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(LIBGCC) -o vmlinux

 
    很明顯了,這裏的vmlinux是由四個部分組成:head.o、head-s3c2410.o、misc.o、piggy.o。關於這幾個文件是幹什麼用的,看看各自的編譯規則就非常清晰了:
 
 

$(HEAD): $(HEAD:.o=.S) \
                        $(wildcard $(TOPDIR)/include/config/zboot/rom.h) \
                        $(wildcard $(TOPDIR)/include/config/cpu/32.h) \
                        $(wildcard $(TOPDIR)/include/config/cpu/26.h)
                $(CC) $(AFLAGS) -traditional -c $(HEAD:.o=.S)

piggy.o: $(SYSTEM)
                $(OBJCOPY) -O binary -.note -.comment -S $(SYSTEM) piggy
                gzip $(GZFLAGS) < piggy > piggy.gz
                $(LD) --o $@ -b binary piggy.gz
                rm -f piggy piggy.gz

font.o: $(FONTC)
                $(CC) $(CFLAGS) -Dstatic= --o $@ $(FONTC)

vmlinux.lds: vmlinux.lds.in Makefile $(TOPDIR)/arch/$(ARCH)/boot/Makefile $(TOPDIR)/.config
                @sed "$(SEDFLAGS)" < vmlinux.lds.in > $@

clean:; rm -f vmlinux core piggy* vmlinux.lds

.PHONY: clean

misc.o: misc.c $(TOPDIR)/include/asm/arch/uncompress.h $(TOPDIR)/lib/inflate.c

 
    可見,vmlinux是把頂層生成的非壓縮的ELF映象vmlinux進行壓縮,同時加入了加壓縮代碼部分。真正的壓縮代碼就是lib/inflate.c。可以看看,主要是gunzip,具體的壓縮算法就不分析了。
 
    至此,就可以用下圖作出總結了:
 
    bootloader把存儲介質中的kernel映象下載到mem_base+0x8000的位置,執行完畢後,跳轉到這一位置,執行此處的代碼。這一位置的入口可能有兩種情況,第一種是kernel映象爲非壓縮格式,通過make Image獲得,那麼真正的入口就是arch/arm/kernel/head_armv.S(ENTRY(stext));第二種是kernel映象爲壓縮格式,通過make zImage獲得,那麼真正的入口就是arch/arm/boot/compressed/head.S(ENTRY(_start))。這個地方並不是kernel判斷,也不需要判斷。道理很簡單,cpu只會按照讀入的代碼執行,兩種情況下執行的代碼不同,自然也就有兩種不同的過程了。
 
(3)探討zImage的magic number的位置
 
    可以看出,如果是zImage,那麼程序的入口是arch/arm/boot/compressed/head.S。分析程序頭部:
 

.align
start:
                .type start,#function

                //重複如下指令8次
                .rept 8
                mov r0, r0
                .endr
                //跳轉指令,跳到下面第一個標號1處
                b 1f

                //這就是第10條指令的位置,也就是偏移爲4*9個字節
                .word 0x016f2818 @ Magic numbers to help the loader
                .word start @ absolute load/run zImage address
                .word _edata @ zImage end address
1: mov r7, r1 @ save architecture ID
                mov r8, #0 @ save r0

 
    可見前面8條指令均爲mov r0, r0,從前面的zImage的16進制格式中可以看出,前面8個字都是相同的,均爲00 00 A0 E1,第9條指令就是b 1f,然後就應該是0x016f2818.這樣就與前面程序的判斷對應上了,也就是說,此處的magic number是固定位置,固定數值的,註釋中也寫的很清晰,那就是magic numbers to help the loader,也就是說幫助bootloader確定映象的文件格式。但是應該說明的是,在vivi的bootloader設計中,雖然檢測zImage 的magic number,但是並沒有進行未識別處理。也就是說,假定用ultra-edit32把此位置的0x016f2818破壞掉,其他不變,那麼雖然vivi 提示無法識別zImage映象,但是並不影響實際的執行。當然,你也可以有其他的設計思路。不過設計的哲學思想是,要完成一件事情,並不只有一種方式。所以,bootloader不能限死只是使用zImage格式,需要有一定的靈活性,爲了引導內核啓動,可以採用不同的方式。
 
(4)完成了前面的理解,下面就要重點看解析參數一部分了。這裏不將zImage方式的啓動作爲重點分析內容,靜下心來跟蹤代碼並不是難事。從整體的角度理解,如果採用zImage,那麼在執行完成解壓縮之後,自然會調轉到解壓之後的kernel的第一條指令處。這時就是真正的啓動內核了。所以我們可以看arch/arm/kernel/head-armv.S,此處做的工作可以參考taoyuetao的分析,完成的功能比較簡單。這裏就感興趣的參數問題分析,需要注意的是,
 

/*
 * Kernel startup entry point.
 *
 * The rules are:
 * r0 - should be 0
 * r1 - unique architecture number
 * MMU - off
 * I-cache - on or off
 * D-cache - off
 *
 * See linux/arch/arm/tools/mach-types for the complete list of numbers
 * for r1.
 */

 
   可見R0是0,R1是mach type,這些都是必須要設定的。在這裏,並沒有限定R2必須爲參數的起始地址。kernel本身並沒有使用R0-R2,如果設定了R2,在這裏也不會修改其值。後面的工作也沒有設計接收參數,最後直接跳到start_kernel(【init/main.c】)
 

asmlinkage void __init start_kernel(void)
{
    char * command_line;
    unsigned long mempages;
    extern char saved_command_line[];
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */

    lock_kernel();
    printk(linux_banner);
    setup_arch(&command_line);
    printk("Kernel command line: %s\n", saved_command_line);
    parse_options(command_line);

 
    從開頭分析,首先是lock_kernel,這裏是SMP相關,我的是單CPU,所以實際上該函數爲空。然後打印版本信息,在vivi中已經分析過這個機制了,兩者相同。下面的setup_arch就是分析的重點了,它要獲取命令行啓動參數,然後打印獲得的命令行參數,然後進行語法解析選項。我們關注的重點就在setup_arch上了。參數設置都在【arch/arm/kernel/setup.c】,這個函數也不例外,進入setup.c。
 

void __init setup_arch(char **cmdline_p)
{
    struct tag *tags = NULL;
    struct machine_desc *mdesc;
    char *from = default_command_line;

    ROOT_DEV = MKDEV(0, 255);

    setup_processor();
    mdesc = setup_machine(machine_arch_type);
    machine_name = mdesc->name;

    if (mdesc->soft_reboot)
        reboot_setup("s");

    if (mdesc->param_offset)
        tags = phys_to_virt(mdesc->param_offset);

    
/*
     * Do the machine-specific fixups before we parse the
     * parameters or tags.
     */

    if (mdesc->fixup)
        mdesc->fixup(mdesc, (struct param_struct *)tags,
             &from, &meminfo);

    
/*
     * If we have the old style parameters, convert them to
     * a tag list before.
     */

    if (tags && tags->hdr.tag != ATAG_CORE)
        convert_to_tag_list((struct param_struct *)tags,
                 meminfo.nr_banks == 0);

    if (tags && tags->hdr.tag == ATAG_CORE)
        parse_tags(tags);

    if (meminfo.nr_banks == 0) {
        meminfo.nr_banks = 1;
        meminfo.bank[0].start = PHYS_OFFSET;
        meminfo.bank[0].size = MEM_SIZE;
    }

    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(&meminfo, cmdline_p, from);
    bootmem_init(&meminfo);
    paging_init(&meminfo, mdesc);
    request_standard_resources(&meminfo, mdesc);

    
/*
     * Set up various architecture-specific pointers
     */

    init_arch_irq = mdesc->init_irq;

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
    conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
    conswitchp = &dummy_con;
#endif
#endif
}

 
    這裏面涉及到3個比較複雜的結構體,包括param_struct、tag、machine_desc。第一步的操作是關於根設備號,暫時不探討;第二步工作setup_processor,是設置處理器,這是多處理器相關部分,暫時不探討;第三步工作是setup_machine,這裏就需要了解了。
 
   首先,machine_arch_type沒有定義,僅僅在頭部有定義,這是全局變量,兩者之間一定存在聯繫:
 

unsigned int __machine_arch_type;

 
   看看頭文件,應該有#include <asm/mach-types.h>,但是未編譯時並沒有,可以確定是編譯前完成的。這裏只有看Makefile了。因爲setup.c在這裏,首先看同層的Makefile。這一層沒有關於mach-types.h的信息,然後到上一層Makefile,發現了:
 

MRPROPER_FILES += \
        arch/arm/tools/constants.h* \
        include/asm-arm/arch \
        include/asm-arm/proc \
        include/asm-arm/constants.h* \
        include/asm-arm/mach-types.h

# We use MRPROPER_FILES and CLEAN_FILES now
archmrproper:
        @/bin/true

archclean:
        @$(MAKEBOOT) clean

archdep: scripts/mkdep archsymlinks
        @$(MAKETOOLS) dep
        @$(MAKEBOOT) dep

 
   說現在使用MRPROPER_FILES,但是下面沒有出現,故而應該看幾個宏的定義:
 

MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
MAKETOOLS = $(MAKE) -C arch/$(ARCH)/tools

 
    由此知道,對應的子文件夾包括boot和tools,boot是與啓動相關,不太可能;而前面也看到,tools下有mach-types,所以判斷在tools下面,看看tools/Makefile:
 

all: $(TOPDIR)/include/asm-arm/mach-types.h \
        $(TOPDIR)/include/asm-arm/constants.h

$(TOPDIR)/include/asm-arm/mach-types.h: mach-types gen-mach-types
        awk -f gen-mach-types mach-types > $@

 
    由此判斷出,mach-types.h是如何生成的,主要是利用awk腳本處理生成。生成之後與s3c2410有關的部分爲:
 

#ifdef CONFIG_S3C2410_SMDK
# ifdef machine_arch_type
# undef machine_arch_type
# define machine_arch_type __machine_arch_type
# else
# define machine_arch_type MACH_TYPE_SMDK2410
# endif
# define machine_is_smdk2410() (machine_arch_type == MACH_TYPE_SMDK2410)
#else
# define machine_is_smdk2410() (0)
#endif

 
    由此就知道了,這裏的machine_arch_type爲193,所以此函數實際上執行:mdesc = setup_machine(193);它要填充結構體machine_desc,如下:
 

struct machine_desc {
    
/*
     * Note! The first four elements are used
     * by assembler code in head-armv.S
     */

    unsigned int        nr;        /* architecture number    */
    unsigned int        phys_ram;    /* start of physical ram */
    unsigned int        phys_io;    /* start of physical io    */
    unsigned int        io_pg_offst;    
/* byte offset for io 
                         * page tabe entry    */


    const char        *name;        /* architecture name    */
    unsigned int        param_offset;    /* parameter page    */

    unsigned int        video_start;    /* start of video RAM    */
    unsigned int        video_end;    /* end of video RAM    */

    unsigned int        reserve_lp0 :1;    /* never has lp0    */
    unsigned int        reserve_lp1 :1;    /* never has lp1    */
    unsigned int        reserve_lp2 :1;    /* never has lp2    */
    unsigned int        soft_reboot :1;    /* soft reboot        */
    void            (*fixup)(struct machine_desc *,
                     struct param_struct *, char **,
                     struct meminfo *);
    void            (*map_io)(void);/* IO mapping function    */
    void            (*init_irq)(void);
};

 
    另外,還提供了一系統的宏,用於填充該結構體:
 

/*
 * Set of macros to define architecture features. This is built into
 * a table by the linker.
 */

#define MACHINE_START(_type,_name) \
const struct machine_desc __mach_desc_##_type \
 __attribute__((__section__(".arch.info"))) = { \
        nr: MACH_TYPE_##_type, \
        name: _name,

#define MAINTAINER(n)

#define BOOT_MEM(_pram,_pio,_vio) \
        phys_ram: _pram, \
        phys_io: _pio, \
        io_pg_offst: ((_vio)>>18)&0xfffc,

#define BOOT_PARAMS(_params) \
        param_offset: _params,

#define VIDEO(_start,_end) \
        video_start: _start, \
        video_end: _end,

#define DISABLE_PARPORT(_n) \
        reserve_lp##_n: 1,

#define BROKEN_HLT /* unused */

#define SOFT_REBOOT \
        soft_reboot: 1,

#define FIXUP(_func) \
        fixup: _func,

#define MAPIO(_func) \
        map_io: _func,

#define INITIRQ(_func) \
        init_irq: _func,

#define MACHINE_END \
};

 
    EDUKIT填充了一個結構體,用如下的方式:
 

MACHINE_START(SMDK2410, "Embest EduKit III (S3C2410x)")
    BOOT_MEM(0x30000000, 0x48000000, 0xe8000000)
    BOOT_PARAMS(0x30000100)
    FIXUP(fixup_smdk)
    MAPIO(smdk_map_io)
    INITIRQ(s3c2410_init_irq)
MACHINE_END

 
    看到有特殊的設置部分,那就是開始爲之分配了一個段,段的名字是.arch.info,也就是說把這部分信息單獨作爲一個段來進行處理。下面把這個宏展開如下:
 

const struct machine_desc __mach_desc_smdk2410 = {
    nr: 193,
    name: "EDUKIT-III (s3c2410)",
    phys_ram: 0x30000000,
    phys_to: 0x48000000,
    io_pg_offset: 0x3a00,
    param_offset: 0x30000100,
    fixup: fixup_smdk,//實際上爲空
    map_io: smdk_map_io,
    init_irq: s3c2410_init_irq,
};

 
    可見,基本的信息已經具備了,而且從這裏,我們也可以看出,啓動參數地址由這個段就可以完成,不需要傳遞了。當然,必須保證bootloader的值,與此處的相同。這樣,也就說明如果不使用R2傳遞參數的起始地址,那麼這個地方就需要把這個結構體設置好。
 
    下面看看這個函數完成什麼功能:
 

static struct machine_desc * __init setup_machine(unsigned int nr)
{
    extern struct machine_desc __arch_info_begin, __arch_info_end;
    struct machine_desc *list;

    
/*
     * locate architecture in the list of supported architectures.
     */

    for (list = &__arch_info_begin; list < &__arch_info_end; list++)
        if (list->nr == nr)
            break;

    
/*
     * If the architecture type is not recognised, then we
     * can co nothing...
     */

    if (list >= &__arch_info_end) {
        printk("Architecture configuration botched (nr %d), unable "
         "to continue.\n", nr);
        while (1);
    }

    printk("Machine: %s\n", list->name);

    return list;
}

 
    這個地方就是要把上面這一系列的信息連貫起來,那麼就不難理解了。上述的宏已經完成了.arch.info段,這個段實際上在內存中就是一個 machine_desc形式組織的信息(對Linux內核來說,並不一定僅僅有一個結構塊),上述函數的兩個變量__arch_info_begin和 __arch_info_end很明顯是有鏈接腳本傳遞進來。於是查看近層的鏈接腳本(【arch/arm/vmlinux-armv.lds.in】,可以發現:
 

.init : { /* Init code and data */
                _stext = .;
                __init_begin = .;
                        *(.text.init)
                __proc_info_begin = .;
                        *(.proc.info)
                __proc_info_end = .;
                __arch_info_begin = .;
                        *(.arch.info)
                __arch_info_end = .;

                __tagtable_begin = .;
                        *(.taglist)
                __tagtable_end = .;

 
    所以上述的功能就很簡單了,就是查看是否有mach-type爲193的結構存在,如果存在就打印出name,這也就是開機啓動後,出現Machine: Embest EduKit III (S3C2410)的原因了。
 
    接下來關注:
 

    if (mdesc->param_offset)
        tags = phys_to_virt(mdesc->param_offset);

 
   很明顯,這裏的mdesc->param_offset並不爲0,而是0x30000100,所以要做一步變換,就是物理地址映射成虛擬地址。把這個地址附給tags指針。然後就是判斷是param_struct類型還是tags類型,如果是param_struct類型,那麼首先轉換成tags類型,然後對tags類型進行解析。
 

    if (tags && tags->hdr.tag != ATAG_CORE)
        convert_to_tag_list((struct param_struct *)tags,
                 meminfo.nr_banks == 0);

    if (tags && tags->hdr.tag == ATAG_CORE)
        parse_tags(tags);

 
    要注意parse_tags函數是非常重要的,它有隱含的功能,不太容易分析。跟蹤上去,主要看這個函數:
 

/*
 * Scan the tag table for this tag, and call its parse function.
 * The tag table is built by the linker from all the __tagtable
 * declarations.
 */

static int __init parse_tag(const struct tag *tag)
{
    extern struct tagtable __tagtable_begin, __tagtable_end;
    struct tagtable *t;

    for (= &__tagtable_begin; t < &__tagtable_end; t++)
        if (tag->hdr.tag == t->tag) {
            t->parse(tag);
            break;
        }

    return t < &__tagtable_end;
}

 
    這裏又用到鏈接器傳遞參數,現在就是來解析每個部分。先看一下tagtable是如何來的。首先看【include/asm-arm/setup.h】,看看宏的定義,也就是帶有__tag,就歸屬爲.taglist段。
 

#define __tag __attribute__((unused, __section__(".taglist")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }

 
    利用__tag有構造了一個複雜的宏__tagtable,實際上就是定義了tagtable列表。現在看setup.c中的宏形式示例:
 

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);

 
    展開之後爲:
 

static struct tagtable __tagtable_ATAG_CMDLINE __tag = {
    ATAG_CMDLINE,
    parse_tag_cmdline
};

 
    於是,段.taglist就是這樣一系列的結構體。那麼上述的函數實際上就是把傳遞進來的tag與此表比較,如果tag標記相同,證明設置了此部分功能,就執行相應的解析函數。以ATAG_CMDLINE爲例,就要執行:
 

static int __init parse_tag_cmdline(const struct tag *tag)
{
#ifndef CONFIG_NO_TAG_CMDLINE
    strncpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
#endif
    default_command_line[COMMAND_LINE_SIZE - 1] = '\0';
    return 0;
}

 
    這樣也就是實現了把tag中的命令行參數複製到了default_command_line中。
 
    在返回來到函數【arch/arm/kernel/setup.c】,看函數setup_arch,定義中有:
 

char *from = default_command_line;

 
    說明from指向數組default_command_line。於是知道,當你完成tag解析的時候,所有傳遞過來的參數實際上已經複製到了相應的部分,比如命令行設置複製到了default_command_line。其他類似,看相應的解析行爲函數就可以了。因爲現在vivi只是傳遞了命令行,所以只是分析清楚這個。後面執行:
 

memcpy(saved_command_line, from, COMMAND_LINE_SIZE);

 
    這就比較容易理解了,就是將傳遞進來的命令行參數複製到saved_command_line,後面還可以打印出此信息。再往後的工作已經與此情景關係不大,所以不再進行詳細分析。
 
    至此,vivi與Linux kernel的參數傳遞情景分析就完成了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章