2012-02-25

Activity的生命週期


Activity的生命週期



內容說明:Activity的相關應用整理與生命週期說明
資料來源:Android Developers

Activity是讓我們進行某個功能時的畫面,它的產生是採取堆疊制的,越晚出現的畫面放在最上面,一旦最後出現的畫面被關閉,就會開啟倒數第二個畫面,即是我們在操作Android手機時,若按下"返回"鍵,就會回到上一個畫面的意思。

Activity的生命週期如圖所示:

onCreate()
當activity第一次被啟動時呼叫。此時系統會建立activity的畫面,並且可以透過Bundle等物件進行初始化設定(參考「Activity間互相傳值」)。

onStart()
讓activity能被使用者看見。

onResume()
將activity移到堆疊的最上層,讓使用者可以和activity互動。

onPause()
當有簡訊、電話、Toast、AlertDialog等另外一個activity進行時,進入onPause()的狀態,停止對螢幕的存取能力。如果畫面沒有轉移,則回到onResume();若畫面轉移到新的activity時,則進入onStop()。

onStop()
當使用者看不見此activity的畫面時產生。如果這個activity無法透過返回鍵回復,則進入onDestroy();反之則繼續停留在Stop狀態,當使用者返回此頁面時,則進入onRestart()。

onRestart()
重新啟動Stop狀態的activity。

onDestroy()
當activity被銷毀時執行。銷毀的原因有幾種:activity不會再被使用、程式呼叫finish()、系統因記憶體不足而銷毀Stop程序。要確認activity是否被銷毀可使用 isFinishing() 方法。
這些控制生命週期的方法可以在Eclipse選單 Source > Override/Implement Methods 中找到。
在正常情況下,記憶會保存使用者輸入的資料,但是在記憶體不足時,系統會銷毀Stop狀態的activity,此時使用者輸入的資料會消失。此外, 若是系統設定在程式運行中改變(例如螢幕方向、鍵盤狀態和語言),則activity會立即銷毀並重建(執行onDestroy()後接著執行 onCreate())。

因此如果我們寫的程式不希望使用者輸入的資料因為種種原因而消失,則必需要在onPause()的時候將資料儲存起來,並且在onCreate()將資料讀出,重新顯示在畫面上。

2012-02-03

File System和MTD技术

本節介紹File System和MTD技術

一 FS
熟知的FS有ext2,3,4.但是這些都是針對磁盤設備的。而ES中一般的存儲設備為Flash,由於Flash的特殊性:
  • Flash存儲按照Block size進行劃分,而一個BLS一般有幾十K。(對比磁盤的一個簇才512個字節)。這麼大的BLS有什麼壞處呢?很明顯,擦除一個BL就需要花費很長的時間了。
  • 另外,FLASH操作,一次必須針對一個BL,也就是如果我想修改一個字節的話,也必須先擦掉128K。這不是想死嗎?
  • FLASH每個BL擦寫次數是有限的,例如2百萬次?如果每次都操作同一個BL的話,很快這個BL就會死掉。
所以,針對FLASH的特性,整出了一個Journaling Flash File System(JFFS2,第二版)。它的特點就是:
  • 損耗均衡,也就是每次擦寫都不會停留在一個BL上。例如BL1上寫一個文件,那麼再來一個新文件的時候,FS就不會選擇BL1了,而是選擇BL2。這個技術叫weal leveling:損耗均衡
(apt-get install mtd-tools,下載不少好工具,另外,可見往flash設備寫log,可能會導致flash短命喔)
一些偽文件系統:proc/sysfs,systool工具用來分析sysfs。
ES中常用的FS還有ramfs/rootfs/tmpfs,都是基於內存的FS。這個和前面講的initramfs有什麼關係?實際上這些是基於Initramfs的。
這裡要解釋一下幾個比如容易混淆的東西:
  • ram disk:這個是一個基於ram的disk,用來模擬block設備的,所以它是建立在一個虛擬的BLOCK設備上的,至於上面的FS用什麼,無所謂.這樣就引起效率等的問題。畢竟你的read等操作還是要傳遞到驅動的,並且如果該設備上構造了一個EXT2 FS的話,還得有相應的ext2-FS模塊。麻煩..
  • ramfs:這是一個基於內存的FS,而不是一個基於真實設備的FS。ramfs的特點就是用戶可以死勁寫內存,直到把系統內存搞空。
  • 為了控制一下上面這個缺點,引出了tmpfs,可以指定tmpfs的size。(這個又好像和真實設備有點類似,因為真實設備的存儲空間也是有限的)
  • rootfs是一種特殊的ramfs或者tmpfs(看LK中是否啟用了tmpfs),另外,rootfs是不能被umount的
