linux ,Android基礎知識總結

來源: ChinaUnix博客  日期: 2009.10.25 08:53 (共有條評論) 我要評論
 
===================================================
linux ,Android基礎知識總結
1. Android編譯系統分析
2. 文件系統分析
3. 製作交叉工具鏈
4. 軟件編譯常識
5. 設置模塊流程分析
6. linux系統啓動流程分析
7. linux下svn使用指南
8. LFS 相關
9. linux 內核的初步理解
====================================================
================
android系統開發指南(常用環境的搭建和使用)
說明:
有的步驟會用到腳本簡化操作,腳本通過svn服務器獲取:
svn co svn://192.168.2.148/smartphone/td0901/release/images/scripts
用戶名爲各位的姓名拼音,密碼與用戶名相同
一  編譯android源碼,製作文件系統
二   ubuntu下燒錄內核和文件系統
一  編譯android源碼,製作文件系統
1. 開發主線源碼位置:
svn://192.168.2.148/smartphone/td0901/trunk/cupcake-jianping  //cupcake 源代碼
svn://192.168.2.148/smartphone/td0901/trunk/linux-2.6.28-a1   //內核源代碼
2. 打標的源代碼位置
svn list svn://192.168.2.148/smartphone/td0901/tag
我們可以通過 svn list  svn://192.168.2.148/smartphone 查看svn版本庫內核
更多信息請參卡以下文檔:
http://192.168.2.148/svn/smartphone/
http://192.168.2.148/svn/smartphone/智能平臺開發部資料管理手冊V1.0.doc
http://192.168.2.148/svn/smartphone/linux下svn操作指南及規範.doc
用戶名爲各位的姓名拼音,密碼與用戶名相同
3. 編譯源碼
進入 cupcake 工作拷貝的頂層目錄,執行:
. ./make_image15.sh
部分執行結果:
out/target/product/littleton/root/   內核需要使用的 initramfs 
out/target/product/littleton/system  文件系統的系統分區
out/target/product/littleton/data/   文件系統數據分區
4. 編譯內核
此處內核編譯主要針對驅動組之外的同事
1> 設置工具鏈
內核的 linux-2.6.28-a1/Makefile 中設定了:
CROSS_COMPILE        ?= arm-linux-
所以設置PATH環境變量,保證能找到正確的工具鏈
假設工具鏈位於: /usr/local/marvell-arm-linux-4.1.1/ 設置爲:
export PATH:=/usr/local/marvell-arm-linux-4.1.1/bin/:$PATH
2> 更改編譯選項(網絡啓動或者本機啓動)
內核頂層目錄執行:
make menuconfig
General setup  ---> 
  
  • Initial RAM filesystem and RAM disk (initramfs/initrd) support
        ()    Initramfs source file(s) (NEW) 
    如果需要支持網絡啓動反選  [] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    如果需要支持本地啓動選中  
  • Initial RAM filesystem and RAM disk (initramfs/initrd) support
    設置 ()    Initramfs source file(s) (NEW) 爲 root
    拷貝  cupcake 編譯結果  out/target/product/littleton/root/  到內核頂層目錄
    3> 編譯
    內核頂層目錄執行 make zImage
    編譯好的內核:
    arch/arm/boot/zImage
    5. 搭建網絡開發環境
    1>  安裝nfs服務器
    sudo apt-get install nfs-kernel-server nfs-common
    2> 修改nfs服務器配置文件/etc/exports ,確保有以下配置項
    /nfsroot/rootfs *(rw,no_root_squash,sync)
    我們在內核中已經固定,手機通過網絡方式啓動,默認從 /nfsroot/rootfs 
    讀取文件系統,修改配置項後需要重啓nfs服務器:
    sudo /etc/init.d/nfs-kernel-server restart
    3> 配置網絡根文件系統
    拷貝  out/target/product/littleton/root/  內容到  /nfsroot/rootfs 目錄
    拷貝  out/target/product/littleton/system 內容到  /nfsroot/rootfs/system
    修改  /nfsroot/rootfs/init.rc 去掉幾個mount命令 
    爲了使大家的過程,結果統一,可以使用腳本 mkfs.cupcake 完成 
    在執行 mkfs.cupcake.nfs  腳本前先到  cupcake-jianping 目錄下執行: . ./make_env15.sh設置環境變量,
    獲取通過手動輸入android源碼的位置,讓腳本來設置環境變量。
    二   ubuntu下燒錄內核和文件系統
    1. 硬件: 
    手機一臺
    usb轉串口線一根
    usb轉網卡線一根
    2. 軟件環境
    1> tftp 服務器
    執行腳本: setup_tftpd.sh 安裝和配置tftp服務器,我們默認以 /tftpboot 
    爲 tftp服務器的根目錄,需要下載的文件都放在該目錄下。
    2> 獲取待燒錄的鏡像文件
    svn list svn://192.168.2.148/smartphone/td0901/release/images 查看服務器上的
    版本情況,通常我們下載最新的,例如,下載9月18號發佈的版本:
    svn co svn://192.168.2.148/smartphone/td0901/release/images/images20090918
    3> 燒錄鏡像文件
    用以下文件爲例,示範通過tftp燒寫內核和文件系統
    內核           zImage0917 
    系統分區: system0918.img  
    數據分區    data0918.img 
    待燒寫的以上文件必須存在於tftp服務器根目錄/tftpboot下。
    具體步驟:
    首先連接好硬件設備進入blob下載模式
    1> blob 起來後按任意鍵
    Processing obm parameters...
    Can't detect micco. Set PMIC as normal I2C mode.
    NAND flash(Manu=0x98 Device=0xba) detected!
    Slot 0 Found
    get relocation table
    Found Main Bad block table at address 0x0f000000, version 0x01
    Found Mirror Bad block table at address 0x0efc0000, version 0x01
    Consider yourself BLOBed!
    blob version 2.0.5-pre3 for Marvell Littleton
    Copyright (C) 1999 2000 2001 2002 2003 Jan-Derk Bakker and Erik Mouw
    blob comes with ABSOLUTELY NO WARRANTY; read the GNU GPL for details.
    This is free software, and you are welcome to redistribute it
    under certain conditions; read the GNU GPL for details.
    length not align with page size, change to 0x0
    Read flash from 0x60000, length 0x0
    Done
    Autoboot (2 seconds) in progress, press any key to stop ..
    Autoboot aborted
    Type "help" to get a list of commands
    blob> 
    2> 通過 tftp 下載內核到pc內存 0x80800000 地址處 
    blob> tftp zImage0917
    Begin init ether usbnet!!!
    ***** Plug-in USB cable & config usbdnet now ****** 
    exit check_usb_connection:1
    TFTPing zImage0917*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OK.
    received 6144 blocks (3145156 bytes)
    tftp_cmd: file 'zImage0917' loaded via tftp to address 0x80800000.
    3> 擦除原來的內核分區,0x100000 爲分區起始地址,0x300000爲分區長度
    blob> nanderase -z 0x100000 0x400000
    the current NAND chip does not support Block Unlocking.
    Erase 0x300000 length data from flash: 0x100000
    Erase flash from 0x100000, length 0x300000
    ........................Done
    4> 燒寫內存 0x80800000 開始 實際長度爲 3145156 的內核數據到起始地址爲 0x100000 的內核分區
    blob> nandwrite -z 0x80800000 0x100000 3145156
    the current NAND chip does not support Block Unlocking.
    Write 0x2ffdc4 length data from RAM: 0x80800000 to flash: 0x100000
    Write flash from 0x100000, length 0x2ffdc4
    Erase flash from 0x100000, length 0x300000
    ........................Done
    ........................Done
    5> 下載系統分區鏡像文件到pc內存 0x80800000 地址處 
    blob> tftp system0918.img
    TFTPing system0918.img*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OK.
    received 113138 blocks (57925824 bytes)
    tftp_cmd: file 'system0918.img' loaded via tftp to address 0x80800000.
    6> 擦除原來的flash系統分區
    blob> nanderase -z 0x500000 0x4000000
    the current NAND chip does not support Block Unlocking.
    Erase 0x3e0f800 length data from flash: 0x400000
    Erase flash from 0x400000, length 0x3e0f800
    ...................................................................................
    ...................................................................................
    ...................................................................................
    ..........................Done
    7> 燒寫數據到flash系統分區
    blob> nandwrite -y 0x80800000 0x500000 57925824
    the current NAND chip does not support Block Unlocking.
    Write 0x373e0c0 length data from RAM: 0x80800000 to flash: 0x400000
    Write flash from 0x400000, length 0x3591800
    Erase flash from 0x400000, length 0x3591800
    ....................................................................................
    .....................................................................................
    ................................................................................Done
    ....................................................................................
    ....................................................................................
    ................................................................Done
    8> 下載數據分區鏡像文件到pc內存 0x80800000 地址處 
    blob> tftp data0918.img
    TFTPing data0918.img*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OK.
    received 33992 blocks (17402880 bytes)
    tftp_cmd: file 'data0918.img' loaded via tftp to address 0x80800000.
    blob> 
    9> 擦除原來的flash數據分區
    blob> nanderase -z 0x4500000 0xBB00000
    the current NAND chip does not support Block Unlocking.
    Erase 0xa81f000 length data from flash: 0x4400000
    Erase flash from 0x4400000, length 0xa81f000
    .....................................................................................
    .....................................................................................
    .....................................................................................
    .....................................................................................
    ...................................................Done
    10> 燒寫數據鏡像到flash數據分區
    blob> nandwrite -y 0x80800000 0x4500000 17402880
    the current NAND chip does not support Block Unlocking.
    Write 0x1098c00 length data from RAM: 0x80800000 to flash: 0x4400000
    Write flash from 0x4400000, length 0x1018000
    Erase flash from 0x4400000, length 0x1018000
    ..................................................................................Done
    ..................................................................................Done
    blob> 
    flash分區圖:
    *******************************************
    *        *          *         *           *
    *  blob  *  kernel  *  system *    data   *
    *        *          *         *           *
    *******************************************
    nanderase -z 0x100000 0x400000
    tftp zImage
    nandwrite -z 0x80800000 0x100000 
    燒寫system.img:
    nanderase -z 0x500000 0x4000000
    tftp system.img
    nandwrite -y 0x80800000 0x500000 
    燒寫 userdata.img :
    nanderase -z 0x4500000 0xBB00000
    tftp userdata.img
    nandwrite -y 0x80800000 0x4500000 
    ============================
    涉及的內容:
    svn服務器的使用
    android的編譯系統,原理,工具鏈,輔助工具,參數等,環境變量,怎樣單獨添加編譯一個單獨的模塊等。
    android 的編譯結果:文件系統分析
    文件系統的使用,啓動流程
    設置模塊流程分析
    ============================
    ====================================================
    1. Android編譯系統分析
    編譯腳本及系統變量
    build/envsetup.sh腳本分析
    在編譯源代碼之前通常需要在android源代碼頂層目錄執行 . ./build/envsetup.sh 目的是爲了使用
    腳本 envsetup.sh 裏面定義了一些函數:
    function help()
    function get_abs_build_var()
    function get_build_var()
    function check_product()
    function check_variant()
    function setpaths()
    function printconfig()
    function set_stuff_for_environment()
    function set_sequence_number()
    function settitle()
    function choosetype()
    function chooseproduct()
    function choosevariant()
    function tapas()
    function choosecombo()
    function print_lunch_menu()
    function lunch()
    function gettop
    function m()
    function findmakefile()
    function mm()
    function mmm()
    function croot()
    function pid()
    function gdbclient()
    function jgrep()
    function cgrep()
    function resgrep()
    function getprebuilt
    function tracedmdump()
    function runhat()
    function getbugreports()
    function startviewserver()
    function stopviewserver()
    function isviewserverstarted()
    function smoketest()
    function runtest()
    function runtest_py()
    function godir ()
    choosecombo 命令分析:
    function choosecombo()
    {
        choosesim $1
        echo
        echo
        choosetype $2
        echo
        echo
        chooseproduct $3
        echo
        echo
        choosevariant $4
        echo
        set_stuff_for_environment
        printconfig
    }
    會依次進行如下選擇:
    Build for the simulator or the device?
         1. Device
         2. Simulator
    Which would you like? [1] 
    Build type choices are:
         1. release
         2. debug
    Which would you like? [1] 
    Product choices are:
         1. emulator
         2. generic
         3. sim
         4. littleton
    You can also type the name of a product if you know it.
    Which would you like? [littleton] 
    Variant choices are:
         1. user
         2. userdebug
         3. eng
    Which would you like? [eng] user
    默認選擇以後會出現:
    TARGET_PRODUCT=littleton
    TARGET_BUILD_VARIANT=user
    TARGET_SIMULATOR=false
    TARGET_BUILD_TYPE=release
    TARGET_ARCH=arm
    HOST_ARCH=x86
    HOST_OS=linux
    HOST_BUILD_TYPE=release
    BUILD_ID=
    ==========
    function chooseproduct()函數分析:
    choices=(`/bin/ls build/target/board/*/BoardConfig.mk vendor/*/*/BoardConfig.mk 2> /dev/null`)
    讀取 build/target/board/* 目錄下的板配置文件:BoardConfig.mk
    讀取 vendor/*/*/目錄下的板配置文件:BoardConfig.mk
    choices 的值爲:
    build/target/board/emulator/BoardConfig.mk
    build/target/board/generic/BoardConfig.mk
    build/target/board/sim/BoardConfig.mk
    vendor/marvell/littleton/BoardConfig.mk
    經過:
        for choice in ${choices[@]}                
        do
            # The product name is the name of the directory containing
            # the makefile we found, above.
            prodlist=(${prodlist[@]} `dirname ${choice} | xargs basename`)
        done
    的處理,prodlist的值爲:
    emulator generic sim littleton
    所以選擇菜單爲:
    Product choices are:
         1. emulator
         2. generic
         3. sim
         4. littleton
    如果選擇 4,那麼 TARGET_PRODUCT 被賦值爲: littleton。
    board_config_mk := \
            $(strip $(wildcard \
                    $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
                    vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \
            ))
    怎樣添加一個模塊
    LOCAL_PATH:= $(call my-dir)
    #編譯靜態庫
    include $(CLEAR_VARS)
    LOCAL_MODULE = libhellos
    LOCAL_CFLAGS = $(L_CFLAGS)
    LOCAL_SRC_FILES = hellos.c
    LOCAL_C_INCLUDES = $(INCLUDES)
    LOCAL_SHARED_LIBRARIES := libcutils
    LOCAL_COPY_HEADERS_TO := libhellos
    LOCAL_COPY_HEADERS := hellos.h
    include $(BUILD_STATIC_LIBRARY)
    #編譯動態庫
    include $(CLEAR_VARS)
    LOCAL_MODULE = libhellod
    LOCAL_CFLAGS = $(L_CFLAGS)
    LOCAL_SRC_FILES = hellod.c
    LOCAL_C_INCLUDES = $(INCLUDES)
    LOCAL_SHARED_LIBRARIES := libcutils
    LOCAL_COPY_HEADERS_TO := libhellod
    LOCAL_COPY_HEADERS := hellod.h
    include $(BUILD_SHARED_LIBRARY)
    BUILD_TEST=true
    ifeq ($(BUILD_TEST),true)
    #使用靜態庫
    include $(CLEAR_VARS)
    LOCAL_MODULE := hellos
    LOCAL_STATIC_LIBRARIES := libhellos
    LOCAL_SHARED_LIBRARIES :=
    LOCAL_LDLIBS += -ldl
    LOCAL_CFLAGS := $(L_CFLAGS)
    LOCAL_SRC_FILES := mains.c
    LOCAL_C_INCLUDES := $(INCLUDES)
    include $(BUILD_EXECUTABLE)
    #使用動態庫
    include $(CLEAR_VARS)
    LOCAL_MODULE := hellod
    LOCAL_MODULE_TAGS := debug
    LOCAL_SHARED_LIBRARIES := libc libcutils libhellod
    LOCAL_LDLIBS += -ldl
    LOCAL_CFLAGS := $(L_CFLAGS)
    LOCAL_SRC_FILES := maind.c
    LOCAL_C_INCLUDES := $(INCLUDES)
    include $(BUILD_EXECUTABLE)
    endif # ifeq ($(WPA_BUILD_SUPPLICANT),true)
    ########################
    #local_target_dir := $(TARGET_OUT)/etc/wifi
    #include $(CLEAR_VARS)
    #LOCAL_MODULE := wpa_supplicant.conf
    #LOCAL_MODULE_TAGS := user
    #LOCAL_MODULE_CLASS := ETC
    #LOCAL_MODULE_PATH := $(local_target_dir)
    #LOCAL_SRC_FILES := $(LOCAL_MODULE)
    #include $(BUILD_PREBUILT)
    ########################
    系統變量解析
    LOCAL_MODULE     - 編譯的目標對象
    LOCAL_SRC_FILES  - 編譯的源文件
    LOCAL_C_INCLUDES - 需要包含的頭文件目錄
    LOCAL_SHARED_LIBRARIES - 鏈接時需要的外部庫
    LOCAL_PRELINK_MODULE   - 是否需要prelink處理
    BUILD_SHARED_LIBRARY   - 指明要編譯成動態庫
    LOCAL_PATH - 編譯時的目錄
    $(call 目錄,目錄….) 目錄引入操作符
    如該目錄下有個文件夾名稱 src,則可以這樣寫 $(call src),那麼就會得到 src 目錄的完整路徑
    include $(CLEAR_VARS) -清除之前的一些系統變量
    CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
    在 build/core/config.mk 定義 CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
    通過include 包含自定義的.mk文件(即是自定義編譯規則)或是引用系統其他的.mk文件(系統定義的編譯規則)。
    LOCAL_SRC_FILES - 編譯的源文件
    可以是.c, .cpp, .java, .S(彙編文件)或是.aidl等格式
    不同的文件用空格隔開。如果編譯目錄子目錄,採用相對路徑,如子目錄/文件名。也可以通過$(call 目錄),指明編譯某目錄
    下所有.c/.cpp/.java/.S/ .aidl文件.追加文件 LOCAL_SRC_FILES += 文件
    LOCAL_C_INCLUDES - 需要包含的頭文件目錄
    可以是系統定義路徑,也可以是相對路徑. 如該編譯目錄下有個include目錄,寫法是include/*.h
    LOCAL_SHARED_LIBRARIES  - 鏈接時需要的外部共享庫
    LOCAL_STATIC_LIBRARIES  - 鏈接時需要的外部外部靜態
    LOCAL_JAVA_LIBRARIES     加入jar包
    LOCAL_MODULE - 編譯的目標對象
    module 是指系統的 native code,通常針對c,c++代碼
    ./system/core/sh/Android.mk:32:LOCAL_MODULE:= sh
    ./system/core/libcutils/Android.mk:71:LOCAL_MODULE := libcutils
    ./system/core/cpio/Android.mk:9:LOCAL_MODULE := mkbootfs
    ./system/core/mkbootimg/Android.mk:8:LOCAL_MODULE := mkbootimg
    ./system/core/toolbox/Android.mk:61:LOCAL_MODULE:= toolbox
    ./system/core/logcat/Android.mk:10:LOCAL_MODULE:= logcat
    ./system/core/adb/Android.mk:65:LOCAL_MODULE := adb
    ./system/core/adb/Android.mk:125:LOCAL_MODULE := adbd
    ./system/core/init/Android.mk:20:LOCAL_MODULE:= init
    ./system/core/vold/Android.mk:24:LOCAL_MODULE:= vold
    ./system/core/mountd/Android.mk:13:LOCAL_MODULE:= mountd
    LOCAL_PACKAGE_NAME 
    Java 應用程序的名字用該變量定義
    ./packages/apps/Music/Android.mk:9:LOCAL_PACKAGE_NAME := Music
    ./packages/apps/Browser/Android.mk:14:LOCAL_PACKAGE_NAME := Browser
    ./packages/apps/Settings/Android.mk:8:LOCAL_PACKAGE_NAME := Settings
    ./packages/apps/Stk/Android.mk:10:LOCAL_PACKAGE_NAME := Stk
    ./packages/apps/Contacts/Android.mk:10:LOCAL_PACKAGE_NAME := Contacts
    ./packages/apps/Mms/Android.mk:8:LOCAL_PACKAGE_NAME := Mms
    ./packages/apps/Camera/Android.mk:8:LOCAL_PACKAGE_NAME := Camera
    ./packages/apps/Phone/Android.mk:11:LOCAL_PACKAGE_NAME := Phone
    ./packages/apps/VoiceDialer/Android.mk:8:LOCAL_PACKAGE_NAME := VoiceDialer
    BUILD_SHARED_LIBRARY - 指明要編譯成動態庫。
    編譯的目標,用include 操作符
    UILD_STATIC_LIBRARY來指明要編譯成靜態庫。
    如果是java文件的話,會用到系統的編譯腳本host_java_library.mk,用BUILD_PACKAGE來指明。三個編譯
    -------------------
    include $(BUILD_STATIC_LIBRARY)
    BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
    -------------------
    include $(BUILD_SHARED_LIBRARY)
    ./build/core/config.mk:50:BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
    -------------------
    include $(BUILD_HOST_SHARED_LIBRARY)
    BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
    -------------------
    include $(BUILD_EXECUTABLE)
    build/core/config.mk:51:BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
    -------------------
    include $(BUILD_HOST_EXECUTABLE)
    ./build/core/config.mk:53:BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
    -------------------
    BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
    -------------------
    BUILD_JAVA_LIBRARY
    ./build/core/config.mk:58:BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
    ------------------
    BUILD_STATIC_JAVA_LIBRARY 編譯靜態JAVA庫
    ./build/core/config.mk:59:BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
    ------------------
    BUILD_HOST_JAVA_LIBRARY   編譯本機用的JAVA庫
    ./build/core/config.mk:60:BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
    ------------------
    BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
    BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
    BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
    BUILD_RAW_STATIC_LIBRARY := $(BUILD_SYSTEM)/raw_static_library.mk
    BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
    BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
    BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_executable.mk
    BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
    BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
    BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
    BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
    BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
    BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
    BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
    BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
    BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
    BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
    BUILD_KEY_CHAR_MAP := $(BUILD_SYSTEM)/key_char_map.mk
    ============
    LOCAL_PRELINK_MODULE
            Prelink利用事先鏈接代替運行時鏈接的方法來加速共享庫的加載,它不僅可以加快起動速度,還可以減少部分內存開銷,
    是各種Linux架構上用於減少程序加載時間、縮短系統啓動時間和加快應用程序啓動的很受歡迎的一個工具。程序運行時的
    動態鏈接尤其是重定位(relocation)的開銷對於大型系統來說是很大的。
            動態鏈接和加載的過程開銷很大,並且在大多數的系統上, 函數庫並不會常常被更動, 每次程序被執行時所進行的鏈接
    動作都是完全相同的,對於嵌入式系統來說尤其如此。因此,這一過程可以改在運行時之前就可以預先處理好,即花一些時間
    利用Prelink工具對動態共享庫和可執行文件進行處理,修改這些二進制文件並加入相應的重定位等信息,節約了本來在程序
    啓動時的比較耗時的查詢函數地址等工作,這樣可以減少程序啓動的時間,同時也減少了內存的耗用。 
            Prelink的這種做法當然也有代價:每次更新動態共享庫時,相關的可執行文件都需要重新執行一遍Prelink才能保
    證有效,因爲新的共享庫中的符號信息、地址等很可能與原來的已經不同了,這就是爲什麼 android framework代碼一改動,
    這時候就會導致相關的應用程序重新被編譯。
    這種代價對於嵌入式系統的開發者來說可能稍微帶來一些複雜度,不過好在對用戶來說幾乎是可以忽略的。
    --------------------
    變量設置爲false那麼將不做prelink操作
    LOCAL_PRELINK_MODULE := false
    默認是需要prlink的,同時需要在 build/core/prelink-linux-arm.map 中加入
    libhellod.so      0x96000000
    這個map文件好像是制定動態庫的地址的,在前面註釋上面有一些地址範圍的信息,注意庫與庫之間的間隔數,
    如果指定不好的話編譯的時候會提示說地址空間衝突的問題。另外,注意排序,這裏要把數大的放到前面去,
    按照大小降序排序。
    解析 LOCAL_PRELINK_MODULE 變量
    build/core/dynamic_binary.mk:94:ifeq ($(LOCAL_PRELINK_MODULE),true)
    ifeq ($(LOCAL_PRELINK_MODULE),true)
    $(prelink_output): $(prelink_input) $(TARGET_PRELINKER_MAP) $(APRIORI)
            $(transform-to-prelinked)
    transform-to-prelinked定義:
    ./build/core/definitions.mk:1002:define transform-to-prelinked
    define transform-to-prelinked
    @mkdir -p $(dir $@)
    @echo "target Prelink: $(PRIVATE_MODULE) ($@)"
    $(hide) $(APRIORI) \
                    --prelinkmap $(TARGET_PRELINKER_MAP) \
                    --locals-only \
                    --quiet \
                    $/build/tools/apriori”
    參考文檔:
    動態庫優化——Prelink(預連接)技術
    http://www.eefocus.com/article/09-04/71629s.html
    ===============
    LOCAL_ARM_MODE := arm
    目前Android大部分都是基於Arm處理器的,Arm指令用兩種模式Thumb(每條指令兩個字節)和arm指令(每條指令四個字節)
    LOCAL_CFLAGS += -O3 -fstrict-aliasing -fprefetch-loop-arrays
    通過設定編譯器操作,優化級別,-O0表示沒有優化,-O1爲缺省值,-O3優化級別最高
    LOCAL_CFLAGS += -W -Wall
    LOCAL_CFLAGS += -fPIC -DPIC
    LOCAL_CFLAGS += -O2 -g -DADB_HOST=1  -Wall -Wno-unused-parameter
    LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY
    LOCAL_CFLAGS += -DUSEOVERLAY2
    根據條件選擇相應的編譯參數
    ifeq ($(TARGET_ARCH),arm)
    LOCAL_CFLAGS += -DANDROID_GADGET=1
    LOCAL_CFLAGS :=   $(PV_CFLAGS)
    endif
    ifeq ($(TARGET_BUILD_TYPE),release)
            LOCAL_CFLAGS += -O2
    endif
    LOCAL_LDLIBS := -lpthread
    LOCAL_LDLIBS += -ldl
    ifdef USE_MARVELL_MVED
    LOCAL_WHOLE_STATIC_LIBRARIES += lib_il_mpeg4aspdecmved_wmmx2lnx lib_il_h264decmved_wmmx2lnx
    LOCAL_SHARED_LIBRARIES += libMrvlMVED
    else
    LOCAL_WHOLE_STATIC_LIBRARIES += lib_il_h264dec_wmmx2lnx lib_il_mpeg4aspdec_wmmx2lnx
    endif
    ====================
    其他一些變量和腳本:
    HOST_JNILIB_SUFFIX
    LOCAL_MODULE_SUFFIX
    LOCAL_MODULE_SUFFIX := $(HOST_JNILIB_SUFFIX)
    HOST_GLOBAL_LDFLAGS
    TARGET_GLOBAL_LDFLAGS
    PRIVATE_LDFLAGS
    LOCAL_LDLIBS
    LOCAL_C_INCLUDES
    LOCAL_STATIC_LIBRARIES
    LOCAL_STATIC_LIBRARIES += codecJPDec_WMMX2LNX miscGen_WMMX2LNX
    LOCAL_SHARED_LIBRARIES 
    LOCAL_SHARED_LIBRARIES += libMrvlIPP
    LOCAL_SHARED_LIBRARIES += $(common_SHARED_LIBRARIES)
    LOCAL_SHARED_LIBRARIES += libMrvlIPP
    LOCAL_SHARED_LIBRARIES += libdl
    ifeq ($(TARGET_PRODUCT),littleton)
    LOCAL_C_INCLUDES += vendor/marvell/littleton/m2d \
    LOCAL_SHARED_LIBRARIES += libOmxCore
    endif
    vendor/marvell/littleton/littleton.mk:27:PRODUCT_NAME := littleton
    vendor/marvell/littleton/littleton.mk:28:PRODUCT_DEVICE := littleton
    vendor/marvell/littleton/AndroidProducts.mk:13:        $(LOCAL_DIR)/littleton.mk
    vendor/sample/products/sample_addon.mk:40:PRODUCT_NAME := sample_addon
    vendor/htc/dream-open/htc_dream.mk:6:PRODUCT_NAME := htc_dream
    ./vendor/htc/dream-open/htc_dream.mk:7:PRODUCT_DEVICE := dream-open
    ./vendor/htc/dream-open/AndroidProducts.mk:3:        $(LOCAL_DIR)/htc_dream.mk
    build/target/product/generic.mk:26:PRODUCT_NAME := generic
    build/target/product/generic_with_google.mk:20:PRODUCT_NAME := generic_with_google
    build/target/product/min_dev.mk:6:PRODUCT_NAME := min_dev
    build/target/product/core.mk:2:PRODUCT_NAME :=
    build/target/product/sim.mk:7:PRODUCT_NAME := sim
    build/target/product/sdk.mk:37:PRODUCT_NAME := sdk
    build/tools/buildinfo.sh:20:echo "ro.product.name=$PRODUCT_NAME"
    lunch sample_addon-eng
    lunch htc_dream-eng
    lunch generic-eng
    lunch 1 
    lunch sim-eng
    TARGET_BUILD_TYPE=release
    lunch 2
    TARGET_BUILD_TYPE=debug
    lunch  generic-user
    .PHONY: systemtarball-nodeps
    systemtarball-nodeps: $(FS_GET_STATS) \
                          $(filter-out systemtarball-nodeps stnod,$(MAKECMDGOALS))
            $(build-systemtarball-target)
    .PHONY: stnod
    stnod: systemtarball-nodeps
    systemimage-nodeps snod
    ./core/main.mk:BUILD_SYSTEM := $(TOPDIR)build/core
    ./core/main.mk:include $(BUILD_SYSTEM)/config.mk
    ./core/main.mk:include $(BUILD_SYSTEM)/cleanbuild.mk
    ./core/main.mk:include $(BUILD_SYSTEM)/version_defaults.mk
    ./core/main.mk:include $(BUILD_SYSTEM)/definitions.mk
    ./core/main.mk:include $(BUILD_SYSTEM)/Makefile
    ./core/static_java_library.mk:include $(BUILD_SYSTEM)/java_library.mk
    ./core/host_java_library.mk:include $(BUILD_SYSTEM)/base_rules.mk
    ./core/executable.mk:include $(BUILD_SYSTEM)/dynamic_binary.mk
    ./core/java_library.mk:include $(BUILD_SYSTEM)/java.mk
    ./core/binary.mk:include $(BUILD_SYSTEM)/base_rules.mk
    ./core/raw_executable.mk:include $(BUILD_SYSTEM)/binary.mk
    ./core/prebuilt.mk:include $(BUILD_SYSTEM)/base_rules.mk
    ./core/host_executable.mk:include $(BUILD_SYSTEM)/binary.mk
    ./core/combo/select.mk:$(combo_target)PRELINKER_MAP := $(BUILD_SYSTEM)/prelink-$(combo_os_arch).map
    ./core/shared_library.mk:include $(BUILD_SYSTEM)/dynamic_binary.mk
    ./core/config.mk:include $(BUILD_SYSTEM)/pathmap.mk
    ./core/config.mk:BUILD_COMBOS:= $(BUILD_SYSTEM)/combo
    ./core/config.mk:CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
    ./core/config.mk:BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
    ./core/config.mk:BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
    ./core/config.mk:BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
    ./core/config.mk:BUILD_RAW_STATIC_LIBRARY := $(BUILD_SYSTEM)/raw_static_library.mk
    ./core/config.mk:BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
    ./core/config.mk:BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
    ./core/config.mk:BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_executable.mk
    ./core/config.mk:BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
    ./core/config.mk:BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
    ./core/config.mk:BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
    ./core/config.mk:BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
    ./core/config.mk:BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
    ./core/config.mk:BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
    ./core/config.mk:BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
    ./core/config.mk:BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
    ./core/config.mk:BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
    ./core/config.mk:BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
    ./core/config.mk:BUILD_KEY_CHAR_MAP := $(BUILD_SYSTEM)/key_char_map.mk
    ./core/config.mk:HOST_JDK_TOOLS_JAR:= $(shell $(BUILD_SYSTEM)/find-jdk-tools-jar.sh)
    ./core/version_defaults.mk:INTERNAL_BUILD_ID_MAKEFILE := $(wildcard $(BUILD_SYSTEM)/build_id.mk)
    ./core/config.mk:include $(BUILD_SYSTEM)/envsetup.mk
    ./core/config.mk:include $(BUILD_SYSTEM)/combo/select.mk
    ./core/config.mk:include $(BUILD_SYSTEM)/combo/select.mk
    ./core/config.mk:include $(BUILD_SYSTEM)/combo/javac.mk
    ./core/product_config.mk:include $(BUILD_SYSTEM)/node_fns.mk
    ./core/product_config.mk:include $(BUILD_SYSTEM)/product.mk
    ./core/product_config.mk:include $(BUILD_SYSTEM)/device.mk
    ./core/dynamic_binary.mk:include $(BUILD_SYSTEM)/binary.mk
    ./core/host_static_library.mk:include $(BUILD_SYSTEM)/binary.mk
    ./core/java.mk:include $(BUILD_SYSTEM)/base_rules.mk
    ./core/host_shared_library.mk:include $(BUILD_SYSTEM)/binary.mk
    ./core/key_char_map.mk:include $(BUILD_SYSTEM)/base_rules.mk
    ./core/package.mk:include $(BUILD_SYSTEM)/java.mk
    ./core/static_library.mk:include $(BUILD_SYSTEM)/binary.mk
    ./core/definitions.mk:include $(BUILD_SYSTEM)/distdir.mk
    ./core/envsetup.mk:include $(BUILD_SYSTEM)/product_config.mk
    ./tools/apicheck/Android.mk:include $(BUILD_SYSTEM)/base_rules.mk
    ./tools/dexpreopt/Android.mk:include $(BUILD_SYSTEM)/host_prebuilt.mk
    COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused
    COMMON_DEBUG_CFLAGS:=
    COMMON_RELEASE_CFLAGS:= -DNDEBUG -UDEBUG
    COMMON_PACKAGE_SUFFIX := .zip
    COMMON_JAVA_PACKAGE_SUFFIX := .jar
    COMMON_ANDROID_PACKAGE_SUFFIX := .apk
    ACP := $(HOST_OUT_EXECUTABLES)/acp$(HOST_EXECUTABLE_SUFFIX)
    AIDL := $(HOST_OUT_EXECUTABLES)/aidl$(HOST_EXECUTABLE_SUFFIX)
    MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX)
    MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX)
    MKYAFFS2 := $(HOST_OUT_EXECUTABLES)/mkyaffs2image$(HOST_EXECUTABLE_SUFFIX)
    APICHECK := $(HOST_OUT_EXECUTABLES)/apicheck$(HOST_EXECUTABLE_SUFFIX)
    FS_GET_STATS := $(HOST_OUT_EXECUTABLES)/fs_get_stats$(HOST_EXECUTABLE_SUFFIX)
    MKEXT2IMG := $(HOST_OUT_EXECUTABLES)/genext2fs$(HOST_EXECUTABLE_SUFFIX)
    MKEXT2BOOTIMG := external/genext2fs/mkbootimg_ext2.sh
    MKTARBALL := build/tools/mktarball.sh
    DX := $(HOST_OUT_EXECUTABLES)/dx
    LOCALIZE := $(HOST_OUT_EXECUTABLES)/localize$(HOST_EXECUTABLE_SUFFIX)
    HOST_GLOBAL_LDFLAGS
    TARGET_GLOBAL_LDFLAGS
    PRIVATE_LDFLAGS
    build/core/combo/linux-arm.mk:16:$(combo_target)NO_UNDEFINED_LDFLAGS := -Wl,--no-undefined
    save_CFLAGS="$CFLAGS -g  -mabi=aapcs-linux"
    LDFLAGS='$LDFLAGS  -lX11 -lxml2 -lXdmcp -lXau -lexpat -lXrender -lXft  -lfontconfig -lfreetype -lz'  
    --without-libtiff "   # --with-gdktarget=directfb"
    LDFLAGS=" -Wl,-rpath-link=$LD_LIBRARY_PATH  -L$PREFIX/lib  ${env_LDFLAGS} ${save_LDFLAGS}"
    ./vendor/marvell/external/alsa/alsa-lib/src/Mdroid.mk:43:LOCAL_CFLAGS += -mabi=aapcs-linux
    ./vendor/marvell/external/alsa/alsa-tools/Mdroid.mk:8:LOCAL_CFLAGS += -mabi=aapcs-linux
    ./vendor/marvell/littleton/libaudio/Mdroid.mk:22:LOCAL_CPPFLAGS += -mabi=aapcs-linux
    ./external/wpa_supplicant/Android.mk:35:L_CFLAGS += -mabi=aapcs-linux
    ./system/wlan/ti/sta_dk_4_0_4_32/CUDK/tiwlan_loader/Android.mk:88:LOCAL_CFLAGS = -Wall -Wstrict-prototypes 
    $(CLI_DEBUGFLAGS) -D__LINUX__ $(DK_DEFINES) -mabi=aapcs-linux
    ./kernel/arch/arm/Makefile
    ifeq ($(CONFIG_AEABI),y)
    CFLAGS_ABI        :=-mabi=aapcs-linux -mno-thumb-interwork
    else
    CFLAGS_ABI        :=$(call cc-option,-mapcs-32,-mabi=apcs-gnu) $(call cc-option,-mno-thumb-interwork,)
    endif
    # Need -Uarm for gcc /dev/mtdblock0
    2.4.3 通過工具釋放yaffs2 文件系統
    yaffs2 image逆向工具 
    http://blog.csdn.net/absurd/archive/2008/11/05/3223825.aspx
    獲取源代碼:
    http://www.limodev.cn/bbs/download/file.php?id=1
    2.5 虛擬文件系統(sysfs,proc,tsmpfs等)
    2.5.1 虛擬文件系統概述
    2.5.2 proc 文件系統
    2.5.3 sysfs文件系統
    2.5.4 tmpfs文件系統
    2.5.5 usbdevfs文件系統
    2.5.6 devpts文件系統
    2.5.1 虛擬文件系統概述
    虛擬內核文件系統(Virtual Kernel File Systems),是指那些是由內核產生但並不存在於硬盤上(存在於內存中)的文件系統,
    他們被用來與內核進行通信前面介紹的ext2,ext3,jffs2,yaffs2等目錄和文件,都是真真正正、實實在在的存儲在具體的外部存
    儲設備上的,它們可能是在本機的硬盤、閃存、光盤中,可能保存在不只一個磁盤分區中,也可能保存在網絡中其它主機的存儲設備中的。
    虛擬文件系統,雖然它們出現在根文件系統中,但它裏面的內容卻無法在任何外部存儲設備中找到,因爲它們都在內存中。
    ==========
    android 網絡掛載:
    rootfs    /         rootfs rw 0 0
    /dev/root /         nfs rw,vers=2,rsize=1024,wsize=1024,...
    tmpfs     /dev      tmpfs rw,mode=755 0 0
    devpts    /dev/pts  devpts rw,mode=600 0 0
    proc      /proc     proc rw 0 0
    sysfs     /sys      sysfs rw 0 0
    tmpfs                   /sqlite_stmt_journals   tmpfs rw,size=4096k 0 0
    /dev/block/mmcblk0p1    /sdcard                 vfat rw,...
    ===========
    android 本機掛載(使用flash中的文件系統)
    rootfs / rootfs ro 0 0
    tmpfs /dev tmpfs rw,mode=755 0 0
    devpts /dev/pts devpts rw,mode=600 0 0
    proc /proc proc rw 0 0
    sysfs /sys sysfs rw 0 0
    tmpfs /sqlite_stmt_journals tmpfs rw,size=4096k 0 0
    /dev/block/mtdblock2 /system yaffs2 ro 0 0
    /dev/block/mtdblock3 /data yaffs2 rw,nosuid,nodev 0 0
    /dev/block/mmcblk0p1 /sdcard vfat rw
    =============
    ubuntu 系統:
    /dev/sda8 on /             type ext3  (rw,relatime,errors=remount-ro)
    tmpfs     on /lib/init/rw  type tmpfs (rw,nosuid,mode=0755)
    /proc     on /proc         type proc  (rw,noexec,nosuid,nodev)
    sysfs     on /sys          type sysfs (rw,noexec,nosuid,nodev)
    varrun    on /var/run      type tmpfs (rw,nosuid,mode=0755)
    tmpfs     on /dev/shm      type tmpfs (rw,nosuid,nodev)
    devpts    on /dev/pts      type devpts (rw,noexec,nosuid,gid=5,mode=620)
    /dev/sda7  on /boot type ext3 (rw,relatime)
    /dev/sda11 on /home type ext3 (rw,relatime)
    /dev/sdb5  on /opt type ext3 (rw,relatime)
    /dev/sda9  on /usr/local type ext3 (rw,relatime)
    /dev/sda1  on /windows/c type vfat (rw,utf8,umask=007,gid=1000)
    /dev/sda5  on /windows/d type vfat (rw,utf8,umask=007,gid=1000)
    /dev/sda6  on /windows/e type vfat (rw,utf8,umask=007,gid=1000)
    ===============
    2.5.2 proc 文件系統
    proc是一個重要虛擬文件系統,通過它裏面的一些文件,可以獲取系統狀態信息並修改某些系統的配置信息。proc文件系統本身不佔用
    磁盤空間,它僅存在於內存之中,爲操作系統本身和應用程序之間的通信提供了一個安全的接口。當我們在內核中添加了新功能或設備驅
    動時,經常需要得到一些系統狀態的信息,一般這樣的功能可能需要經過一些象ioctl()這樣的系統調用來完成。系統調用接口對於一些
    功能性的信息可能是適合的,因爲應用程序必須將這些信息讀出後再做一定的處理。但對於一些實時性的系統信息,例如內存的使用狀況,
    或者是驅動設備的統計資料等,我們更需要一個比較簡單易用的接口來取得它們。proc文件系統就是這樣的一個接口,我們可以簡單的用
    cat、strings程序來查看這些信息。例如,執行下面的命令:
    cat /proc/filesystems    //操作系統支持的文件系統類型
    cat /proc/meminfo        //內存的實時信息,內存大小等
    cat /proc/partitions     //存儲器分區信息
    cat /proc/cpuinfo        //查看cpu信息
    同樣的,free、df、top、ps等程序的功能實現,強烈依賴於proc文件系統,爲了使用那些程序,一定要使內核支持proc文件系統,
    並將其掛接到根文件系統的/proc目錄下。
    其他使用 /proc 文件系統的例子:
    processor        : 0
    vendor_id        : AuthenticAMD
    processor        : 1
    vendor_id        : AuthenticAMD
    model name        : AMD Athlon(tm) 64 X2 Dual Core CPU 5000+
    1.vmware 虛擬機無法正常啓動
    在Linux下,單個進程的最大內存使用量受/proc/sys/kernel/shmmax中設置的數字限制(單位爲字節),
    例如 ubuntu 8.10 的shmmax默認值爲33554432字節(33554432bytes/1024/1024=32MB)。
    2.scratchbox 開發工具不能登錄
    /scratchbox/login 
    Inconsistency detected by ld.so: rtld.c: 1192: dl_main: Assertion `(void *) ph->p_vaddr == 
    _rtld_local._dl_sysinfo_dso' failed!
    NOTE: on Ubuntu installation, you have to disable VDSO to make Scratchbox work fine, 
    or you'll get errors like this:
    在 ubuntu 系統中,我們必須關閉 VDSO 標記,以便scratchbox能正常工作
    echo 0 | sudo tee /proc/sys/vm/vdso_enabled
    echo 4096 | sudo tee /proc/sys/vm/mmap_min_addr
    vm.vdso_enabled = 0
    vm.mmap_min_addr = 4096
    修改 /proc 文件系統值的方法
    1.直接修改
    echo "2147483648" | sudo tee /proc/sys/kernel/shmmax
    echo 0 | sudo tee /proc/sys/vm/vdso_enabled
    echo 4096 | sudo tee /proc/sys/vm/mmap_min_addr
    2.將以下命令放入 /etc/rc.local 啓動文件中:
    echo "2147483648" > /proc/sys/kernel/shmmax
    echo 0            > /proc/sys/vm/vdso_enabled
    echo 4096         > /proc/sys/vm/mmap_min_addr
    3.使用 sysctl 命令來更改 SHMMAX 的值:
    sysctl -w kernel.shmmax=2147483648
    4.內核參數插入到 /etc/sysctl.conf 啓動文件中,使這種更改永久有效
    echo "kernel.shmmax=2147483648" >> /etc/sysctl.conf 
    sudo sysctl –p
    ./system/core/logcat/logcat.cpp:403:   fd = open("/proc/cmdline", O_RDONLY);
    ./system/core/init/init.c:553:    char cmdline[1024];
    ./system/core/init/init.c:557:    fd = open("/proc/cmdline", O_RDONLY);
    ./system/core/init/init.c:580:    chmod("/proc/cmdline", 0440);
    ./system/core/init/bootchart.c:139:   proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
    ./system/core/init/bootchart.c:319:   proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
    ./system/core/init/bootchart.c:320:   s = strstr(cmdline, KERNEL_OPTION);
    ./system/core/rootdir/init.rc:162:    chown root radio /proc/cmdline
    2.5.3 sysfs文件系統
    與proc文件系統類似,sysfs文件系統也是一個不佔有任何磁盤空間的虛擬文件系
    統。它通常被掛接在/sys目錄下。sysfs文件系統是Linux2.6內核引入的,它把連接在系
    統上的設備和總線組織成爲一個分級的文件,使得它們可以在用戶空間存取。其實
    sysfs是從proc和devfs中劃分出來的。
    一、devfs
       linux下有專門的文件系統用來對設備進行管理,devfs和sysfs就是其中兩種。
    在2.6內核以前一直使用的是devfs,devfs掛載於/dev目錄下,提供了一種類似於文件的方法來管理位於/dev目錄下的所有設備,我們知道
    /dev目錄下的每一個文件都對應的是一個設備,至於當前該設備存在與否先且不論,而且這些特殊文件是位於根文件系統上的,在製作文件
    系統的時候我們就已經建立了這些設備文件,因此通過操作這些特殊文件,可以實現與內核進行交互。但是devfs文件系統有一些缺點,例如:
    不確定的設備映射,有時一個設備映射的設備文件可能不同,例如我的U盤可能對應sda有可能對應sdb;沒有足夠的主/輔設備號,當設備過多
    的時候,顯然這會成爲一個問題;/dev目錄下文件太多而且不能表示當前系統上的實際設備;命名不夠靈活,不能任意指定等等。
    二、sysfs
          正因爲上述這些問題的存在,在linux2.6內核以後,引入了一個新的文件系統sysfs,它掛載於/sys目錄下,跟devfs一樣它也是一個
    虛擬文件系統,也是用來對系統的設備進行管理的,它把實際連接到系統上的設備和總線組織成一個分級的文件,用戶空間的程序同樣可以利用
    這些信息以實現和內核的交互,該文件系統是當前系統上實際設備樹的一個直觀反應,它是通過kobject子系統來建立這個信息的,當一個
    kobject被創建的時候,對應的文件和目錄也就被創建了,位於/sys下的相關目錄下,既然每個設備在sysfs中都有唯一對應的目錄,那麼也
    就可以被用戶空間讀寫了。用戶空間的工具udev 就是利用了sysfs提供的信息來實現所有devfs的功能的,但不同的是udev運行在用戶空間中,
    而devfs卻運行在內核空間,而且udev不存在 devfs那些先天的缺陷。很顯然,sysfs將是未來發展的方向。
    2.5.4 tmpfs文件系統
    tmpfs 是Linux特有的文件系統,唯一的標準掛接點是/dev/shm。當然,用戶可以將其掛接在其他地方。tmpfs有些像虛擬磁盤(ramdisk),
    但不是一回事。說其像虛擬磁盤,是因爲它可以使用你的RAM,但它也可以使用你的交換分區。傳統的虛擬磁盤是一個塊設備,而且需要一個mkfs
    之類的命令格式化它才能使用。tmpfs是一個獨立的文件系統,不是塊設備,只要掛接,立即就可以使用。tmpfs的大下是不確定的,它最初只有
    很小的空間,但隨着文件的複製和創建,它的大小就會不斷變化,換句話說,它會根據你的實際需要而改變大小;tmpfs的速度非常驚人,畢竟它
    是駐留在RAM中的,即使用了交換分區,性能仍然非常卓越;由於tmpfs是駐留在RAM的,因此它的內容是不持久的,斷電後,tmpfs的內容就消
    失了,這也是被稱作tmpfs的根本原因。
    tmpfs 是ramfs的衍生物,用來限制緩存大小、向swap空間寫入數據。它是用來保存VM所有文件的文件系統。
    tmpfs中緩存的內容全部是臨時的。一旦卸載,所有的內容都會遺失。它把所有的緩存置於內核,它的規模隨着
    文件的規模同步變化。但是它規模有大小限制,可以修改。它可以把當前不再需要的頁寫入到 swap空間。
    tmpfs 和 ramfs 本身就是一個文件系統, 用的時候只需要直接掛載就可以. tmpfs可以使用ram, 也可以使用swap
    共享內存的時候會使用tmpfs
    系統默認共享內存是內存的一半大小! /dev/shm是掛載點!
    通過 df -h 可以看出,默認狀況下它爲內存大小的一半:
    文件系統        容量      已用      可用     已用%    掛載點
    tmpfs    1013M   12K  1013M   1%    /dev/shm
    mount | grep tmpfs 顯示當前系統中的 tmpfs:
    tmpfs   on /lib/init/rw   type   tmpfs (rw,nosuid,mode=0755)
    varrun  on /var/run       type   tmpfs (rw,nosuid,mode=0755)
    varlock on /var/lock      type   tmpfs (rw,noexec,nosuid,nodev,mode=1777)
    udev    on /dev           type   tmpfs (rw,mode=0755)
    tmpfs   on /dev/shm       type   tmpfs (rw,nosuid,nodev)
    lrm     on /lib/modules/2.6.27-4-generic/volatile type tmpfs (rw,mode=755)
    2.5.6 usbdevfs文件系統
    顧名思義,usbdevfs就是USB設備文件系統,它是一個動態生成的文件系統,有
    些類似於proc文件系統。它的標準掛接點是/proc/bus/usb,當然,也可以掛接到其他
    地方。它主要用於:用戶級驅動、即插即用、提供USB設備信息、應用程序輪詢
    USB設備的變化等。
    2.5.7 devpts文件系統
    devpts文件系統爲僞終端提供了一個標準接口,它的標準掛接點是/dev/pts。只要
    pty的主複合設備/dev/ptmx被打開,就會在/dev/pts下動態的創建一個新的pty設備文
    件。掛接時,UID、GID及其工作模式會指定給devpts文件系統的所有pty文件。這可
    以保證僞終端的安全性。
    討論devpts文件系統的詳細內容,已經超過本文範圍,還請讀者參考其他專
    著。
    2.6 一些必要重要的系統文件 ( /etc/fstab ,inittab,init.rc等)
    2.6.1 /etc/inittab
    2.6.2 /etc/init.d/rcS
    2.6.3 /etc/fstab 文件
    ================
    2.6.1 /etc/inittab
    initab  被 init 使用
    2.6.1.1 老平臺 inittab文件內容
    2.6.1.1 gpephone 官方的inittab 文件(與redhat,federo差不多)
    2.6.1.1 ubuntu中沒有inittab文件
    =================
    2.6.1.1 老平臺 inittab文件內容
    -----------------------------------------
    ::sysinit:/etc/init.d/rcS
    ::respawn:-/bin/sh
    ::restart:/sbin/init
    ::ctrlaltdel:/sbin/reboot
    ::shutdown:/bin/umount -a -r
    ::shutdown:/sbin/swapoff -a
    -----------------------------------------
    2.6.1.2 gpephone 官方的inittab 文件(與redhat,federo差不多
    -----------------------------------------
    # /etc/inittab: init(8) configuration.
    # $Id: inittab,v 1.91 2002/01/25 13:35:21 miquels Exp $
    # The default runlevel.
    id:5:initdefault:
    # Boot-time system configuration/initialization script.
    # This is run first except when booting in emergency (-b) mode.
    si::sysinit:/etc/init.d/rcS
    # What to do in single-user mode.
    ~~:S:wait:/sbin/sulogin
    # /etc/init.d executes the S and K scripts upon change
    # of runlevel.
    #
    # Runlevel 0 is halt.
    # Runlevel 1 is single-user.
    # Runlevels 2-5 are multi-user.
    # Runlevel 6 is reboot.
    l0:0:wait:/etc/init.d/rc 0
    l1:1:wait:/etc/init.d/rc 1
    l2:2:wait:/etc/init.d/rc 2
    l3:3:wait:/etc/init.d/rc 3
    l4:4:wait:/etc/init.d/rc 4
    l5:5:wait:/etc/init.d/rc 5
    l6:6:wait:/etc/init.d/rc 6
    # Normally not reached, but fallthrough in case of emergency.
    z6:6:respawn:/sbin/sulogin
    1:2345:respawn:/sbin/getty 38400 tty1
    -------------------------------------------
    2.6.1.3 ubuntu中沒有inittab文件
    在unbutu系統中我們沒看到此文件,是因爲ubuntu用的是 upstart ,lfs中使用的是 sysvinit ,嵌入式系統中
    一般使用的是 busybox 中的 init ,android 系統使用的是 system/core/init 
    init:
    main()
      init_main()
        read_inittab();
    gdm運行後
    /etc/rc5.d/S30gdm -> ../init.d/gdm
    /etc/init.d/gdm:19:DAEMON=/usr/sbin/gdm
    /etc/init.d/gdm:24:        SSD_ARG="--startas $DAEMON"
    /etc/init.d/gdm:27:        SSD_ARG="--exec $DAEMON"
    啓動gdm:
    log_begin_msg "Starting GNOME Display Manager..."
    start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --name gdm $SSD_ARG -- $CONFIG_FILE >/dev/null
    ================
    2.6.2 /etc/init.d/rcS
    -------------------
    #!/bin/sh
    掛在 /etc/fstab 中的文件系統
    /bin/mount -a
    . /etc/default/rcS
    #環境變量
    . /etc/profile
    #屏幕叫準備
    . /etc/X11/run-calibrate
    #啓動X
    . /etc/X11/Xserver
    . /etc/scripts/testd-bus.sh
    #啓動dbus消息總線
    #啓動gpephone
    -------------------
    ubuntu 系統
    ---------------
    exec /etc/init.d/rc S
    ---------------
    會依此執行 /etc/rcS.d/ 下以
    S01mountkernfs.sh 
    S02hostname.sh
    S10udev 
    S11mountdevsubfs.sh
    S20checkroot.sh
    S22mtab.sh
    S30checkfs.sh
    S35mountall.sh
    S40networking
    S43portmap
    S55bootmisc.sh
    ./rc3.d/S30gdm
    ./rc2.d/S30gdm
    ./rc4.d/S30gdm
    ./rc5.d/S30gdm
    /etc/rcS.d/S35mountall.sh -> ../init.d/mountall.sh
    mount -a -t nonfs,nfs4,smbfs,cifs,ncp,ncpfs,coda,ocfs2,gfs,gfs2 -O no_netdev
    mount命令的一些解析:
    mount -a [-t|-O] ...     : mount all stuff from /etc/fstab
    mount -t type dev dir    : ordinary mount command
    ================
    2.6.3 /etc/fstab 文件
    Util-linux 軟件包包含許多工具。其中比較重要的是加載、卸載、格式化、分區和管理硬盤驅動器,打開 tty 端口和得到內核消息
    arch         報告機器的體系結構
    blockdev         在命令行中調用塊設備的ioctl
    cal         顯示一個簡單的日曆。
    cfdisk         處理指定設備的分區表
    column         把輸出格式化爲幾列
    ctrlaltdel         設置CTRL+ALT+DEL組合鍵的功能爲硬重啓或軟重啓
    dmesg         顯示內核的啓動信息
    fdisk         磁盤分區管理程序
    fsck.cramfs         對Cramfs文件系統的一致性進行檢查
    getopt         在給出的命令行進行選項和參數解析
    hexdump         用用戶指定的方式(包括ASCII, 十進制, 十六進制, 八進制)顯示一個文件或者標準輸入的數據
    hwclock         查詢和設置硬件時鐘(也被稱爲RTC或BIOS時鐘)。
    ipcrm         刪除給定的進程間通信(IPC)資源
    mkfs         在一個設備(通常是一個硬盤分區)設備上建立文件系統
    mkfs.cramfs         創建cramfs文件系統
    mkswap         初始化指定設備或文件,以用做交換分區
    more         分屏顯示文件,但沒有less好用
    mount         把一個文件系統從一個設備掛載到一個目錄
    ramsize         顯示或者改變 RAM disk 的大小
    raw 將一個原始的Linux字符設備綁定到一個塊設備
    rdev  查詢和設置內核的根設備和其他信息
    readprofile         顯示內核側寫文件/proc/profile的信息
    rename         對文件進行重命名
    renice         修改正在運行進程的優先級
    sfdisk         磁盤分區表管理工具
    umount         卸載一個被掛載的文件系統
    mount掛載與/etc/fstab
    mount 源目錄 目的目錄
    mount -a 自動掛載/etc/fstab中的文件系統     
    根目錄 / 是必須掛載的﹐而且一定要先於其它 mount point 被掛載進來。     其它 mount point 必須爲已建立的目錄﹐可任意指定﹐
    但一定要遵守必須的系統目錄架構原則     所有 mount point 在同一時間之內﹐只能掛載一次。     所有 partition 在同一時間之內﹐
    只能掛載一次。     如若進行卸載﹐您必須先將工作目錄移到 mount point(及其子目錄) 之外。 
    /etc/fstab 
    第一列:label 
    第二列:掛載點
    第三列:分區的文件系統
    第四列:文件系統掛載選項,看附件啦
    第五列:是否被dump作用。0代表不要做dump 備份,1代表要每天進行dump的動作。 2 也代表其它不定日期的dump備份動作,通常這個數值不是0就是1啦!
    第六列:是否以fsck檢查分區(開機時候檢查分區)0爲不檢查,1爲開機的時候檢查,2爲在稍後的時間檢查
    /dev/sda8 on / type ext3 (rw,relatime,errors=remount-ro)
    /proc on /proc type proc (rw,noexec,nosuid,nodev)
    sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
    tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
    devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620)
    /dev/sda7 on /boot type ext3 (rw,relatime)
    /dev/sda11 on /home type ext3 (rw,relatime)
    /dev/sdb5 on  /opt type ext3 (rw,relatime)
    /dev/sda9 on  /usr/local type ext3 (rw,relatime)
    /dev/sda1 on  /windows/c type vfat (rw,utf8,umask=007,gid=1000)
    /dev/sda5 on  /windows/d type vfat (rw,utf8,umask=007,gid=1000)
    /dev/sda6 on  /windows/e type vfat (rw,utf8,umask=007,gid=1000)
    可以在/etc/fstab 中進行指定
    proc        /proc         proc        defaults    0        0
    none        /tmp         ramfs        defaults        0        0
    sysfs        /sys         sysfs        defaults        0        0
    none    /dev/pts devpts defaults        0        0
    ./util-linux-2.12r/mount/mount.c
    main()
    result = do_mount_all (types, options, test_opts);
    mount --help 可以知道 mount -a 是mount所有/etc/fstab 
    mount -a [-t|-O] ...     : mount all stuff from /etc/fstab
    ======================
    2.7 製作文件系統
    2.7.1 原始方式
    2.7.2 通過scratchbox等工具
    2.7.3 通過 android 源碼集成開發環境
    2.7.1 原始方式
    創建基本文件系統標準目錄(根據不同的linux系統,ubuntu跟android目錄結構就完全不同)
    lfs中的標準目錄:
    創建修改必要的配置文件
    /scratchbox/source2/source/busybox/busybox-1.1.2/examples/bootfloppy/etc/
    vim  ${CLFS_ROOTFS_DIR}/etc/profile
    vim  ${CLFS_ROOTFS_DIR}/etc/inittab
    vim  ${CLFS_ROOTFS_DIR}/etc/fstab
    vim  ${CLFS_ROOTFS_DIR}/etc/init.d/rcS
    創建帳號以及密碼文件
    sudo vim ${CLFS_ROOTFS_DIR}/passwd
    拷貝必須的動態庫文件
    cd  ${CLFS_ROOTFS_DIR}/lib
    cp -d  $COMPILER_LIB/ld* ./
    cp      $COMPILER_LIB/libc-2.3.5.so ./
    cp -d  $COMPILER_LIB/libc.so.6 ./
    cp      $COMPILER_LIB/libm-* ./
    cp -d  $COMPILER_LIB/libm.s* ./
    cp      $COMPILER_LIB/libcrypt-* ./
    cp -d  $COMPILER_LIB/libcrypt.s* ./
    拷貝可選的動態庫文件
    如果需要域名解析:
    1)增加/etc/resolv.conf
    [root@lqm /etc]#cat resolv.conf
    nameserver 192.168.x.x  //加入域名解析器
    2)增加相應動態庫的支持
    增加如下:
    libnss_files
    libnss_dns
    libresolv.so
    find find . -name "libnss*"  $COMPILER_LIB/
    ./libnss_files.so.2
    ./libnss_files.so
    ./libnss_dns-2.3.2.so
    ./libnss_dns.so
    ./libnss_files-2.3.2.so
    ./libnss_dns.so.2
    find . -name "libresolv*"  /scratchbox/compilers/arm-linux-gcc-3.4.4-glibc-2.3.5/arm-unknown-linux-gnu/lib/
    ./libresolv.so
    ./libresolv.so.2
    ./libresolv-2.3.2.so
    2.7.2 通過scratchbox等工具
    ===================
    2.7.3 通過 android 源碼集成開發環境
    環境搭建問題:
            1.爲什麼拷貝  cupcake 編譯結果  out/target/product/littleton/root/  到內核頂層目錄?
            2.cupcake-jianping/make_image15.sh中的choosecombo是什麼作用?
            3.make_image15.sh 與 make_env15.sh只差一句make -j2?
            4.補充shell腳本知識。
    =====================
    2.7.4 配置android網絡文件系統
    下面是曾經用過的幾種開發板的命令行參數:
    S3C2410 啓動參數:
    noinitrd root=/dev/nfs  nfsroot=192.168.2.56:/nfsroot/rootfs  
    ip=192.168.2.188:192.168.2.56:192.168.2.56:255.255.255.0::eth0:on console=ttySAC0
    S3C2440 啓動參數:
    setenv bootargs console=ttySAC0 root=/dev/nfs nfsroot=192.168.2.56:/nfsroot/rootfs 
    ip=192.168.2.175:192.168.2.56:192.168.2.201:255.255.255.0::eth0:on mem=64M init=/init         
    marvell 310 啓動參數:
    boot root=/dev/nfs nfsroot=192.168.2.56:/nfsroot/rootfs,rsize=1024,wsize=1024 
    ip=192.168.2.176:192.168.2.201:192.168.2.201:255.255.255.0::eth0:-On  
    console=ttyS2,115200 mem=64M init=/init
    當前android內核的.config文件中的命令行參數:
    CONFIG_CMDLINE="root=/dev/nfs nfsroot=192.168.1.100:/nfsroot/rootfs,rsize=1024,wsize=1024 
    ip=192.168.1.101:192.168.1.100:192.168.1.100:255.255.255.0::usb0:on 
    console=ttyS1,115200 mem=128M init=/init android uart_dma=1"
    `root=' 參數
    此參數告訴內核啓動時以那個設備作爲根文件系統使用。我的pc根文件系統:
    /dev/sda8              9614116   6522156   2603588  72% /
    ubuntu 的/boot/grub/menu.lst參數:
    kernel                /vmlinuz-2.6.27-4-generic root=UUID=2ffa7dc6-2dc5-4b66-8661-1226c086951a 
                    ro locale=zh_CN quiet splash 
    initrd                /initrd.img-2.6.27-4-generic
    其中 root可以設置爲:root=/dev/sda8
    /dev/nfs, 這並非真的是個設備, 而是一個告訴核心經由網絡取得根文件系統
    lfs的/boot/grub/menu.lst參數:
    title LFS 6.4
    root (hd1,1)
    kernel /boot/lfskernel-2.6.27.4 root=/dev/sdb1
    `nfsroot=' 參數
    這個參數告訴內核到哪臺pc的哪個目錄讀取根文件系統。此參數的格式如下:
    nfsroot=[:][,]
    --pc機的ip地址,如果此字段沒給值,那麼將使用由 nfsaddrs 變量(見下面)所決定的值。
    -- pc服務端上要作爲根掛入的目錄域名(/nfsroot/rootfs)
    -- 標準的網絡文件系統選項。所有選項都以逗號分開。如果沒有給定此選項字段則使用下列的缺省值:
            port            = as given by server portmap daemon
            rsize           = 1024
            wsize           = 1024
            timeo           = 7
            retrans         = 3
            acregmin        = 3
            acregmax        = 60
            acdirmin        = 30
            acdirmax        = 60
            flags           = hard, nointr, noposix, cto, ac
    `init=' 參數
    內核啓動時缺省執行 `init' 程序,內核將會到/sbin/, /bin/ 等目錄下查找默認的init,如果沒有找到那麼就報告出錯。
    而最後它會去試 /bin/sh (可能在 /etc/rc )。如果說,例如,如果你的 init 程序壞掉了,只要使用 init=/bin/sh
    這個啓動參數就能讓你在啓動時直接跳到解譯環境(shell),使你能夠換掉壞掉的程序。
    `ip=' 參數
    nfsaddrs=::::::
    ip=192.168.1.101:192.168.1.100:192.168.1.100:255.255.255.0::usb0:on
    ip=192.168.2.175:192.168.2.56:192.168.2.201:255.255.255.0::eth0:on
    -- 板子的ip 使用何種協議端視配置核心時打開的選項以及  參數而定。如果設定此參數,就不會使用反向地址解析協議或啓動協議。
    -- 網絡文件系統服務端之互聯網地址。
    -- 網關(gateway),
    -- 本地網絡界面的網絡掩碼。如果爲空白,則網絡掩碼由客戶端的互聯網地址導出,除非由啓動協議接收到值。
    -- 客戶端的域名。如果空白,則使用客戶端互聯網地址之 ASCII-標記法,或由啓動協議接收的值。
    -- 要使用的網絡設備域名。
    -- 用以作爲自動配置的方法。
    參考文檔:
    ramfs, rootfs, initrd and initramfs
    http://blog.chinaunix.net/u2/89923/showart_1890405.html
    嵌入式系統文件系統比較
    http://blog.sina.com.cn/s/blog_53ad41a50100eptc.html
    LINUX系統性能調諧
    http://www.host01.com/article/server/00070002/0621409052193755_2.htm
    怎樣限制或者修改/dev/shm的大小
    http://www.linuxfly.cn/html/65/t-665.html
    ====================================================================
    ====================================================================
    3. 製作交叉工具鏈
    3.1 什麼是工具鏈
    3.2 獲取交叉工具鏈的幾種途徑
    3.3 android工具鏈與gnu工具鏈的比較
           每一個軟件,在編譯的過程中,都要經過一系列的處理,才能從源代碼變成可執行的目標代碼。這一系列處理包括:預編譯,高級語言編譯,
    彙編,連接及重定位。這一套流程裏面用到的每個工具和相關的庫組成的集合,就稱爲工具鏈(tool chain)。以GNU的開發工具GCC爲例,
    它就包括了預編譯器cpp,c編譯器gcc,彙編器as,和連接器ld等。在GNU自己對工具鏈定義中,還加進了一套額外的用於處理二進制包的
    工具包binutils,整個工具鏈應該是GCC+binutils+Glibc, binutils其實與Glibc關係不是很大,它可以被獨立安裝的,所以GNU工具
    鏈也可以狹義地被理解爲GCC+Glibc。
    要構建出一個交叉工具鏈,需要解決三個問題。一是這個工具鏈必須是可以運行在原工作站平臺上的。二是我們需要更換一個與目標平臺對應的
    彙編器,使得工具鏈能產生對應的目標代碼,三是要更換一套與目標平臺對應的二進制庫,使得工具鏈在連接時能找到正確的二進制庫。
    3.2 獲取交叉工具鏈的幾種途徑
    3.2.1 利用源代碼製作交叉工具鏈
    網上直接下載工具鏈或者從方案商處獲取(如:marvell)
    下載地址:
    http://www.angstrom-distribution.org/unstable/
    3.2.2 用腳本製作工具鏈
    3.2.2.1 croostool-0.43
    http://www.kegel.com/crosstool/crosstool-0.43.tar.gz
    製作工具鏈的源碼包搭配情況: http://www.kegel.com/crosstool/crosstool-0.43/buildlogs/
    3.2.2.2 buildroot
    http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2
    若想詳細地瞭解buildroot可參考該文檔http://buildroot.uclibc.org/buildroot.html
    3.2.3 利用OE製作工具鏈
    http://www.scratchbox.org/wiki/OpenEmbedded
    3.3 android工具鏈與gnu工具鏈的比較
    Android所用的Toolchain(即交叉編譯工具鏈)可從下面的網址下載:
    http://android.kernel.org/pub/android-toolchain-20081019.tar.bz2。如果下載了完整的Android項目的源代碼,則可以在
    “/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin”目錄下找到交叉編譯工具,比如Android所用的
    arm-eabi-gcc-4.2.1。Android並沒有採用glibc作爲C庫,而是採用了Google自己開發的Bionic Libc,它的官方Toolchain也是基於
    Bionic Libc而並非glibc的。這使得使用或移植其他Toolchain來用於Android要比較麻煩:在Google公佈用於Android的官方Toolchain之前,
    多數的Android愛好者使用的Toolchain是在http://www.codesourcery.com/gnu_toolchains/arm/download.html 下載的一個通用的
    Toolchain,它用來編譯和移植Android 的Linux內核是可行的,因爲內核並不需要C庫,但是開發Android的應用程序時,直接採用或者移植其他
    的Toolchain都比較麻煩,其他Toolchain編譯的應用程序只能採用靜態編譯的方式才能運行於Android模擬器中,這顯然是實際開發中所不能接
    受的方式。目前尚沒有看到說明成功移植其他交叉編譯器來編譯Android應用程序的資料。
    與glibc相比,Bionic Libc有如下一些特點:
    -          採用BSD License,而不是glibc的GPL License;
    -          大小隻有大約200k,比glibc差不多小一半,且比glibc更快;
    -          實現了一個更小、更快的pthread;
    -          提供了一些Android所需要的重要函數,如”getprop”, “LOGI”等;
    -          不完全支持POSIX標準,比如C++ exceptions,wide chars等;
    -          不提供libthread_db 和 libm的實現
    另外,Android中所用的其他一些二進制工具也比較特殊:
    -          加載動態庫時使用的是/system/bin/linker而不是常用的/lib/ld.so;
    -          prelink工具不是常用的prelink而是apriori,其源代碼位於” /build/tools/apriori”
    -          strip工具也沒有采用常用的strip,即“/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin”
    目錄下的arm-eabi-strip,而是位於/out/host/linux-x86/bin/的soslim工具。
    參考文檔:
    CLFS2.0原理分析 
    http://www.linuxsir.org/bbs/showthread.php?t=267672
    Cross-Compiled Linux From Scratch
    http://cross-lfs.org/view/clfs-sysroot/arm/
    全手工製作arm-linux交叉編譯工具鏈《一》
    http://blog.chinaunix.net/u2/62168/showart_1898748.html
    自己製作arm-linux交叉編譯環境(一)-scratch篇
    http://blog.csdn.net/chenzhixin/archive/2007/01/12/1481442.aspx
    如何建立交叉編譯工具鏈
    http://www.decell.org/article.asp?id=53
    Android Toolchain與Bionic Libc
    http://www.top-e.org/jiaoshi/html/?151.html
    ndroid編譯環境(2) - 手工編譯C模塊
    =================================================
    =================================================
    4. 軟件編譯常識
    4.1 鏈接器和加載器
    4.2 android 的標準鏈接器和加載器
    4.3 Makefile基本語法
    何爲鏈接器和加載器? 
    鏈接器爲ld,加載爲ld-linux.so.2,兩個的區別很大,一個編譯時用,一個運行時用,ld負責在編譯的搜索路徑裏找到要求的庫,並查看
    是否有提供了需要的 符號(如函數等),如果有,記錄相關信息到程序中,由ld-linux.so.2在執行時查找到該庫,並根據相關信息進行需
    要符號的重定位等工作.注意 這兩者的搜索庫的方式是不同的。
    動態連接器通常是指的動態加載器(不要與 Binutils 裏的標準連接器 ld 混淆了)。動態連接器由 Glibc 提供,用來
    找到並加載一個程序運行時所需的共享庫,在做好程序運行的準備之後,運行這個程序。動態連接器的名稱通常是 
    ld-linux.so.2,標準連接器 ld 由 Binutils 這個包提供。
    標準連接器
    查看gcc使用的標準連接器
    mhf@mhf-desktop:/usr/local/marvell-arm-linux-4.1.1/bin$ arm-linux-gcc -print-prog-name=ld
    編譯時庫的搜索路徑,以下幾種方式讓連接器去找需要的庫
    1. 編譯的時候明確指定,如: gcc test.c  ./say.so -o test中的   ./say.so
    2. 編譯 Binutils 的時候通過LIB_PATH 變量指定,
    如:make -C ld LIB_PATH=/tools/lib
    -C ld LIB_PATH=/tools/lib
    這個選項重新編譯 ld 子目錄中的所有文件。在命令行中指定 Makefile 的 LIB_PATH 變量值,使它明確指向/tools/lib工具目錄,
    以覆蓋默認值。這個變量的值指定了連接器的默認庫搜索路徑。
    來源:Linux From Scratch - 版本 6.4第 5 章 構建臨時系統  5.4. Binutils-2.18 - 第一遍
    http://www.bitctp.org/lfsbook-6.4/chapter05/binutils-pass1.html
    3. 在源碼包configure的時候通過  --with-lib-path 指定,或者 --lib- path
    例如:
    binutils-2.18/configure --prefix=/tools --disable-nls --with-lib-path=/tools/lib
    配置選項的含義:
    --with-lib-path=/tools/lib
        告訴配置腳本在爲編譯 Binutils 的過程中使用正確的庫搜索路徑,也就是將 /tools/lib 傳遞給連接器。這防止連接器搜索宿主系統中的庫文件目錄。
    來源: Linux From Scratch - 版本 6.4 第 5 章 構建臨時系統  lfs 5.13. Binutils-2.18 - 第二遍 
    http://www.bitctp.org/lfsbook-6.4/chapter05/binutils-pass2.html
    4. 到 ld –verbose | grep SEARCH 列出的默認目錄下去找
    5. -L/usr/gpephone/lib 指定的目錄找
    經常以 LDFLAGS=" -L/usr/gpephone/lib  -L/lib -L/usr/lib -L/usr/X11R7/lib" 的方式傳入
    參數 -rpath 與  -rpath-link 
    如果使用了'-rpath'選項, 那運行時搜索路徑就只從'-rpath'選項中得到
    'nodefaultlib'標誌一個對象,使在搜索本對象所依賴的庫時,忽略所有缺省庫搜索路徑.
    LDFLAGS="-Wl,-rpath-link=/usr/gpephone/lib/:/usr/gphone/lib:/usr/local/lib  -L/usr/gpephone/lib  -L/usr/gphone/lib"
    -rpath 與  -rpath-link 的特性:
    1. 在編譯的時候我們都可以使用這兩個路徑,
    2. '-rpath'跟'-rpath_link'的不同之處在於,由'-rpath'指定的路徑會被包含到可執行程序中,並在運行時使用,
    而'-rpath-link'選項僅僅在鏈接時起作用。
    -dumpspecs               Display all of the built in spec strings
      -dumpversion             Display the version of the compiler
      -dumpmachine             Display the compiler's target processor
      -print-search-dirs       Display the directories in the compiler's search path
      -print-prog-name=  Display the full path to compiler component 
      -specs=            Override built-in specs with the contents of 
      -Wa,            Pass comma-separated  on to the assembler
      -Wp,            Pass comma-separated  on to the preprocessor
      -Wl,            Pass comma-separated  on to the linker
    從工具鏈內建的規範中查看動態加載器
    gcc -dumpspecs | grep  dynamic-linker  //本機
    查看編譯起所指定的動態加載器
    1. s3c2440 (arm9tdmi) 平臺的工具鏈
    /scratchbox/compilers/arm-9tdmi-softfloat-linux-gcc-3.4.4-glibc-2.3.5/bin/arm-softfloat-linux-gnu-gcc -dumpspecs | grep  dynamic-linker
    /scratchbox/compilers/arm-softfloat-linux-gcc-3.4.4-glibc-2.3.5/bin/arm-softfloat-linux-gnu-gcc -dumpspecs | grep  dynamic-linker
    2. marvell 的工具鏈
    /scratchbox/compilers/marvell-arm-linux-4.1.1/bin/arm-linux-gcc  -dumpspecs | grep  dynamic-linker
    3. scrathbox 中工具鏈 host-gcc 
    /scratchbox/compilers/host-gcc/bin/host-gcc  -dumpspecs | grep  dynamic-linker
    如果我們在編譯的時候給編譯起 gcc 指定 -specs=/scratchbox/compilers/host-gcc/host-gcc.spec ,那麼-specs指定
    的規範將會覆蓋工具鏈內建的規範。
    cat /scratchbox/compilers/host-gcc/host-gcc.specs  | grep ld 有如下內容:
    -dynamic-linker /scratchbox/host_shared/lib/ld.so
    /scratchbox/compilers/host-gcc/bin/gcc -specs=/scratchbox/compilers/host-gcc/host-gcc.specs
    mhf@mhf-desktop:/usr/local/marvell-arm-linux-4.1.1/arm-iwmmxt-linux-gnueabi/bin$ ./gcc -dumpspecs|grep dynamic-linker
    gcc -dumpspecs | sed 's@/lib/ld-linux.so.2@/tools&@g'  | sudo tee  `dirname $(gcc -print-libgcc-file-name)`/specs
    cat `dirname $(gcc -print-libgcc-file-name)`/specs | grep tools
    查看本機應用程序使用的動態加載器
    readelf -l /usr/bin/make | grep interpreter 
    [Requesting program interpreter: /lib/ld-linux.so.2]
    查看 scratchbox 中應用程序使用的動態加載器
    readelf -l /scratchbox/tools/bin/make | grep interpreter
    [Requesting program interpreter: /scratchbox/host_shared/lib/ld.so]
    cd ~/svn/mohuifu.svn/trunk/mysource/compiler_test
    /scratchbox/compilers/host-gcc/bin/gcc -specs=/scratchbox/compilers/host-gcc/host-gcc.specs -o ld.so.test1 ld.so.test.c
    /scratchbox/compilers/host-gcc/bin/gcc -o ld.so.test2 ld.so.test.c
    readelf -l ./ld.so.test1 | grep interpreter 
    readelf -l ./ld.so.test2 | grep interpreter 
    其他示例:
    readelf -l /scratchbox/tools/bin/make | grep interpreter
    readelf -l /usr/bin/make | grep interpreter
    分別顯示:
    [Requesting program interpreter: /scratchbox/host_shared/lib/ld.so]
    [Requesting program interpreter: /lib/ld-linux.so.2]
    下面的方式也可以查看應用程序所使用的加載器
    strings  /scratchbox/tools/bin/make  |grep lib
    strings  /usr/bin/make  |grep lib
    分別爲:
    /scratchbox/host_shared/lib/ld.so
    /lib/ld-linux.so.2
    查看應程序加載器庫的搜索路徑
    顯示 scratchbox 中加載器的庫搜索路徑
    strings /scratchbox/host_shared/lib/ld.so |grep lib
    display library search paths
    /scratchbox/host_shared/lib/
    /scratchbox/tools/lib/
    顯示本機中加載器的庫搜索路徑
    strings  /lib/ld-linux.so.2 |grep lib
    display library search paths
    /lib/
    /usr/lib/
    /lib/i486-linux-gnu/
    /usr/lib/i486-linux-gnu/
    ldd 驗證應用程序所使用動態庫
    ldd /scratchbox/tools/bin/make
            libc.so.6 => /scratchbox/host_shared/lib/libc.so.6 (0xb7ef9000)
            /scratchbox/host_shared/lib/ld.so => /scratchbox/host_shared/lib/ld.so (0xb802f000)
    ldd /usr/bin/make
            librt.so.1 => /lib/tls/i686/cmov/librt.so.1 (0xb7fb9000)
            libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e5b000)
            libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7e42000)
            /lib/ld-linux.so.2 (0xb7fd5000)
    參考文檔:
    交叉編譯中libtool相關的問題
    http://hi.baidu.com/lieyu063/blog/item/9c99a2dd23e41f365882dd39.html
    靜態庫和共享庫庫的定位搜索路徑
    http://blog.csdn.net/lwhsyit/archive/2008/08/26/2830783.aspx
    Linux動態連接原理
    http://blog.chinaunix.net/u2/67984/showart_1359874.html
    程序編譯鏈接運行時對庫關係的探討(原創)
    http://www.360doc.com/content/061107/09/13188_251964.html
    http://lamp.linux.gov.cn/Linux/LFS-6.2/chapter05/toolchaintechnotes.html
    [Linux命令] ld 中文使用手冊完全版(譯) 
    http://blog.csdn.net/rstevens/archive/2008/01/28/2070568.aspx
    scratchbox 是mameo (nokia) 提供的一個集成開發環境,可以去官方網站:
    http://www.scratchbox.org/
    http://www.scratchbox.org/download/
    4.2 android 的標準鏈接器和加載器
    android的標準鏈接器 ./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-ld
    android 中標準連接器搜索庫的路徑
    ./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-ld -verbose | grep SEARCH
    SEARCH_DIR("/android/mathias/armdev/toolchain-eabi-4.2.1/arm-eabi/lib");
    Android編譯環境所用的交叉編譯工具鏈是./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc,
    -I和-L參數指定了所用的C庫頭文件和動態庫文件路徑分別是bionic/libc /include 和out/target/product/generic/obj/lib,
    其他還包括很多編譯選項以及-D所定義的預編譯宏。這裏值得留意的是參數“-Wl,-dynamic-linker,/system/bin/linker”,它指定了
    Android專用的動態鏈接器/system/bin/linker,而不是通常所用的ld.so。
    上面的“make clean-$(LOCAL_MODULE)”是Android編譯環境提供的make clean的方式。
    android中應用程序使用的加載器
    strings  out/target/product/littleton/obj/EXECUTABLES/rild_intermediates/rild  | grep link
    /system/bin/linker
    ./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -dumpspecs|grep dynamic-linker
    %{mbig-endian:-EB} %{mlittle-endian:-EL} %{static:-Bstatic} %{shared:-shared} %{symbolic:-Bsymbolic} 
    %{!static:%{shared: -Bsymbolic} %{!shared:%{rdynamic:-export-dynamic} %{!dynamic-linker:-dynamic-linker /system/bin/linker}}} -X
    android中加載器搜索庫的路徑
    strings /nfsroot/rootfs/system/bin/linker | grep lib
    /system/lib
    /lib
    生成的可執行程序可用file和readelf命令來查看一下:
    file out/target/product/littleton/obj/EXECUTABLES/rild_intermediates/rild 
    out/target/product/littleton/obj/EXECUTABLES/rild_intermediates/rild: ELF 32-bit LSB executable, 
    ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
    readelf -d out/target/product/littleton/obj/EXECUTABLES/rild_intermediates/rild  |grep NEEDED
    0x00000001 (NEEDED)                     Shared library: [liblog.so]
    0x00000001 (NEEDED)                     Shared library: [libcutils.so]
    0x00000001 (NEEDED)                     Shared library: [libril.so]
    0x00000001 (NEEDED)                     Shared library: [libc.so]
    0x00000001 (NEEDED)                     Shared library: [libstdc++.so]
    0x00000001 (NEEDED)                     Shared library: [libm.so]
    0x00000001 (NEEDED)                     Shared library: [libdl.so]
    這是ARM格式的動態鏈接可執行文件,運行時需要libc.so和libm.so。“not stripped”表示它還沒被STRIP。嵌入式系統中爲節省空間通常
    將編譯完成的可執行文件或動態庫進行STRIP,即去掉其中多餘的符號表信息。在前面“make helloworld showcommands”命令的最後我們也
    可以看到,Android編譯環境中使用了out/host/linux-x86/bin/soslim工具進行STRIP。
    4.3 Makefile基本語法
    Makefile詳解(超級好)
    linux/Unix環境下的make和makefile詳解
    http://www.unlinux.com/doc/program/20051026/2365.html
    跟我一起寫 Makefile
    http://dev.csdn.net/develop/article/20/20025.shtm
    =================================================
    =================================================
    5. 設置模塊流程分析
    rild 流程分析
    5.1 設置 pin 狀態,pin認證
    5.1.1 設置pin狀態
    5.1.2 修改sim卡pin
    5.1.3 pin認證流程
    5.2 網絡設置
    5.3 屏幕背光設置
    5.4 獲取,顯示電池狀態
    ================
    EditPinPreference.java (packages\apps\settings\src\com\android\settings)
    private OnPinEnteredListener mPinListener;
    protected void onDialogClosed(boolean positiveResult)
      mPinListener.onPinEntered(this, positiveResult);
    執行  SimLockSettings.java (packages\apps\settings\src\com\android\settings)中函數:
    public void onPinEntered(EditPinPreference preference, boolean positiveResult)
    修改pin狀態: tryChangeSimLockState();
    修改pin:        tryChangePin();
    5.1.1 設置pin狀態
    private void tryChangeSimLockState()
      Message callback = Message.obtain(mHandler, ENABLE_SIM_PIN_COMPLETE);
      mPhone.getSimCard().setSimLockEnabled(mToState, mPin, callback);
    進入sim lock 菜單會顯示初始化pin狀態,是通過下面語句得到:
    mPinToggle.setChecked(mPhone.getSimCard().getSimLockEnabled());
    mPhone.getSimCard().setSimLockEnabled(mToState, mPin, callback)調用的是文件:
    GsmSimCard.java (frameworks\base\telephony\java\com\android\internal\telephony\gsm)中的函數:
    public void setSimLockEnabled (boolean enabled,String password, Message onComplete) {
      int serviceClassX;
      serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
                    CommandsInterface.SERVICE_CLASS_DATA +
                    CommandsInterface.SERVICE_CLASS_FAX;
      mDesiredPinLocked = enabled;
      phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
                    enabled, password, serviceClassX,
                    obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
    phone.mCM.setFacilityLock 調用的是文件:
    RIL.java (frameworks\base\telephony\java\com\android\internal\telephony\gsm)中的函數:
        public void
        setFacilityLock (String facility, boolean lockState, String password,
                            int serviceClass, Message response)
        {
            String lockString;
             RILRequest rr
                    = RILRequest.obtain(RIL_REQUEST_SET_FACILITY_LOCK, response);
            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
            // count strings
            rr.mp.writeInt(4);
            rr.mp.writeString(facility);
            lockString = (lockState)?"1":"0";
            rr.mp.writeString(lockString);
            rr.mp.writeString(password);
            rr.mp.writeString(Integer.toString(serviceClass));
            send(rr);
        }
    設置應用程序向 rild 發送 RIL_REQUEST_SET_FACILITY_LOCK 請求的 socket消息,
    android的初始源代碼中 RIL_REQUEST_SET_FACILITY_LOCK 請求,在參考實現 Reference-ril.c 
    (hardware\ril\reference-ril) 中沒有實現。
    我們需要做得工作是:
    ==========
    5.1.2 修改sim卡pin
    private void tryChangePin()
      mPhone.getSimCard().changeSimLockPassword(mOldPin,mNewPin, callback);
    mPhone.getSimCard 調用的是文件:
    GsmSimCard.java (frameworks\base\telephony\java\com\android\internal\telephony\gsm)中的函數:
    public void changeSimLockPassword(String oldPassword, String newPassword,
                Message onComplete)
      phone.mCM.changeSimPin(oldPassword, newPassword,
                    obtainMessage(EVENT_CHANGE_SIM_PASSWORD_DONE, onComplete));
    phone.mCM.changeSimPin 調用的是文件:
    RIL.java (frameworks\base\telephony\java\com\android\internal\telephony\gsm)中的函數:
        public void
        changeSimPin(String oldPin, String newPin, Message result)
        {
            RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN, result);
            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
            rr.mp.writeInt(2);
            rr.mp.writeString(oldPin);
            rr.mp.writeString(newPin);
            send(rr);
        }
    rild端處理流程:
    5.1.3 pin認證流程
    ========
    5.2 網絡設置
    =======
    5.3 屏幕背光設置
    packages/apps/Settings/src/com/android/settings/BrightnessPreference.java
    背光設置滾動條和關閉按鈕都會調用  setBrightness(mOldBrightness);
    public void onProgressChanged(SeekBar seekBar, int progress,boolean fromTouch)
    protected void onDialogClosed(boolean positiveResult) 
        private void setBrightness(int brightness) {
            try {
                IHardwareService hardware = IHardwareService.Stub.asInterface(
                        ServiceManager.getService("hardware"));
                if (hardware != null) {
                    hardware.setBacklights(brightness);
                }
            } catch (RemoteException doe) {        
            }        
        }
    調用硬件服務器 HardwareService 的 setBacklights 函數
    HardwareService.java (frameworks\base\services\java\com\android\server):    
    public void setBacklights(int brightness)
    {
            . . .
            // Don't let applications turn the screen all the way off
            brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
            setLightBrightness_UNCHECKED(LIGHT_ID_BACKLIGHT, brightness);
            setLightBrightness_UNCHECKED(LIGHT_ID_KEYBOARD, brightness);
            setLightBrightness_UNCHECKED(LIGHT_ID_BUTTONS, brightness);
            . . .
    }
    void setLightOff_UNCHECKED(int light) 

            //本地調用 setLight_native
            setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0);
    }
        void setLightBrightness_UNCHECKED(int light, int brightness) {
            int b = brightness & 0x000000ff;
            b = 0xff000000 | (b = LIGHT_COUNT || devices->lights[light] == NULL) {
            return ;
        }
        memset(&state, 0, sizeof(light_state_t));
        state.color = colorARGB;
        state.flashMode = flashMode;
        state.flashOnMS = onMS;
        state.flashOffMS = offMS;
        devices->lights[light]->set_light(devices->lights[light], &state);
    }
    Lights.h (hardware\libhardware\include\hardware):#define LIGHTS_HARDWARE_MODULE_ID "lights"
    com_android_server_HardwareService.cpp (frameworks\base\services\jni)
    err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    static const char *variant_keys[] = {
        "ro.hardware",  /* This goes first so that it can pick up a different
                           file on the emulator. */
        "ro.product.board",
        "ro.board.platform",
        "ro.arch"
    };
    int hw_get_module(const char *id, const struct hw_module_t **module) 
            status = load(id, prop, &hmi);
            status = load(id, HAL_DEFAULT_VARIANT, &hmi);
    static int load(const char *id, const char *variant,const struct hw_module_t **pHmi)
            snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH, id, variant);
    #define HAL_DEFAULT_VARIANT     "default"
    #define HAL_LIBRARY_PATH "/system/lib/hw"
    所以path等於:
    /system/lib/hw/light.marvell.so
    /system/lib/hw/light.default.so
    我們編譯的light模塊放在  /system/lib/hw/light.default.so 所以初始化成功。
    property_get(variant_keys, prop, NULL) 只有 ro.hardware 存在 [ro.hardware]: [marvell]
    static int lights_device_open(const struct hw_module_t* module, const char* name,struct hw_device_t** device)
        dev->set_light = set_light_backlight;
    static struct hw_module_methods_t lights_module_methods = {
        open: lights_device_open
    };
    hardware/libhardware/modules/lights/Android.mk
    LOCAL_MODULE:= lights.default
    err = module->methods->open(module, name, &device);
    執行的是 : lights_device_open
    const char * const brightness_file = "/sys/class/backlight/micco-bl/brightness";
    static int  set_light_backlight(struct light_device_t* dev,
                struct light_state_t const* state)

       . .  .
       color = state->color;
        tmp = ((77*((color>>16)&0x00ff)) + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
        brightness = tmp/16;
        LOGD("---->calling %s(),line=%d state->color=%d,brightness=%d\n",__FUNCTION__,__LINE__,state->color,brightness);
        len = sprintf(buf,"%d",brightness);
        len = write(fd, buf, len);
        . . .

    上面的函數完成了與內核的交互
    綜上所述,程序調用流程如下,上層應用通過 /sys/class/leds/lcd-backlight/brightnes 於內核打交道
    設置模塊 -> 硬件服務器 -> 本地調用 ->功能庫 -> 讀寫  /sys/class/leds/lcd-backlight/brightness 函數與內核交互
    Init.rc (vendor\marvell\littleton):    chown system system /sys/class/leds/keyboard-backlight/brightness
    Init.rc (vendor\marvell\littleton):    chown system system /sys/class/leds/lcd-backlight/brightness
    Init.rc (vendor\marvell\littleton):    chown system system /sys/class/leds/button-backlight/brightness
    5.4 獲取,顯示電池狀態
    電池狀態(正在充電(AC)):
    Status.java 
    String statusString;
    mBatteryStatus.setSummary(statusString);
        public static final int BATTERY_STATUS_UNKNOWN = 1;
        public static final int BATTERY_STATUS_CHARGING = 2;
        public static final int BATTERY_STATUS_DISCHARGING = 3;
        public static final int BATTERY_STATUS_NOT_CHARGING = 4;
        public static final int BATTERY_STATUS_FULL = 5;
        // values for "health" field in the ACTION_BATTERY_CHANGED Intent
        public static final int BATTERY_HEALTH_UNKNOWN = 1;
        public static final int BATTERY_HEALTH_GOOD = 2;
        public static final int BATTERY_HEALTH_OVERHEAT = 3;
        public static final int BATTERY_HEALTH_DEAD = 4;
        public static final int BATTERY_HEALTH_OVER_VOLTAGE = 5;
        public static final int BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6;
    public static final int BATTERY_PLUGGED_AC = 1;  電源充電
    public static final int BATTERY_PLUGGED_USB = 2; USB充電
    BatteryInfo.java (packages\apps\settings\src\com\android\settings)
    電池級別(50%)
    BatteryService.java (frameworks\base\services\java\com\android\server)
    電池服務器:
    構造函數:
    public BatteryService(Context context)
      mUEventObserver.startObserving("SUBSYSTEM=power_supply");
    ----------
    UEventObserver.java (frameworks\base\core\java\android\os)
    void startObserving(String match)
      ensureThreadStarted();
        sThread = new UEventThread();
        sThread.start();
      sThread.addObserver(match, this);
    -----------
      update()
        native_update();
        sendIntent();
    private final void sendIntent()
      Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
      intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
      ...
      intent.putExtra("status", mBatteryStatus);
      intent.putExtra("health", mBatteryHealth);
      intent.putExtra("present", mBatteryPresent);
      intent.putExtra("level", mBatteryLevel);
      intent.putExtra("scale", BATTERY_SCALE);
      intent.putExtra("icon-small", icon);
      intent.putExtra("plugged", mPlugType);
      intent.putExtra("voltage", mBatteryVoltage);
      intent.putExtra("temperature", mBatteryTemperature);
      intent.putExtra("technology", mBatteryTechnology);
    ActivityManagerNative.broadcastStickyIntent(intent, null);
    把讀取的電池信息通過廣播信息發送給所有的應用程序。
    native_update 本地調用的是文件 com_android_server_BatteryService.cpp (frameworks\base\services\jni) 中的函數:
    static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
    {
        setBooleanField(env, obj, AC_ONLINE_PATH, gFieldIds.mAcOnline);
        setBooleanField(env, obj, USB_ONLINE_PATH, gFieldIds.mUsbOnline);
        setBooleanField(env, obj, BATTERY_PRESENT_PATH, gFieldIds.mBatteryPresent);
        
        setIntField(env, obj, BATTERY_CAPACITY_PATH, gFieldIds.mBatteryLevel);
        setIntField(env, obj, BATTERY_VOLTAGE_PATH, gFieldIds.mBatteryVoltage);
        setIntField(env, obj, BATTERY_TEMPERATURE_PATH, gFieldIds.mBatteryTemperature);
        
        const int SIZE = 128;
        char buf[SIZE];
        
        if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0)
            env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
        
        if (readFromFile(BATTERY_HEALTH_PATH, buf, SIZE) > 0)
            env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
        if (readFromFile(BATTERY_TECHNOLOGY_PATH, buf, SIZE) > 0)
            env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf));
    }
    #define AC_ONLINE_PATH "/sys/class/power_supply/ac/online"
    #define USB_ONLINE_PATH "/sys/class/power_supply/usb/online"
    #define BATTERY_STATUS_PATH "/sys/class/power_supply/battery/status"
    #define BATTERY_HEALTH_PATH "/sys/class/power_supply/battery/health"
    #define BATTERY_PRESENT_PATH "/sys/class/power_supply/battery/present"
    #define BATTERY_CAPACITY_PATH "/sys/class/power_supply/battery/capacity"
    #define BATTERY_VOLTAGE_PATH "/sys/class/power_supply/battery/batt_vol"
    #define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp"
    #define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology"
    =================================================
    =================================================
    6. linux系統啓動流程分析
    6.1 桌面操作系統啓動流程(redhat,federa,ubuntu)
    6.2 小型嵌入式系統啓動流程
    6.3 android 系統啓動流程
    ==============
    6.1 桌面操作系統啓動流程(redhat,federa,ubuntu)
    ubuntu從6.10開始逐步用upstart代替原來的sysinit,進行服務進程的管理。爲了對原有的init實現向後兼容,
    目前ubuntu中與init相關的幾個目錄和應用程序,可以方便後面的論述。這些目錄和程序包括:
    init
    telinit  //字面理解 tell init
    runlevel
    /etc/event.d/
    /etc/init.d/
    /etc/rcX.d/
    首先是/etc/event.d/目錄,這是upstart的核心,upstart不同於原有的init的地方就在於它引入了event機制。Event 機制通俗的
    講就是將所有進程的觸發、停止等等都看作event(事件)。/etc/event.d/中就存放了目前upstart需要識別的event。這其中主要有三種
    rc-default, rcX(x=0,1,...6,S)以及ttyX。這rc-default就類似於 inittab文件,它就是設置默認運行級別的 ,需要運行程序的
    腳本,而ttyX則是設置僞終端數目的,也就是你Ctrl+Alt+F(1~6)調出的那個Console。我們以rc2爲例,cat rc2:
    rc-default
    start on stopped rcS
    telinit 2
    所以會依次執行 /etc/event.d/rcS /etc/event.d/rc2
    它們又會分別執行:
    exec /etc/init.d/rc S
    exec /etc/init.d/rc 2
    這樣,我們就可以自然地過渡到下一個重要的目錄,/etc/init.d/了。
    /etc/init.d/中存放的是服務(services)或者任務(tasks)的執行腳本。可以這麼說,只要你安裝了一個程序(特別是服務程序daemon),
    它可以在系統啓動的時候運行,那麼它必定會在/etc/init.d/中有一個腳本文件。
    執行了一個exec /etc/init.d/rc 2的命令。也就是說,給/etc/init.d/rc腳本傳遞了一個參數"2",讓它執行。
    rc腳本(很長,耐心點),能看到這樣的一段:
    # Now run the START scripts for this runlevel.
    # Run all scripts with the same level in parallel
    .......
    for s in /etc/rc$runlevel.d/S*
    .......
    將會開始執行/etc/rc2.d/下S開頭的腳本。這就過渡到下一個目錄/etc/rc2.d/了。
    /etc/rc2.d 都是一些到/etc/init.d/中腳本的符號鏈接。不同的是在開頭加上了S和一個數字,S表示在啓動時運行,數字則表示執行的先後順序。
    /etc/rcS.d/S35mountall.sh
    K08vmware
    S19vmware
    S20nfs-common
    S20nfs-kernel-server
    S20samba
    S20xinetd
    S30gdm
    S98usplash
    S99rc.local
    總結:
        這樣一來,upstart管理的ubuntu啓動過程應該就清楚了。梳理一下:
        1,內核啓動init
        2,init找到/etc/event.d/rc-default文件,確定默認的運行級別(X)
        3,觸發相應的runlevel事件,開始運行/etc/event.d/rcX
        4,rcX運行/etc/init.d/rc,傳入參數X
        5,/etc/init.d/rc腳本進行一系列設置,最後運行相應的/etc/rcX.d/中的腳本
        6,/etc/rcX.d/中的腳本按事先設定的優先級依次啓動,直至最後給出登錄畫面(啓動X服務器和GDM)
        理解了這些,手動配置開機服務的啓動與否就很簡單了。Ubutnu默認的啓動級別是2,不想啓動的程序,只要把相應的符號鏈接從/etc/rc2.d/中刪去即可
    注意:
    想redat ,federa 這些系統,他們用的是sysvinit ,有 /etc/inittab 文件,裏面定義了 :
    id:5:initdefault:
    si::sysinit:/etc/init.d/rcS
    init 直接解析  id:5:initdefault 字段,然後執行 /etc/rc5.d/ 下面的腳本
    ================
    參考文檔:
    linux教程:upstart 和ubuntu啓動過程原理介紹
    http://www.zhiweinet.com/jiaocheng/2009-06/12500.htm
    6.2 小型嵌入式系統啓動流程
    小型嵌入式的 init 通常使用busybox中自帶的,
    6.3 android 系統啓動流程
    參考文檔:
    init 是內核進入文件系統後第一個運行的程序,我們可以在linux的命令行中進行指定,如果沒指定,內核將會到/sbin/, /bin/ 等目錄下
    查找默認的init,如果沒有找到那麼就報告出錯。
    init 源代碼分析
    init的mian函數在文件:./system/core/init/init.c 中,init會一步步完成下面的任務:
    1.初始化log系統
    2.解析/init.rc和/init.%hardware%.rc文件  
    3. 執行 early-init action in the two files parsed in step 2.  
    4. 設備初始化,例如:在 /dev 下面創建所有設備節點,下載 firmwares.  
    5. 初始化屬性服務器,Actually the property system is working as a share memory. Logically it looks like a registry under Windows system.  
    6. 執行 init action in the two files parsed in step 2.  
    7. 開啓 屬性服務。
    8. 執行 early-boot and boot actions in the two files parsed in step 2.  
    9. 執行 Execute property action in the two files parsed in step 2.  
    10. 進入一個無限循環 to wait for device/property set/child process exit events.例如,如果SD卡被插入,init會收到一個設備插入事件,
    它會爲這個設備創建節點。系統中比較重要的進程都是由init來fork的,所以如果他們他誰崩潰了,那麼init 將會收到一個 SIGCHLD 信號,把這個信號轉化
    爲子進程退出事件, 所以在loop中,init 會操作進程退出事件並且執行 *.rc 文件中定義的命令。
    例如,在init.rc中,因爲有:
    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        socket zygote stream 666
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
    所以,如果zygote因爲啓動某些服務導致異常退出後,init將會重新去啓動它。
    int main(int argc, char **argv)
    {
        ...
        //需要在後面的程序中看打印信息的話,需要屏蔽open_devnull_stdio()函數
        open_devnull_stdio();
        ...
        //初始化log系統
        log_init();
        //解析/init.rc和/init.%hardware%.rc文件
        parse_config_file("/init.rc");
        ...
        snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
        parse_config_file(tmp);
        ...
        //執行 early-init action in the two files parsed in step 2.
        action_for_each_trigger("early-init", action_add_queue_tail);
        drain_action_queue();
        ...
        /* execute all the boot actions to get us started */
        /* 執行 init action in the two files parsed in step 2 */
        action_for_each_trigger("init", action_add_queue_tail);
        drain_action_queue();
        ...
        /* 執行 early-boot and boot actions in the two files parsed in step 2 */
        action_for_each_trigger("early-boot", action_add_queue_tail);
        action_for_each_trigger("boot", action_add_queue_tail);
        drain_action_queue();
        /* run all property triggers based on current state of the properties */
        queue_all_property_triggers();
        drain_action_queue();
        /* enable property triggers */  
        property_triggers_enabled = 1;    
        ...
        for(;;) {
            int nr, timeout = -1;
        ...
            drain_action_queue();
            restart_processes();
            if (process_needs_restart) {
                timeout = (process_needs_restart - gettime()) * 1000;
                if (timeout 
      
      
      
    重要的數據結構
    兩個列表,一個隊列。
    static list_declare(service_list);
    static list_declare(action_list);
    static list_declare(action_queue);
    *.rc 腳本中所有 service關鍵字定義的服務將會添加到 service_list 列表中。
    *.rc 腳本中所有 on     關鍵開頭的項將會被會添加到 action_list 列表中。
    每個action列表項都有一個列表,此列表用來保存該段落下的 Commands
    腳本解析過程
    parse_config_file("/init.rc")
    int parse_config_file(const char *fn)
    {
        char *data;
        data = read_file(fn, 0);
        if (!data) return -1;
        parse_config(fn, data);
        DUMP();
        return 0;
    }
    static void parse_config(const char *fn, char *s)

        ...
        case T_NEWLINE:
            if (nargs) {
                int kw = lookup_keyword(args[0]);
                if (kw_is(kw, SECTION)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
       ...

    parse_config會逐行對腳本進行解析,如果關鍵字類型爲  SECTION ,那麼將會執行 parse_new_section()
    類型爲 SECTION 的關鍵字有: on 和 sevice
    關鍵字類型定義在 Parser.c (system\core\init) 文件中
    Parser.c (system\core\init)
    #define SECTION 0x01
    #define COMMAND 0x02
    #define OPTION  0x04
    關鍵字        屬性       
    capability,  OPTION,  0, 0)
    class,       OPTION,  0, 0)
    class_start, COMMAND, 1, do_class_start)
    class_stop,  COMMAND, 1, do_class_stop)
    console,     OPTION,  0, 0)
    critical,    OPTION,  0, 0)
    disabled,    OPTION,  0, 0)
    domainname,  COMMAND, 1, do_domainname)
    exec,        COMMAND, 1, do_exec)
    export,      COMMAND, 2, do_export)
    group,       OPTION,  0, 0)
    hostname,    COMMAND, 1, do_hostname)
    ifup,        COMMAND, 1, do_ifup)
    insmod,      COMMAND, 1, do_insmod)
    import,      COMMAND, 1, do_import)
    keycodes,    OPTION,  0, 0)
    mkdir,       COMMAND, 1, do_mkdir)
    mount,       COMMAND, 3, do_mount)
    on,          SECTION, 0, 0)
    oneshot,     OPTION,  0, 0)
    onrestart,   OPTION,  0, 0)
    restart,     COMMAND, 1, do_restart)
    service,     SECTION, 0, 0)
    setenv,      OPTION,  2, 0)
    setkey,      COMMAND, 0, do_setkey)
    setprop,     COMMAND, 2, do_setprop)
    setrlimit,   COMMAND, 3, do_setrlimit)
    socket,      OPTION,  0, 0)
    start,       COMMAND, 1, do_start)
    stop,        COMMAND, 1, do_stop)
    trigger,     COMMAND, 1, do_trigger)
    symlink,     COMMAND, 1, do_symlink)
    sysclktz,    COMMAND, 1, do_sysclktz)
    user,        OPTION,  0, 0)
    write,       COMMAND, 2, do_write)
    chown,       COMMAND, 2, do_chown)
    chmod,       COMMAND, 2, do_chmod)
    loglevel,    COMMAND, 1, do_loglevel)
    device,      COMMAND, 4, do_device)
    parse_new_section()中再分別對 service 或者 on 關鍵字開頭的內容進行解析。
        ...
        case K_service:
            state->context = parse_service(state, nargs, args);
            if (state->context) {
                state->parse_line = parse_line_service;
                return;
            }
            break;
        case K_on:
            state->context = parse_action(state, nargs, args);
            if (state->context) {
                state->parse_line = parse_line_action;
                return;
            }
            break;
        }
        ...
    對 on 關鍵字開頭的內容進行解析
    static void *parse_action(struct parse_state *state, int nargs, char **args)
    {
        ...
        act = calloc(1, sizeof(*act));
        act->name = args[1];
        list_init(&act->commands);
        list_add_tail(&action_list, &act->alist);
        ...
    }
    對 service 關鍵字開頭的內容進行解析
    static void *parse_service(struct parse_state *state, int nargs, char **args)
    {
        struct service *svc;
        if (nargs name = args[1];
        svc->classname = "default";
        memcpy(svc->args, args + 2, sizeof(char*) * nargs);
        svc->args[nargs] = 0;
        svc->nargs = nargs;
        svc->onrestart.name = "onrestart";
        list_init(&svc->onrestart.commands);
        //添加該服務到 service_list 列表
        list_add_tail(&service_list, &svc->slist);
        return svc;
    }
    服務的表現形式:
    service   [  ]*
    ...
    申請一個service結構體,然後掛接到service_list鏈表上,name 爲服務的名稱 pathname 爲執行的命令 argument
    爲命令的參數。之後的 option 用來控制這個service結構體的屬性,parse_line_service 會對 service關鍵字後的
    內容進行解析並填充到 service 結構中 ,當遇到下一個service或者on關鍵字的時候此service選項解析結束。
    例如:
    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        socket zygote stream 666
        onrestart write /sys/android_power/request_state wake
    服務名稱爲:                           zygote
    啓動該服務執行的命令:                 /system/bin/app_process
    命令的參數:                           -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666: 創建一個名爲:/dev/socket/zygote 的 socket ,類型爲:stream
    當*.rc 文件解析完成以後:
    action_list 列表項目如下:
    on init
    on boot
    on property:ro.kernel.qemu=1
    on property:persist.service.adb.enable=1
    on property:persist.service.adb.enable=0
    init.marvell.rc 文件
    on early-init
    on init
    on early-boot
    on boot
    service_list 列表中的項有:
    service console
    service adbd
    service servicemanager
    service mountd
    service debuggerd
    service ril-daemon
    service zygote
    service media
    service bootsound
    service dbus
    service hcid
    service hfag
    service hsag
    service installd
    service flash_recovery
    設備初始化
    early-init 初始化
    初始化屬性服務器
    在init.c 的main函數中啓動狀態服務器。
    property_set_fd = start_property_service();
    狀態讀取函數:
    Property_service.c (system\core\init)
    const char* property_get(const char *name)
    Properties.c (system\core\libcutils)
    int property_get(const char *key, char *value, const char *default_value)
    狀態設置函數:
    Property_service.c (system\core\init)
    int property_set(const char *name, const char *value)
    Properties.c (system\core\libcutils)
    int property_set(const char *key, const char *value)
    在終端模式下我們可以通過執行命令 setprop  
    setprop 工具源代碼所在文件: Setprop.c (system\core\toolbox)
    Getprop.c (system\core\toolbox):        property_get(argv[1], value, default_value);
    Property_service.c (system\core\init)
    中定義的狀態讀取和設置函數僅供init進程調用,
    handle_property_set_fd(property_set_fd);
      property_set()   //Property_service.c (system\core\init)
        property_changed(name, value) //Init.c (system\core\init)
          queue_property_triggers(name, value)
          drain_action_queue()
    只要屬性一改變就會被觸發,然後執行相應的命令:  
    例如:
    在init.rc 文件中有
    on property:persist.service.adb.enable=1
      start adbd
    on property:persist.service.adb.enable=0
      stop adbd
    所以如果在終端下輸入:
    setprop property:persist.service.adb.enable 1或者0
    那麼將會開啓或者關閉adbd 程序。
    執行action_list 中的命令:
    從action_list 中取出 act->name 爲 early-init 的列表項,再調用 action_add_queue_tail(act)將其插入到
    隊列 action_queue 尾部。drain_action_queue() 從action_list隊列中取出隊列項 ,然後執行act->commands
    列表中的所有命令。
    所以從  ./system/core/init/init.c mian()函數的程序片段:
    action_for_each_trigger("early-init", action_add_queue_tail);
    drain_action_queue();
    action_for_each_trigger("init", action_add_queue_tail);
    drain_action_queue();
    action_for_each_trigger("early-boot", action_add_queue_tail);
    action_for_each_trigger("boot", action_add_queue_tail);
    drain_action_queue();
    /* run all property triggers based on current state of the properties */
    queue_all_property_triggers();
    drain_action_queue();
    可以看出,在解析完init.rc init.marvell.rc 文件後,action 命令執行順序爲:
    執行act->name 爲 early-init,act->commands列表中的所有命令
    執行act->name 爲 init,            act->commands列表中的所有命令
    執行act->name 爲 early-boot,act->commands列表中的所有命令
    執行act->name 爲 boot,            act->commands列表中的所有命令
    關鍵的幾個命令:
    class_start default   啓動所有service 關鍵字定義的服務。
    class_start 在act->name爲boot的 act->commands列表中,所以當 class_start 被觸發後,實際
    上調用的是函數 do_class_start()
    int do_class_start(int nargs, char **args)
    {
            /* Starting a class does not start services
             * which are explicitly disabled.  They must
             * be started individually.
             */
        service_for_each_class(args[1], service_start_if_not_disabled);
        return 0;
    }
    void service_for_each_class(const char *classname,
                                void (*func)(struct service *svc))
    {
        struct listnode *node;
        struct service *svc;
        list_for_each(node, &service_list) {
            svc = node_to_item(node, struct service, slist);
            if (!strcmp(svc->classname, classname)) {
                func(svc);
            }
        }
    }
    因爲在調用 parse_service() 添加服務列表的時候,所有服務 svc->classname 默認取值:"default",
    所以 service_list 中的所有服務將會被執行。
    參考文檔:
    http://blog.chinaunix.net/u1/38994/showart_1775465.html
    http://blog.chinaunix.net/u1/38994/showart_1168440.html
    淺析kernel啓動的第1個用戶進程init如何解讀init.rc腳本
    http://blog.chinaunix.net/u1/38994/showart_1168440.html
    Zygote 服務概論:
    Zygote 是android 系統中最重要的一個服務,它將一步一步完成下面的任務:
    start Android Java Runtime and start system server. It’s the most important service. The source is in device/servers/app.
    1. 創建JAVA 虛擬機
    2. 爲JAVA 虛擬機註冊android 本地函數
    3. 調用 com.android.internal.os.ZygoteInit 類中的main函數,android/com/android/internal/os/ZygoteInit.java.
    a) 裝載ZygoteInit類
    b) 註冊zygote socket
    c) 裝載preload classes(the default file is device/java/android/preloaded-classes)
    d) 裝載Load preload 資源
    e) 調用 Zygote::forkSystemServer (定義在./dalvik/vm/InternalNative.c)來fork一個新的進程,在新進程中調用 
    com.android.server.SystemServer 的main函數。
    a) 裝載 libandroid_servers.so庫
    bb) 調用JNI native init1 函數 (device/libs/android_servers/com_android_server_SystemServers)
    Load libandroid_servers.so
    Call JNI native init1 function implemented in device/libs/android_servers/com_android_server_SystemServers. 
    It only calls system_init implemented in device/servers/system/library/system_init.cpp.
    If running on simulator, instantiate AudioFlinger, MediaPlayerService and CameraService here.
    Call init2 function in JAVA class named com.android.server.SystemServer, whose source is in 
    device/java/services/com/android/server. This function is very critical for Android because it start all of 
    Android JAVA services.
    If not running on simulator, call IPCThreadState::self()->joinThreadPool() to enter into service dispatcher.
    SystemServer::init2 將會啓動一個新的線程來啓動下面的所有JAVA服務:
    Core 服務:
    1.  Starting Power Manager(電源管理)
    2.  Creating Activity Manager(活動服務)
    3.  Starting Telephony Registry(電話註冊服務)
    4.  Starting Package Manager(包管理器)
    5.  Set Activity Manager Service as System Process
    6.  Starting Context Manager
    7.  Starting System Context Providers
    8.  Starting Battery Service(電池服務)
    9.  Starting Alarm Manager(鬧鐘服務)
    10. Starting Sensor Service
    11. Starting Window Manager(啓動窗口管理器)
    12. Starting Bluetooth Service(藍牙服務)
    13. Starting Mount Service
    其他services:
    1.  Starting Status Bar Service(狀態服務)
    2.  Starting Hardware Service(硬件服務)
    3.  Starting NetStat Service(網絡狀態服務)
    4.  Starting Connectivity Service
    5.  Starting Notification Manager
    6.  Starting DeviceStorageMonitor Service
    7.  Starting Location Manager
    8.  Starting Search Service(查詢服務)
    9.  Starting Clipboard Service
    10. Starting Checkin Service
    11. Starting Wallpaper Service
    12. Starting Audio Service
    13. Starting HeadsetObserver
    14. Starting AdbSettingsObserver
    最後SystemServer::init2 將會調用 ActivityManagerService.systemReady 通過發送
    Intent.CATEGORY_HOME intent來啓動第一個 activity.還有另外一種啓動system server的方法是:
    通過名爲 system_server的程序(源代碼:device/servers/system/system_main.cpp)它也是通過
    調用 system_init 來啓動 system services,這時候就有個問題:爲什麼android 有兩種方式啓動system services?
    我的猜想是:
    My guess is that directly start system_server may have synchronous problem with zygote because 
    system_server will call JNI to start SystemServer::init2, while at that time zygote may not start 
    JAVA VM yet. So Android uses another method. After zynote is initialized, fork a new process to 
    start system services.
    Zygote服務啓動的詳細過程:
    通過啓動服務列表的 app_process 進程,實際上進入的是
    App_main.cpp (frameworks\base\cmds\app_process)
    main()
    根據 init.rc 中的 --zygote --start-system-server
    分別調用的是
    runtime.start("com.android.internal.os.ZygoteInit",startSystemServer);
    或者
    runtime.start();
    start()函數在 AndroidRuntime.cpp (frameworks\base\core\jni)文件中
    從打印信息:
    D/AndroidRuntime(   56): >>>>>>>>>>>>>> AndroidRuntime START GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");
      從上面的調用可以看出一類引用的過程都是從 main方法
    所以接着調用了 ZygoteInit 類的main方法
    main方法主要完成:
    1.Register zygote socket, Registers a server socket for zygote command connections
    2.Load preload classes(the default file is device/java/android/preloaded-classes).
    3.Load preload resources, Load in commonly used resources, so they can be shared across processes.
    4.Start SystemServer, Prepare the arguments and fork for the system server process.
    具體執行過程如下:
    ZygoteInit.java (frameworks\base\core\java\com\android\internal\os)中的mian
    main()
      registerZygoteSocket()
      preloadClasses()
        loadLibrary()
          Log.i(TAG, "Preloading classes...");
        Runtime.loadLibrary
          Dalvik_java_lang_Runtime_nativeLoad()
            dvmLoadNativeCode()
              LOGD("Trying to load lib %s %p\n", pathName, classLoader);
              System.loadLibrary("media_jni");
      preloadResources();
      startSystemServer()
        Zygote.forkSystemServer(parsedArgs.uid, parsedArgs.gid,parsedArgs.gids, debugFlags, null);
        //Zygote.java (dalvik\libcore\dalvik\src\main\java\dalvik\system)
          forkSystemServer()
            forkAndSpecialize()   //Zygote.java (dalvik\libcore\dalvik\src\main\java\dalvik\system)
              Dalvik_dalvik_system_Zygote_forkAndSpecialize() //dalvik_system_Zygote.c (dalvik\vm\native)
                Dalvik_dalvik_system_Zygote_forkAndSpecialize()
                  setSignalHandler()
                  fork()
          handleSystemServerProcess() //handleChildProc(parsedArgs, descriptors, newStderr);
            closeServerSocket();
          RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
            zygoteInit()         //RuntimeInit.java (frameworks\base\core\java\com\android\internal\os)
              zygoteInitNative()
                invokeStaticMain()
                  System.loadLibrary("android_servers");
                  //com.android.server.SystemServer startSystemServer() 函數中
                  m = cl.getMethod("main", new Class[] { String[].class });
                    //執行的是SystemServer 類的main函數 SystemServer.java (frameworks\base\services\java\com\android\server)
                    init1() //SystemServer.java (frameworks\base\services\java\com\android\server)             
                      //init1()實際上是調用android_server_SystemServer_init1(JNIEnv* env, jobject clazz)
                      //com_android_server_SystemServer.cpp (frameworks\base\services\jni)
                    android_server_SystemServer_init1()//JNI 調用
                      system_init() //System_init.cpp (frameworks\base\cmds\system_server\library)
                        // Start the SurfaceFlinger
                        SurfaceFlinger::instantiate();
                        //Start the AudioFlinger media playback  camera service
                          AudioFlinger::instantiate();
                          MediaPlayerService::instantiate();
                          CameraService::instantiate();
                          //調用 SystemServer 類的init2
                          runtime->callStatic("com/android/server/SystemServer", "init2");
                          init2()//SystemServer.java (frameworks\base\services\java\com\android\server)
                          ServerThread()
                            run()//在run中啓動電源管理,藍牙,等核心服務以及狀態,查找等其他服務
                             ((ActivityManagerService)ServiceManager.getService("activity")).setWindowManager(wm);
                             ...
                             ActivityManagerNative.getDefault().systemReady();         
      runSelectLoopMode();
        done = peers.get(index).runOnce();
          forkAndSpecialize()   //Zygote.java (dalvik\libcore\dalvik\src\main\java\dalvik\system)
            Dalvik_dalvik_system_Zygote_forkAndSpecialize() //dalvik_system_Zygote.c (dalvik\vm\native)
              forkAndSpecializeCommon()      
                setSignalHandler()
              RETURN_INT(pid);                
      closeServerSocket();
    見附A
    主進程runSelectLoopMode()
    5.Runs the zygote process's select loop runSelectLoopMode(), Accepts new connections as they happen, and 
    reads commands from connections one spawn-request's worth at a time.
    如果運行正常,則zygote進程會在runSelectLoopMode()中循環:
    zygote 被siganl(11)終止
    在  dalvik_system_Zygote.c (dalvik\vm\native)
    的 static void sigchldHandler(int s) 函數中打印:              
    "Process %d terminated by signal (%d)\n",
    "Exit zygote because system server (%d) has terminated\n",
    startSystemServer() ZygoteInit.java (frameworks\base\core\java\com\android\internal\os)
    SystemServer 的mian()函數會調用
    SystemServer.java (frameworks\base\services\java\com\android\server)中的 init1()函數。
    init1()實際執行的是com_android_server_SystemServer.cpp (frameworks\base\services\jni)
    中的 android_server_SystemServer_init1()。
    android_server_SystemServer_init1()調用的是
    System_init.cpp (frameworks\base\cmds\system_server\library) 中的 system_init()函數
    system_init()函數定義如下:
    extern "C" status_t system_init()
    {
        ...
        sp sm = defaultServiceManager();
        ...
        property_get("system_init.startsurfaceflinger", propBuf, "1");
        if (strcmp(propBuf, "1") == 0) {
            //讀取屬性服務器,開啓啓動 SurfaceFlinger服務
            //接着會開始顯示機器人圖標
            //BootAnimation.cpp (frameworks\base\libs\surfaceflinger):status_t BootAnimation::readyToRun()
            SurfaceFlinger::instantiate();
        }
        //在模擬器上 audioflinger 等幾個服務與設備上的啓動過程不一樣,所以
        //我們在這裏啓動他們。
        if (!proc->supportsProcesses()) {
            //啓動 AudioFlinger,media playback service,camera service服務
            AudioFlinger::instantiate();
            MediaPlayerService::instantiate();
            CameraService::instantiate();
        }
        //現在開始運行 the Android runtime ,我們這樣做的目的是因爲必須在 core system services
        //起來以後才能 Android runtime initialization,其他服務在調用他們自己的main()時,都會
        //調用 Android runtime
        //before calling the init function.
        LOGI("System server: starting Android runtime.\n");
        AndroidRuntime* runtime = AndroidRuntime::getRuntime();
        LOGI("System server: starting Android services.\n");
        //調用 SystemServer.java (frameworks\base\services\java\com\android\server)
        //中的init2函數
        runtime->callStatic("com/android/server/SystemServer", "init2");
           
        // If running in our own process, just go into the thread
        // pool.  Otherwise, call the initialization finished
        // func to let this process continue its initilization.
        if (proc->supportsProcesses()) {
            LOGI("System server: entering thread pool.\n");
            ProcessState::self()->startThreadPool();
            IPCThreadState::self()->joinThreadPool();
            LOGI("System server: exiting thread pool.\n");
        }
        return NO_ERROR;
    }
    System server: entering thread pool 表明已經進入服務線程 ServerThread
    在 ServerThread 類的run 服務中開啓核心服務:
        @Override
        public void run() {
            EventLog.writeEvent(LOG_BOOT_PROGRESS_SYSTEM_RUN,
                SystemClock.uptimeMillis());
            ActivityManagerService.prepareTraceFile(false);     // create dir
            Looper.prepare();
        //設置線程的優先級
            android.os.Process.setThreadPriority(
                    android.os.Process.THREAD_PRIORITY_FOREGROUND);
        ...
            //關鍵(核心)服務
            try {
                Log.i(TAG, "Starting Power Manager.");
                Log.i(TAG, "Starting activity Manager.");
                Log.i(TAG, "Starting telephony registry");
                Log.i(TAG, "Starting Package Manager.");
                Log.i(TAG, "tarting Content Manager.");
                Log.i(TAG, "Starting System Content Providers.");
                Log.i(TAG, "Starting Battery Service.");
                Log.i(TAG, "Starting Alarm Manager.");
                Log.i(TAG, "Starting Sensor Service.");
                Log.i(TAG, "Starting Window Manager.");
                Log.i(TAG, "Starting Bluetooth Service.");
                //如果是模擬器,那麼跳過藍牙服務。
                // Skip Bluetooth if we have an emulator kernel
             //其他的服務
                Log.i(TAG, "Starting Status Bar Service.");
                Log.i(TAG, "Starting Clipboard Service.");
                Log.i(TAG, "Starting Input Method Service.");
                Log.i(TAG, "Starting Hardware Service.");
                Log.i(TAG, "Starting NetStat Service.");
                Log.i(TAG, "Starting Connectivity Service.");
                Log.i(TAG, "Starting Notification Manager.");
                // MountService must start after NotificationManagerService
                Log.i(TAG, "Starting Mount Service.");
            Log.i(TAG, "Starting DeviceStorageMonitor service");
                Log.i(TAG, "Starting Location Manager.");
                Log.i(TAG, "Starting Search Service.");
                ...
                if (INCLUDE_DEMO) {
                    Log.i(TAG, "Installing demo data...");
                    (new DemoThread(context)).start();
                }
                try {
                    Log.i(TAG, "Starting Checkin Service.");
                    Intent intent = new Intent().setComponent(new ComponentName(
                            "com.google.android.server.checkin",
                            "com.google.android.server.checkin.CheckinService"));
                    if (context.startService(intent) == null) {
                        Log.w(TAG, "Using fallback Checkin Service.");
                        ServiceManager.addService("checkin", new FallbackCheckinService(context));
                    }
                } catch (Throwable e) {
                    Log.e(TAG, "Failure starting Checkin Service", e);
                }
                Log.i(TAG, "Starting Wallpaper Service");
            Log.i(TAG, "Starting Audio Service");
                Log.i(TAG, "Starting HeadsetObserver");
                Log.i(TAG, "Starting AppWidget Service");
            ...
                try {
                    com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
                } catch (Throwable e) {
                    Log.e(TAG, "Failure installing status bar icons", e);
                }
            }
            // make sure the ADB_ENABLED setting value matches the secure property value
            Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED,
                    "1".equals(SystemProperties.get("persist.service.adb.enable")) ? 1 : 0);
            // register observer to listen for settings changes
            mContentResolver.registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
                    false, new AdbSettingsObserver());
            // It is now time to start up the app processes...
            boolean safeMode = wm.detectSafeMode();
            if (statusBar != null) {
                statusBar.systemReady();
            }
            if (imm != null) {
                imm.systemReady();
            }
            wm.systemReady();
            power.systemReady();
            try {
                pm.systemReady();
            } catch (RemoteException e) {
            }
            if (appWidget != null) {
                appWidget.systemReady(safeMode);
            }
            // After making the following code, third party code may be running...
            try {
                ActivityManagerNative.getDefault().systemReady();
            } catch (RemoteException e) {
            }
            Watchdog.getInstance().start();
            Looper.loop();
            Log.d(TAG, "System ServerThread is exiting!");
        }
    startActivity()
      mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);

    ActivityManagerService.java 3136p (frameworks\base\services\java\com\android\server\am)
      startActivity()
        startActivityLocked() //3184
        int res = startActivityLocked(caller, intent, resolvedType,grantedUriPermissions, grantedMode, aInfo,
                        resultTo, resultWho, requestCode, -1, -1,
                        onlyIfNeeded, componentSpecified);
    public abstract class ActivityManagerNative extends Binder implements IActivityManager
    ActivityManagerService.java 1071p  (frameworks\base\services\java\com\android\server\am)
    ActivityManagerService.main()
      //ActivityManagerService.java 7375p (frameworks\base\services\java\com\android\server\am)
      m.startRunning(null, null, null, null);
        //ActivityManagerService.java 7421p (frameworks\base\services\java\com\android\server\am)
        systemReady();
    ActivityManagerService.java 3136p (frameworks\base\services\java\com\android\server\am)
      startActivity(IApplicationThread caller,Intent intent,...)
        int startActivityLocked(caller, intent,...)    //3184L 定義:2691L
          void startActivityLocked()       //3132L 定義:2445L
            resumeTopActivityLocked(null); //2562p 定義:2176L
            if(next=NULL)
            {
              intent.addCategory(Intent.CATEGORY_HOME);
              startActivityLocked(null, intent, null, null, 0, aInfo,null, null, 0, 0, 0, false, false);
            }
            else
            {
              startSpecificActivityLocked(next, true, false); //2439L 定義:1628L
                realStartActivityLocked() //1640L    定義:1524L
                //1651L 定義:1654L
                startProcessLocked(r.processName, r.info.applicationInfo, true, 0,"activity", r.intent.getComponent());
                  //1717L 定義:1721L
                  startProcessLocked(app, hostingType, hostingNameStr);
                    //1768L  定義:Process.java 222L(frameworks\base\core\java\android\os)
                    int pid = Process.start("android.app.ActivityThread",...)
                      startViaZygote(processClass, niceName, uid, gid, gids,debugFlags, zygoteArgs);
                        pid = zygoteSendArgsAndGetPid(argsForZygote);
                          sZygoteWriter.write(Integer.toString(args.size()));
            }

      runSelectLoopMode();
        done = peers.get(index).runOnce();
          forkAndSpecialize()   //Zygote.java (dalvik\libcore\dalvik\src\main\java\dalvik\system)
            Dalvik_dalvik_system_Zygote_forkAndSpecialize() //dalvik_system_Zygote.c (dalvik\vm\native)
              forkAndSpecializeCommon()      
                setSignalHandler()
              RETURN_INT(pid);
               
           ActivityThread main()
             ActivityThread attach() //ActivityThread.java 3870p (frameworks\base\core\java\android\app)     
               mgr.attachApplication(mAppThread)
               //ActivityManagerService.java 4677p (frameworks\base\services\java\com\android\server\am)
               attachApplication()
                 //ActivityManagerService.java 4677p (frameworks\base\services\java\com\android\server\am)
                 attachApplicationLocked()
                   if (realStartActivityLocked(hr, app, true, true)) //ActivityManagerService.java 4609p 
                                                                       //(frameworks\base\services\java\com\android\server\am)
                   realStartActivityLocked()
                     //ActivityManagerService.java (frameworks\base\services\java\com\android\server\am)
                     app.thread.scheduleLaunchActivity(new Intent(r.intent), r,r.info, r.icicle, results, newIntents, \
                        !andResume,isNextTransitionForward());
                     scheduleLaunchActivity()
                       queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
                         ActivityThread.H.handleMessage()
                           handleLaunchActivity()    //ActivityThread.java (frameworks\base\core\java\android\app)
                             performLaunchActivity() //ActivityThread.java (frameworks\base\core\java\android\app)
                               activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
         
                 
    /////////////////////////////////////////////////       
    init 守護進程:
    //andriod init 函數啓動過程分析:
    在main循環中會重複調用
    drain_action_queue();
    restart_processes();
    static void restart_processes()
    {
        process_needs_restart = 0;
        service_for_each_flags(SVC_RESTARTING,
                               restart_service_if_needed);
    }
    通過循環檢測服務列表service_list 中每個服務的 svc->flags 標記,如果爲 SVC_RESTARTING,
    那麼在滿足條件的情況下調用:restart_service_if_needed
    通過 service_start 來再次啓動該服務。
    ActivityManagerService.main
    I/SystemServer(   45): Starting Power Manager.
    I/ServiceManager(   26): service 'SurfaceFlinger' died
    D/Zygote  (   30): Process 45 terminated by signal (11)
    I/Zygote  (   30): Exit zygote because system server (45) has terminated
    通過錯誤信息發現程序在調用 SurfaceFlinger服務的時候被中止。
    Service_manager.c (frameworks\base\cmds\servicemanager):
    LOGI("service '%s' died\n", str8(si->name));
    Binder.c (frameworks\base\cmds\servicemanager):
    death->func(bs, death->ptr);
    Binder.c (kernel\drivers\misc)中的函數
    binder_thread_read()
    struct binder_work *w;
    switch (w->type)
    爲 BINDER_WORK_DEAD_BINDER 的時候
    binder_parse()中
    當 cmd 爲 BR_DEAD_BINDER的時候
    執行 death->func(bs, death->ptr)
    因爲函數
    int do_add_service(struct binder_state *bs,
                       uint16_t *s, unsigned len,
                       void *ptr, unsigned uid)
    的 si->death.func = svcinfo_death;
    所以 death->func(bs, death->ptr) 實際上執行的是
    svcinfo_death()//Service_manager.c (frameworks\base\cmds\servicemanager)
    ================================================
    =================================================
    7. linux下svn使用指南
    1.1 服務器端配置說明
    1.1.3 配置用戶和權限
    1.1.4 導入工程到倉庫中
    1.2 客戶端操作指南及使用規範
    1.2.1 檢出工作拷貝
    1.2.2 svn update 更新別人做的更改
    1.2.2.1 svn update 獲取最新版本
    1.2.2.2 svn update  -r 獲取特定的版本
    1.2.3 svn st 查看文件狀態信息
    1.2.4 svn log 查看log信息
    1.2.5 svn diff 查看文件修改詳情
    1.2.6 svn list 顯示版本庫的文件列表
    1.2.8 svn add 增加目錄或者文件
    1.2.9 svn delete 刪除目錄或者文件
    1.2.10 svn revert 取消本地修改
    1.2.11 svn commit 提交本地做的更改
    1.2.12 文件更新,提交時的衝突處理
    1.2.13 打標籤
    1.2.14 清除緩存的認證信息,重新輸入用戶名和密碼
    =================
    1.1 服務器端配置說明
    1.1.1 ubuntu-8.10 svn服務器安裝
    sudo apt-get install subversion
    1.1.2 建立版本庫(Repository)
    運行Subversion服務器需要首先要建立一個版本庫(Repository),可以看作服務器上存放數據的數據庫,在安裝了Subversion服務器之後,可以直接運行
    cd path_to_svn_root  例如:/home/svn
    svnadmin create --fs-type=fsfs  smartphone
    --fs-type 指定倉庫類型,可以爲fsfs或bdb 如果沒有指定默認創建爲fsfs類型  smartphone爲倉庫名稱
    1.1.3 配置用戶和權限
    修改 path_to_svn_repos/conf/svnserve.conf 文件,打開下面配置項
    ---------------------------
    #anon-access = read
    anon-access = none
    auth-access = write
    password-db = passwd
    authz-db = authz
    anon-access 應設置等於 none ,否則沒有log信息
    修改  path_to_svn_repos/conf/passwd 文件,添加用戶和密碼
    ----------------------------
    [users]
    wanghui=wanghui

    1.1.4 導入工程到倉庫中
    svn import  smartphone/  svn://192.168.2.148/smartphone
    1.1.5 運行svn服務器
    svnserve -d -r  path_to_svn_root  例如:/home/svn
    1.2 客戶端操作指南及使用規範
    以我們服務器上 android 源代碼爲例,介紹svn常用操作。
    1.2.1 檢出工作拷貝
    檢出工作拷貝到 ~/svn/cupcake-jiangping
    使用svn co url
    cd ~/svn
    svn co svn://192.168.2.148/smartphone/td0901/trunk/cupcake-jianping  cupcake-jianping
    1.2.2 svn update 更新別人做的更改
    1.2.2.1 svn update 獲取最新版本
    svn update cupcake-jiangping
    或者進入目錄更新
    cd cupcake-jiangping
    svn update
    如果負責的應用與系統的關聯性不是很大,通常不建議頻繁進行更新。
    1.2.2.2 svn update  -r 獲取特定的版本
    直接在某目錄下執行 svn update 獲取當前目錄下所有文件的最新版本,如果我們只需要獲取某個文件或者目錄的特定版本,可以通過-r 和 名稱進行指定:
    svn update –r 5  cupcake-jianping/packages/apps/Phone/src/com/android/phone/xxxx.java
    1.2.3 svn st 查看文件狀態信息
    M  cupcake-jianping/packages/apps/Phone/src/com/android/phone/xxxx.java
    ?  cupcake-jianping/packages/apps/Phone/src/com/android/phone/yyyy.java
    M  表明文件已經有修改
    ? 表明文件沒有受版本控制
    1.2.4 svn log 查看log信息
    svn log -r        查看所有版本的log信息
    svn log -r 5      查看某一版本的log信息
    svn log -r 5:19   查看某區間一系列版本的log信息
    如果要查看log的詳細信息可以加上 –v 選項,如:
    svn log –v -r 5
    1.2.5 svn diff 查看文件修改詳情
    顯示單個文件或者某目錄下所有文件的修改詳情
    svn diff有三種不同的用法
    1. 檢查本地修改
    2. 比較工作拷貝與版本庫
    3. 比較版本庫與版本庫
    不使用任何參數調用時,svn diff將會比較你的工作文件與緩存在.svn的“原始”拷貝,如:
    svn diff cupcake-jianping/packages/apps/Phone
    svn diff cupcake-jianping/packages/apps/Phone/src/com/android/phone/zzzz.java
    如果傳遞一個—revision –r 參數,你的工作拷貝會與指定的版本比較。
    svn diff -r 3 cupcake-jianping/packages/apps/Phone
    如果通過--revision –r 傳遞兩個通過冒號分開的版本號,這兩個版本會進行比較。
    svn diff -r 2:3 cupcake-jianping/packages/apps/Phone
    如果你在本機沒有工作拷貝,還是可以比較版本庫的修訂版本,只需要在命令行中輸入合適的URL:
    svn diff -r 33 svn://192.168.2.148/smartphone/td0901/trunk/cupcake-jianping/packages/apps/Phone
    1.2.6 svn list 顯示版本庫的文件列表
    svn list svn://192.168.2.148/smartphone/td0901
    design/
    hedoc/
    pm/
    release/
    tag/
    trunk/
    svn list svn://192.168.2.148/smartphone/td0901/trunk
    3src/
    boot-a1/
    cupcake-jianping/
    linux-2.6.28-a1/
    svn list 類似本機的ls命令,它查看的是服務器端的目錄結構。
    1.2.7 svn info 查看版本庫信息
    cd ~/svn/cupcake-jianping
    svn infos
    路徑: .
    URL: svn://192.168.2.148/smartphone/td0901/trunk/cupcake-jianping
    版本庫根: svn://192.168.2.148/smartphone
    版本庫 UUID: 1fac82c5-1665-442c-a8d6-2b3dd850438a
    版本: 146
    節點種類: 目錄
    調度: 正常
    最後修改的作者: tangligang
    最後修改的版本: 145
    最後修改的時間: 2009-07-31 15:40:50 +0800 (五, 2009-07-31)
    1.2.8 svn add 增加目錄或者文件
    svn add cupcake-jianping/packages/apps/Phone/src/com/android/phone/xxxx
    svn add cupcake-jianping/packages/apps/Phone/src/com/android/phone/yyyy.java
    1.2.9 svn delete 刪除目錄或者文件
    svn delete cupcake-jianping/packages/apps/Phone/src/com/android/phone/xxxx
    svn delete cupcake-jianping/packages/apps/Phone/src/com/android/phone/yyyy.java
    在進行刪除操作的時候要非常小心,假設我們要添加一個文件:
    cupcake-jianping/packages/apps/Phone/src/com/android/phone/yyyy.java
    但是在提交之前我們發現並不需要這個文件,這時候我們經常通過 svn delete 來撤銷之前添加的文件:
    svn delete cupcake-jianping/packages/apps/Phone/src/com/android/phone/yyyy.java
    這樣操作的後果往往導致本地的文件yyyy.java 被誤刪除掉,所以我們正確的做法是:
    svnn delete cupcake-jianping/packages/apps/Phone/src/com/android/phone/yyyy.java –keep-local
    1.2.10 svn revert 取消本地修改
    1.當你發現對某個文件的所有修改都是錯誤的,或許你根本不應該修改這個文件,或者是從開頭重新修改會更加容易的時候可以用這個命令。
    2.通過svn add 添加了一個項目,如果想取消可以通過該命令。
    1.2.11 svn commit 提交本地做的更改
    通常只對自己負責的模塊進行提交,如果負責電話模塊,那麼提交命令如下:
    svn commit cupcake-jiangping/packages/apps/Phone
    在提交之前建議用命令:
    svn st cupcake-jiangping/packages/apps/Phone 查看狀態
    M cupcake-jianping/packages/apps/Phone/src/com/android/phone/xxxx.java
    ?  cupcake-jianping/packages/apps/Phone/src/com/android/phone/yyyy.java
    M  表明文件已經有修改
    ? 表明文件沒有受版本控制
    1. 如果有 “?”存在,並且該文件或者目錄是自己添加並且是工程的一部分,那麼在提交之前必須先執行svn add 操作:svn add cupcake-jianping/packages/apps/Phone/src/com/android/phone/yyyy.java ; 
    2. 提交之前也必須解決衝突,否則會提交失敗。
    3. 提交之前必須寫log
    1.2.12 文件更新,提交時的衝突處理
    $ svn update
    U  xxxx
    G  yyyy
    C  xxxx.c
    1. 更新的時候如果前面的狀態爲:C 表示有衝突存在。
    2. 工作拷貝里做過修改,且服務器版本庫在修改前工作拷貝的版本後被提交過其他修改;那麼svn commit首先會失敗並要求update,此時便會出現版本衝突的情況。
    當你Update出現了衝突時,Subversion會產生三個文件
    filename.mine   :  你更新前的文件,沒有衝突標誌,只是你最新更改的內容。
    Filename.roldrev:  就是你在上次更新之後未作更改的版本。
    Filename.rnewrev:  客戶端從服務器剛剛收到的版本,這個文件對應版本庫的HEAD版本。
    衝突的文件內容,在衝突的地方將被使用“>>>>”標誌出來,用戶自己進行合併的取捨。
    解決衝突之後,svn resolved path_to_name,Subversion刪除衝突所產生三個文件刪除,此時你纔可以進行提交。( 也可以手動刪除此三個文件。)
    1.2.13 打標籤
    svn 的標籤是通過copy命令完成,但是操作的路徑必須是服務器的路徑,打標籤實際上類似於創建一個到特定版本的鏈接,如:
    svn cp svn://192.168.2.148/smartphone/td0901/trunk/cupcake-jianping  \
    svn://192.168.2.148/smartphone/td0901/tags/cupcake-1.0.6 
    如果 svn://192.168.2.148/smartphone/td0901/trunk/cupcake-jianping 的當前版本爲 5 ,\
    那麼 svn://192.168.2.148/smartphone/td0901/tags/cupcake-1.0.6 實際 \
    上就是 svn://192.168.2.148/smartphone/td0901/trunk/cupcake-jianping 版本5的一個標籤。
    1.2.14 清除緩存的認證信息,重新輸入用戶名和密碼
    一個具有權限控制的 svn 版本庫在第一次 checkout 工作拷貝的時候會要求輸入用戶名和密碼:
    認證領域:  176512f1-51ee-4947-8c07-88c90ab77ac5
    “$USER”的密碼:
    認證領域:  d3216b51-7915-4881-bf30-02e0672c61cd
    用戶名: xxxxx
    “xxxxx”的密碼:
    這些信息被緩存在 ~/.subversion/auth/svn.simple/ 如果需要更換另一個用戶登錄,必須先清除緩存的認證信息:
    rm ~/.subversion/auth/svn.simple/* -rf
    1.3 爲規避風險,建議遵守以下規範
    1.3.1 提交前審查修改情況, 用命令svn status瀏覽所做的修改,svn diff檢查修改的詳細信息
    1.3.2 提交時,必須填寫註釋,註釋內容清晰描述本次提交內容,變動信息。
    1.3.3 做較大修改時,和項目組其他同事的工作相關時,必須通知對方。
    1.3.4 納入版本控制的項目必須定期提交,至少一週提交一次,避免意外事故導致代碼丟失。
    1.3.5 每次提交後,必須確認工程可正常運行,即SVN裏保存的是可以正確運行的代碼,否則恢復至穩定版本。
    1.3.6 編譯過程動態產生的東西不要提交到服務器
    1.3.7 每次提交前先更新,這樣能在提交前發現是否和別人的衝突
    filelist=`find ./ -name "*.conf"`;svn add $filelist; svn commit  $filelist
    filelist=`find ./ -name "*.conf"`;svn delete $filelist --force --keep-local
    =================================================
    ================================================
    8. LFS 相關
    7.1 lfs 相關資源
    7.2 LFS問題解答
    =========
    LFS──Linux from Scratch,就是一種從網上直接下載源碼,從頭編譯LINUX的安裝方式。它不是發行版,只是一個菜譜,
    告訴你到哪裏去買菜(下載源碼),怎麼把這些生東西( raw code) 作成符合自己口味的菜餚──個性化的linux,不單單是
    個性的桌面。
    LFS 有什麼優勢呢?現在看來,它可以提供最快和最小的 Linux。但是最大的優勢就是,安裝LFS是菜鳥變成高手的捷徑。
    第一次安裝,需要按照LFS文檔安裝,如果在此期間所有文檔內容你都認真的閱讀,保證你受益匪淺;然後發現很多地方可以
    不按照別人的老路操作,這個時候用自己的方式參考第一次安裝的經驗,再一次建立linux,完成的時候,你會發現你在 LinuxSir.Org 
    上已經再也不是菜鳥了。
    7.1 lfs 相關資源
    官方網站:
    http://www.linuxfromscratch.org/
    lfs中文網站
    http://lfs.linuxsir.org/main/
    Linux From Scratch版本 6.2
    http://lamp.linux.gov.cn/Linux/LFS-6.2/index.html
    Linux From Scratch 版本 6.4
    http://www.bitctp.org/lfsbook-6.4/index.html
    Linux 發行版 LFS 討論區
    http://www.linuxsir.org/bbs/forumdisplay.php?f=58
    7.2 LFS問題解答
    構建LFS的過程中遇到一些問題,總體來說還算順利,但是還有一些不明白的地方,這裏總結一下:
    1./etc/fstab是否在開機就執行,是被誰調用執行的。
    2.爲什麼系統啓動之後就要自動掛載/proc 和/sys,這兩個目錄有什麼作用;devpts和tmpfs有什麼作用。
    參考章節:文件系統概述
    3.關於文件系統:按照我的理解,文件系統是內核提供支持的,可以看作是一種協議,提供一種數據組織方式,每個設備必須有自己的文件系統。
    不同文件系統的存儲設備的數據組織形式不同。mke2fs -jv /dev/默認在上面創建EXT3的文件系統嗎?既然這樣的話爲什麼
    我們還需要把以ext3掛載到一個目錄呢?如果不是 的話,又是創建什麼文件系統呢?爲什麼第六章中掛載了虛擬內核文件系統之後才能
    進入chroot環境呢?
    參考章節:文件系統概述
    4.虛擬文件系統.作用.什麼?
    虛擬內核文件系統(Virtual Kernel File Systems),是指那些是由內核產生但並不存在於硬盤上(存在於內存中)的文件系統,他們
    被用來與內核進行通信。
    5.符號鏈接 和硬鏈接的區別是什麼?什麼是符號鏈接?什麼是硬鏈接?爲什麼liinux上都使用符號鏈接,而不是硬鏈接?linux上很多地方
    使用了鏈接,是爲了組織清晰系統的結構和節省空間嗎?
    硬連接和軟連接的區別, 硬連接和複製的區別?
    硬連接記錄的是目標的 inode;軟連接記錄的是目標的 path。
    hard link 由於 inode 的緣故,只能在本分區中做 link;soft link 可以做跨分區的 link。硬連接因爲記錄的是 inode,所以不怕改名,
    比如ln aaa bbb, mv aaa ccc, 這時 bbb 仍然可以訪問;soft-link 就不行:source 的名字改變後,所有鏈接到這裏的 soft-link 
    全部變爲 broken。事實上,即使所有指向該 inode 的 hard-link 的文件名都變了,每一個仍然都可以訪問。我想這是它最大的優點吧。
    硬連接和複製的區別:
    幾個硬連接=幾個名字的同一個房子,這些名字可以相同或不同但地址(i-node)是一樣的, 所以硬連接被刪除只是把相應名字抹去,只有最
    後一個名字被抹去你纔會找不到房子;而複製是建造一個一模一樣的房子,當然地址(i-node)就不同的了。
    6.工作平臺中由Glibc提供的動態連接器與Binutils裏面的標準連接器有什麼區別?
    參考章節: 鏈接器和加載器
    7.$LFS/tools 目錄的所有者是僅存在於宿主環境中的 lfs 用戶。如果保留 $LFS/tools 目錄,那麼該目錄內文件的所有者的 user ID 就
    沒有對應的賬號 ?爲什麼沒有帳戶,難道不是LFS?
    查看 /etc/password /etc/group  兩個文件 分別記錄 用戶和組的信息
    如果用戶名和用戶ID 組名和組ID 的對應關係分別存在上面兩個文件中,那麼ls -ls 的時候就可以查看到用戶信息,而不再是ID等數字信息
    8.系統的環境變量保存在哪個文件?
    保存在tty中
    9。配置參數腳本時[alias1] [alias2 ...]什麼時候用到?
    別名的意思
    alias ls='ls --color=auto'
    /etc/skel/.bashrc:81:    #alias dir='dir --color=auto'
    /etc/skel/.bashrc:82:    #alias vdir='vdir --color=auto'
    /etc/skel/.bashrc:84:    #alias grep='grep --color=auto'
    /etc/skel/.bashrc:85:    #alias fgrep='fgrep --color=auto'
    /etc/skel/.bashrc:86:    #alias egrep='egrep --color=auto'
    /etc/skel/.bashrc:89:# some more ls aliases
    /etc/skel/.bashrc:90:#alias ll='ls -l'
    /etc/skel/.bashrc:91:#alias la='ls -A'
    /etc/skel/.bashrc:92:#alias l='ls -CF'
    alias mohuifu='ls -l'
    ========================
    9. linux 內核的初步理解
    4. 編譯內核
    此處內核編譯主要針對驅動組之外的同事
    1> 設置工具鏈
    內核的 linux-2.6.28-a1/Makefile 中設定了:
    CROSS_COMPILE        ?= arm-linux-
    所以設置PATH環境變量,保證能找到正確的工具鏈
    假設工具鏈位於: /usr/local/marvell-arm-linux-4.1.1/ 設置爲:
    export PATH:=/usr/local/marvell-arm-linux-4.1.1/bin/:$PATH
    2> 更改編譯選項(網絡啓動或者本機啓動)
    內核頂層目錄執行:
    make menuconfig
    General setup  ---> 
      
  • Initial RAM filesystem and RAM disk (initramfs/initrd) support
        ()    Initramfs source file(s) (NEW) 
    如果需要支持網絡啓動反選  [] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    如果需要支持本地啓動選中  
  • Initial RAM filesystem and RAM disk (initramfs/initrd) support
    設置 ()    Initramfs source file(s) (NEW) 爲 root
    拷貝  cupcake 編譯結果  out/target/product/littleton/root/  到內核頂層目錄
    3> 編譯
    內核頂層目錄執行 make zImage
    編譯好的內核:
    arch/arm/boot/zImage
    initramfs與initrd
    1. initrd是一個單獨的文件;initramfs和Linux內核鏈接在一起(/usr目錄下的程序負責生成initramfs文檔)。
    2. initrd是一個壓縮的文件系統映像(可以是ext2等,需要內核的驅動);initramfs是類似tar的cpio壓縮文檔。
    內核中的cpio解壓縮代碼很小,而且init數據在boot後可以丟棄。
    3. initrd運行的程序(initd,不是init)進行部分setup後返回內核;initramfs執行的init程序不返回內核
    (如果/init需要向內核傳遞控制權,可以再次安裝在/目錄下一個新的root設備並且啓動一個新的init程序)。
    編譯腳本及系統變量
    initramfs與initrd的區別
    1. initrd是一個單獨的文件;initramfs和Linux內核鏈接在一起(/usr目錄下的程序負責生成initramfs文檔)。
    2. initrd是一個壓縮的文件系統映像(可以是ext2等,需要內核的驅動);initramfs是類似tar的cpio壓縮文檔。
    內核中的cpio解壓縮代碼很小,而且init數據在boot後可以丟棄。
    3. initrd運行的程序(initd,不是init)進行部分setup後返回內核;initramfs執行的init程序不返回內核
    (如果/init需要向內核傳遞控制權,可以再次安裝在/目錄下一個新的root設備並且啓動一個新的init程序)。
    4. 切換到另一個root設備時,initrd執行pivot_root後,卸載ramdisk;initramfs是rootfs,既不能 
    pivot_root,也不能卸載。initramfs會刪掉rootfs的所有內容(find -xdev / -exec rm '{}' ';'),
    再次安裝root到rootfs(cd /newmount; mount --move . /; chroot .),把stdin/sdout/stderr掛在
    新的/dev/console上,重新執行init。由於這是一個相當困難的實現過程(包括在使用一個命令之前把它刪除),所以
    klibc工具包引入一個幫助程序/utils/run_init.c來執行上述過程。其他大部分工具包(包括busybox) 把這個命令
    稱爲"switch_root"。
    ======================= end ========================
                    
                    
                    

    本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u1/49742/showart_2077813.html
  • 發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章