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系统性能调谐
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启动过程原理介绍
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);
Initial RAM filesystem and RAM disk (initramfs/initrd) support () Initramfs source file(s) (NEW) 如果需要支持网络启动反选 [] Initial RAM filesystem and RAM disk (initramfs/initrd) support 如果需要支持本地启动选中