下面介紹一下如何利用mount loop創建一個虛擬的基於文件的BLOCK設備。
  • 先創建一個全是0的文件,利用dd命令:dd if=/dev/zero of=../fstest bs=1024 count=512 這個解釋如下:從if中拷貝數據到of,每次拷貝字節為1024,拷貝總次數為512. 各位可用十六制工具看看,生成的文件裡邊全是0X00
  • 在這個文件中創建FS,mkfs.ext2fs ../fstest。現在,FS就存在於這個文件了。其實FS也就是一些組織結構,例如superblock,inode等信息
  • 如何把這個帶有FS信息的文件掛載呢?其實也就是如何把這個文件當做一個Block device呢?利用mount的loop選項,mount -t ext2 -o loop fstest /tmp/。這樣這個文件就被當做一個虛擬Block設備掛載到tmp了。
二 MTD技術
MTD全稱是Memory Technology Device,內存技術設備?實際上是一個虛擬設備驅動層,類似Virtual File System。它提供標準API給那些操作Raw Flash的device driver。那麼Flash device和普通的Block device的區別是什麼呢?
  • 普通的BLD只有兩種操作:read和write
  • 而Flash Device有三種操作:read,write和erase,另外,還需要一種wear leveling算法來做損耗均衡
這裡要重點指出的是:
SD/MMC卡、CF(Compact Flash)卡、USB Flash等並不是MTD設備,因為這些設備中已經有一個內置的Flash Translation Layer,這個layer處理erase、wear leveling事情了(這個TL應該是固件中支持的)。所以這些設備直接當做普通的Block Device使用
(上面的描述還是沒能說清楚MTD到底是怎麼用的,以後會結合源碼分析一下)

2.1 內核中啟用MTD支持
這個很簡單,make menuconfig的時候打開就行了,有好幾個選項。
圖1 LK中MTD支持的配置選項
其中:
  • MTC_CHAR和MTD_BLOCK用來支持CHAR模式和BLOCK模式讀寫MTD設備。這個和普通的char以及block設備意思一樣
  • 最後兩個是在內核中設置一個MTD test驅動。8192K用來設置總大小,第二個128用來設置block size。就是在內核中搞了一個虛擬的Flash設備,用作測試
ES中又如何配置MTD及其相關的東西呢?
  • 為Flash Disk設置分區(也可以整個Device就一個分區。BTW,我一直沒徹底搞清楚分區到底是想幹什麼,這個可能是歷史原因啦....)
  • 設置Flash的類型以及location。Flash設備分為NOR和NAND,本節最後會簡單介紹下二者的區別。
  • 為Flash芯片選擇合適的driver
  • 為LK配置driver
下面先看看分區的設置
可對Flash分區,這裡有一些稍微重要的內容:如何把Flash分區的信息傳遞給LK呢?有兩種方法:
  • 將整個device的分區情況存在一個BLock中,這樣BootLoader啟動的時候,根據這個BLock中的內容建立相應信息等。好像只有Red Boot支持。所以叫RedBoot Partition Table。另外,LK可以識別這種分區,通過CFI(Command Flash Interface)讀取這個分區的信息。
  • Kernel Command Line Partitioning:通過Kernel啟動的時候傳入參數,不過KL必須配置一下。Command格式如下:
圖2
再看看Driver的Mapping,也就是將MTD和對應的Flash Driver配對...
kernel/drivers/mtd/maps......,以後要分析
Flash芯片本身的Driver呢?
kernel/drivers/mtd/chips,目前比較流行的是CFI接口
三 一些參考資料和補充知識
MTD的本意是:
We're working on a generic Linux subsystem for memory devices, especially Flash devices.
The aim of the system is to make it simple to provide a driver for new hardware, by providing a generic interface between the hardware drivers and the upper layers of the system.
Hardware drivers need to know nothing about the storage formats used, such as FTL, FFS2, etc., but will only need to provide simple routines for read, write and erase. Presentation of the device's contents to the user in an appropriate form will be handled by the upper layers of the system.
MTD overview
MTD subsystem (stands for Memory Technology Devices) provides an abstraction layer for raw flash devices. It makes it possible to use the same API when working with different flash types and technologies, e.g. NAND, OneNAND, NOR, AG-AND, ECC'd NOR, etc.
MTD subsystem does not deal with block devices like MMC, eMMC, SD, CompactFlash, etc. These devices are not raw flashes but they have a Flash Translation layer inside, which makes them look like block devices. These devices are the subject of the Linux block subsystem, not MTD. Please, refer to this FAQ section for a short list of the main differences between block and MTD devices. And the raw flash vs. FTL devices UBIFS section discusses this in more details.
MTD subsystem has the following interfaces.
  • MTD character devices - usually referred to as /dev/mtd0, /dev/mtd1, and so on. These character devices provide I/O access to the raw flash. They support a number of ioctl calls for erasing eraseblocks, marking them as bad or checking if an eraseblock is bad, getting information about MTD devices, etc. /dev/mtdx竟然是char device!!
  • The sysfs interface is relatively newer and it provides full information about each MTD device in the system. This interface is easily extensible and developers are encouraged to use the sysfs interface instead of older ioctl or /proc/mtd interfaces, when possible.
  • The /proc/mtd proc file system file provides general MTD information. This is a legacy interface and the sysfs interface provides more information.
    MTD subsystem supports bare NAND flashes with software and hardware ECC, OneNAND flashes, CFI (Common Flash Interface) NOR flashes, and other flash types.
Additionally, MTD supports legacy FTL/NFTL "translation layers", M-Systems' DiskOnChip 2000 and Millennium chips, and PCMCIA flashes (pcmciamtd driver). But the corresponding drivers are very old and not maintained very much.
MTD Block Driver:
The mtdblock driver available in the MTD is an archaic tool which emulates block devices on top of MTD devices. It does not even have bad eraseblock handling, so it is not really usable with NAND flashes. And it works by caching a whole flash erase block in RAM, modifying it as requested, then erasing the whole block and writing back the modified. This means that mtdblock does not try to do any optimizations, and that you will lose lots of data in case of power cuts. And last, but not least, mtdblock does not do any wear-leveling.
Often people consider mtdblock as general FTL layer and try to use block-based file systems on top of bare flashes using mtdblock. This is wrong in most cases. In other words, please, do not use mtdblock unless you know exactly what you are doing.There is also a read-only version of this driver which doesn't have the capacity to do the caching and erase/writeback, mainly for use with uCLinux where the extra RAM requirement was considered too large
These are the modules which provide interfaces that can be used directly from userspace. The user modules currently planned include:
  • Raw character access: A character device which allows direct access to the underlying memory. Useful for creating filesystems on the devices, before using some of the translation drivers below, or for raw storage on infrequently-changed flash, or RAM devices.
  • Raw block access :A block device driver which allows you to pretend that the flash is a normal device with sensible sector size. It actually works by caching a whole flash erase block in RAM, modifying it as requested, then erasing the whole block and writing back the modified data.This allows you to use normal filesystems on flash parts. Obviously it's not particularly robust when you are writing to it - you lose a whole erase block's worth of data if your read/modify/erase/rewrite cycle actually goes read/modify/erase/poweroff. But for development, and for setting up filesystems which are actually going to be mounted read-only in production units, it should be fine. There is also a read-only version of this driver which doesn't have the capacity to do the caching and erase/writeback, mainly for use with uCLinux where the extra RAM requirement was considered too large.
  • Flash Translation Layer (FTL):NFTL,Block device drivers which implement an FTL/NFTL filesystem on the underlying memory device. FTL is fully functional. NFTL is currently working for both reading and writing, but could probably do with some more field testing before being used on production systems.
  • Journalling Flash File System, v2:This provides a filesystem directly on the flash, rather than emulating a block device. For more information, see sources.redhat.com.
  • MTD hardware device drivers

    These provide physical access to memory devices, and are not used directly - they are accessed through the user modules above.

    On-board memory:Many PC chipsets are incapable of correctly caching system memory above 64M or 512M. A driver exists which allows you to use this memory with the linux-mtd system.
  • PCMCIA devices:PCMCIA flash (not CompactFlash but real flash) cards are now supported by the pcmciamtd driver in CVS.
  • Common Flash Interface (CFI) onboard NOR flash:This is a common solution and is well-tested and supported, most often using JFFS2 or cramfs file systems.
  • Onboard NAND flash:NAND flash is rapidly overtaking NOR flash due to its larger size and lower cost; JFFS2 support for NAND flash is approaching production quality.
  • M-Systems' DiskOnChip 2000 and Millennium:The DiskOnChip 2000, Millennium and Millennium Plus devices should be fully supported, using their native NFTL and INFTL 'translation layers'. Support for JFFS2 on DiskOnChip 2000 and Millennium is also operational although lacking proper support for bad block handling.
這裡牽扯到NOR和NAND,那麼二者有啥區別呢?
Beside the different silicon cell design, the most important difference between NAND and NOR Flash is the bus interface. NOR Flash is connected to a address / data bus direct like other memory devices as SRAM etc. NAND Flash uses a multiplexed I/O Interface with some additional control pins. NAND flash is a sequential access device appropriate for mass storage applications, while NOR flash is a random access device appropriate for code storage application. NOR Flash can be used for code storage and code execution. Code stored on NAND Flash can't be executed from there. It must be loaded into RAM memory and executed from there.
  • NOR可以直接和CPU相連,就好像內存一樣。NAND不可以,因為NAND還需要別的一些I/O控制接口。所以NAND更像磁盤,而NOR更像內存
  • NOR比NAND貴,並且,NAND支持順序讀取,而NOR支持隨機讀取。
  • 所以,NOR中可存儲代碼,這樣CPU直接讀取就在其中運行。NAND不可以(主要還是因為CPU取地址的時候不能直接找到NAND)

Polling, Interrupt-driven I/O, DMA(Direct Memory Access)

三種將資料在I/O間傳送的方法有
1. Polling
2. Interrupt-driven I/O
3. DMA(Direct Memory Access)

Polling:最簡單的方式讓I/O device與CPU溝通。I/O device只要將information放進status register,CPU會週期性的檢查並取得information來得知需要服務的device。

Interrupt-driven I/O:利用interrupt的機制,當一個I/O device需要服務時,會發出interrupt來通知CPU。一個I/O interrupt對於指令的執行是asynchronous,Control unit需要在執行一個新指令時檢查是否有pending I/O interrupt。I/O interrupt也會傳達更多訊息,如哪個device發出的interrupt和它的priority。

DMA(Direct Memory Access):提供一個device controller,讓I/O device能夠直接在記憶體做資料的傳輸,不需要CPU的參與。

DMA transfer的三個步驟
1. CPU要初始化DMA controller,包括提供I/O device的名稱、執行何種運算、記憶體位置以及所要傳送的bytes數。

2. DMA開始運作。如果request需要多於一次的transfer在bus上,DMA unit會產生下一個memory address和初始下一次的transfer。

3. 一旦DMA transfer完成,DMA controller發出一個interrupt給CPU。


優缺點比較
 Polling  Interrupt-driven I/O DMA 
優點 簡單容易執行,可利用軟體來更改CPU polling的順序 不用浪費許多時間在polling上面 適用於高速裝置
不用浪費許多時間在polling上面 
缺點 因為CPU速度遠快於I/O device,會浪費許多時間在polling上
不適合high-bandwidth devices
需要有interrupt signals和interrupt service routine存在
不適合high-bandwidth devices
需要有DMA controller

2012-02-02

u-boot common/main.c

接下来我们主要分析main_loop()
common/main.c:
void main_loop (void)
{
#ifndef CFG_HUSH_PARSER
    static char lastcommand[CFG_CBSIZE] = { 0, };
    int len;
    int rc = 1;
    int flag;
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    char *s;
    int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
    char *p;
#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
    unsigned long bootcount = 0;
    unsigned long bootlimit = 0;
    char *bcs;
    char bcs_set[16];
#endif /* CONFIG_BOOTCOUNT_LIMIT */

#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)   /*smdk2410没定义*/
    ulong bmp = 0;     /* default bitmap */
    extern int trab_vfd (ulong bitmap);

#ifdef CONFIG_MODEM_SUPPORT
    if (do_mdm_init)
        bmp = 1;    /* alternate bitmap */
#endif
    trab_vfd (bmp);
#endif  /* CONFIG_VFD && VFD_TEST_LOGO */

#ifdef CONFIG_BOOTCOUNT_LIMIT   /*smdk2410没定义*/
    bootcount = bootcount_load();
    bootcount++;
    bootcount_store (bootcount);
    sprintf (bcs_set, "%lu", bootcount);
    setenv ("bootcount", bcs_set);
    bcs = getenv ("bootlimit");
    bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
#endif /* CONFIG_BOOTCOUNT_LIMIT */

#ifdef CONFIG_MODEM_SUPPORT /*smdk2410没定义*/
    debug ("DEBUG: main_loop:   do_mdm_init=%d/n", do_mdm_init);
    if (do_mdm_init) {
        uchar *str = strdup(getenv("mdm_cmd"));
        setenv ("preboot", str);  /* set or delete definition */
        if (str != NULL)
            free (str);
        mdm_init(); /* wait for modem connection */
    }
#endif  /* CONFIG_MODEM_SUPPORT */

#ifdef CONFIG_VERSION_VARIABLE /*smdk2410没定义*/
    {
        extern char version_string[];

        setenv ("ver", version_string);  /* set version variable */
    }
#endif /* CONFIG_VERSION_VARIABLE */

#ifdef CFG_HUSH_PARSER /*smdk2410没定义*/
    u_boot_hush_start ();
#endif

#ifdef CONFIG_AUTO_COMPLETE /*smdk2410没定义*/
    install_auto_complete();
#endif

#ifdef CONFIG_PREBOOT /*smdk2410没定义*/
    if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED /*smdk2410没定义*/
        int prev = disable_ctrlc(1);   /* disable Control C checking */
# endif

# ifndef CFG_HUSH_PARSER /*smdk2410没定义*/
        run_command (p, 0);
# else
        parse_string_outer(p, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
# endif

# ifdef CONFIG_AUTOBOOT_KEYED /*smdk2410没定义*/
        disable_ctrlc(prev);   /* restore Control C checking */
# endif
    }
#endif /* CONFIG_PREBOOT */

/**/
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    s = getenv ("bootdelay");
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

    debug ("### main_loop entered: bootdelay=%d/n/n", bootdelay);

# ifdef CONFIG_BOOT_RETRY_TIME
    init_cmd_timeout ();
# endif /* CONFIG_BOOT_RETRY_TIME */

#ifdef CONFIG_BOOTCOUNT_LIMIT
    if (bootlimit && (bootcount > bootlimit)) {
        printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd./n",
                (unsigned)bootlimit);
        s = getenv ("altbootcmd");
    }
    else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
        s = getenv ("bootcmd");  /*smdk2410下这个环境变量是空的*/

    debug ("### main_loop: bootcmd=/"%s/"/n", s ? s : "<UNDEFINED>");

    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
        int prev = disable_ctrlc(1);   /* disable Control C checking */
# endif

# ifndef CFG_HUSH_PARSER
        run_command (s, 0);
# else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
# endif

# ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);   /* restore Control C checking */
# endif
    }

# ifdef CONFIG_MENUKEY
    if (menukey == CONFIG_MENUKEY) {
        s = getenv("menucmd");
        if (s) {
# ifndef CFG_HUSH_PARSER
        run_command (s, 0);
# else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
# endif
        }
    }
#endif /* CONFIG_MENUKEY */
#endif  /* CONFIG_BOOTDELAY */

#ifdef CONFIG_AMIGAONEG3SE
    {
        extern void video_banner(void);
        video_banner();
    }
#endif

    /*
     * Main Loop for Monitor Command Processing
     */
     /*
      * 最核心的就是下面这个死循环了
      */
#ifdef CFG_HUSH_PARSER
    parse_file_outer();
    /* This point is never reached */
    for (;;);
#else
    for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
        if (rc >= 0) {
            /* Saw enough of a valid command to
             * restart the timeout.
             */
            reset_cmd_timeout();
        }
#endif
        len = readline (CFG_PROMPT);

        flag = 0;   /* assume no special flags for now */
        if (len > 0)
            strcpy (lastcommand, console_buffer);
        else if (len == 0)
            flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
        else if (len == -2) {
            /* -2 means timed out, retry autoboot
             */
            puts ("/nTimed out waiting for command/n");
# ifdef CONFIG_RESET_TO_RETRY
            /* Reinit board to run initialization code again */
            do_reset (NULL, 0, 0, NULL);
# else
            return;     /* retry autoboot */
# endif
        }
#endif

        if (len == -1)
            puts ("<INTERRUPT>/n");
        else
            rc = run_command (lastcommand, flag);

        if (rc <= 0) {
            /* invalid command or not repeatable, forget it */
            lastcommand[0] = 0;
        }
    }
#endif /*CFG_HUSH_PARSER*/
}
该函数比较长,但核心就是不停的从串口获取命令并解析执行此命令, 这个功能就在函数尾部的那个死循环里, 我们重点就是分析这个循环。

先看readline()
common/main.c:
/****************************************************************************/
/*
 * Prompt for input and read a line.
 * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
 * time out when time goes past endtime (timebase time in ticks).
 * Return:  number of read characters
 *      -1 if break
 *      -2 if timed out
 * 上面的注释说的很清楚了,就是提示用户输入命令,并读取这个命令
*/
int readline (const char *const prompt)
{
    char   *p = console_buffer;   /* console_buffer 是个全局变量*/
    int n = 0;             /* buffer index    */
    int plen = 0;          /* prompt length   */
    int col;               /* output column cnt   */
    char    c;

    /* print prompt */
    if (prompt) { /*如果要输出提示符,则输出*/
        plen = strlen (prompt);
        puts (prompt);  //前面分析过了
    }
    col = plen;

    for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME  /*sdmk2410没定义*/
        while (!tstc()) {  /* while no incoming data */
            if (retry_time >= 0 && get_ticks() > endtime)
                return (-2);    /* timed out */
        }
#endif
        WATCHDOG_RESET();      /* Trigger watchdog, if needed */

#ifdef CONFIG_SHOW_ACTIVITY /*sdmk2410没定义*/
        while (!tstc()) {
            extern void show_activity(int arg);
            show_activity(0);
        }
#endif
        c = getc();  /*从串口获取用户输入的命令*/

        /*
         * Special character handling
         */
        /*下面这个switch就是处理不同的字符了*/
        switch (c) {
        case '/r':             /* Enter        */
        case '/n':
            *p = '/0';
            puts ("/r/n");
            return (p - console_buffer);  /*输入结束*/

        case '/0':             /* nul          */  /*忽略,继续*/
            continue;

        case 0x03:             /* ^C - break*//*呵呵,linux下的ctrl+c功能*/
            console_buffer[0] = '/0';  /* discard input */
            return (-1);

        case 0x15:             /* ^U - erase line */ /*删除*/
            while (col > plen) {
                puts (erase_seq);
                --col;
            }
            p = console_buffer;
            n = 0;
            continue;

        case 0x17:             /* ^W - erase word *//*删除*/
            p=delete_char(console_buffer, p, &col, &n, plen);
            while ((n > 0) && (*p != ' ')) {
                p=delete_char(console_buffer, p, &col, &n, plen);
            }
            continue;

        case 0x08:             /* ^H  - backspace */
        case 0x7F:             /* DEL - backspace */
            p=delete_char(console_buffer, p, &col, &n, plen);/*删除*/
            continue;

        default:   /*获取常规字符*/
            /*
             * Must be a normal character then
             */
            if (n < CFG_CBSIZE-2) {
                if (c == '/t') {   /* expand TABs     */
#ifdef CONFIG_AUTO_COMPLETE
                   /* if auto completion triggered just continue */
                    /*自动补全功能,熟悉linux的肯定知道*/
                   *p = '/0';
                   if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
                       p = console_buffer + n; /* reset */
                       continue;
                   }
#endif
                   puts (tab_seq+(col&07));
                   col += 8 - (col&07);
                } else {
                    ++col;      /* echo input      */
                   putc (c);
                }
                *p++ = c;/*把字符保存在buffer*/
                ++n;
            } else {           /* Buffer full     */
                putc ('/a');
            }
        }
    }
}
上面这个函数就是提示用户输入,然后一直等待用户输入,指到用户输入完成, 然后获取用户数据,并存入全局变量console_buffer中。

再来回顾下那个for循环
common/main.c:
void main_loop (void)
{
……
#ifdef CFG_HUSH_PARSER
    parse_file_outer();
    /* This point is never reached */
    for (;;);
#else
    for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
        if (rc >= 0) {
            /* Saw enough of a valid command to
             * restart the timeout.
             */
            reset_cmd_timeout();
        }
#endif
        len = readline (CFG_PROMPT); /*获取用户下的command*/

        flag = 0;   /* assume no special flags for now */
        if (len > 0)
            strcpy (lastcommand, console_buffer);  /*command存入lastcommand*/
        else if (len == 0)
            flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
        else if (len == -2) {
            /* -2 means timed out, retry autoboot
             */
            puts ("/nTimed out waiting for command/n");
# ifdef CONFIG_RESET_TO_RETRY
            /* Reinit board to run initialization code again */
            do_reset (NULL, 0, 0, NULL);
# else
            return;     /* retry autoboot */
# endif
        }
#endif

        if (len == -1)
            puts ("<INTERRUPT>/n");
        else
            rc = run_command (lastcommand, flag); /*执行command*/

        if (rc <= 0) {
            /* invalid command or not repeatable, forget it */
            lastcommand[0] = 0;
        }
    }
#endif /*CFG_HUSH_PARSER*/
}
OK!核心中的核心就是run_command函数了,一猜就知道是用户执行所有用户下达命令的函数。Let’s go