Android深度探索(卷1):HAL与驱动开发
上QQ阅读APP看书,第一时间看更新

第4章 源代码的下载和编译

上一章介绍了Git的使用方法。在本章玩点真格的。使用Git下载两套源代码。这两套源代码是本书的重点。其中一套就是Android源代码。这套源代码可谓是重量级别,全部下载有近4GB的大小。前面曾提到的HAL(硬件抽象层)的代码就位于这4GB的某个位置(后面会详细介绍)。不过很遗憾,Android的核心部分,也就是Linux内核源代码可不在这4GB之中。可能有的读者会认为到www.kernel.org下载一个Linux内核就可以了。我的回答是:NO。Android所使用的Linux内核是在官方Linux内核的基础上修改而成的。虽然大部分与官方Linux内核兼容,但仍然不能简单地互换。所以要用Google提供的URI去下载适应于Android的Linux内核。Linux内核源代码与Android源代码比起来可谓是小巫见大巫。完全下载Linux内核源代码只有几百MB(解压后)。本章主要介绍了如何下载和编译Android源代码和Linux内核源代码。现在就让我们开始源代码之旅吧!

4.1 下载、编译和测试Android源代码

Android源代码包含了很多东西,如内嵌在Android系统中的应用程序(拍照、计算器、日历、相册、拨号器等)的源代码;Android SDK带的各种工具(如adb、emulator、mksdcard、aapt等)的源代码;Android NDK的源代码;当然,还有本书要详细介绍的HAL源代码。所以从Android所带的源代码数量来看,Android源代码还是很复杂的。不过也不用担心。研究Android源代码并不一定要了解每一个细节,只要关注我们需要了解的源代码即可。

4.1.1 配置Android源代码下载环境

在下载Android源代码之前必须要在Linux终端执行一系列命令来配置下载环境。下面来看看如何安装下载Android源代码的环境。

第1步:创建一个用于存放下载脚本文件(repo)的目录(可将该脚本文件一放到任何目录中,在这里使用~/bin)。

    # mkdir ~/bin
        # PATH=~/bin:$PATH

第2步:下载repo脚本文件(用于下载Android源代码)。

    # curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
        # chmod a+x ~/bin/repo

repo文件实际上是用 python 脚本对 Git 的封装,主要是为了简化下载 Android 源代码的操作。

第3步:创建用于存放Android源代码的目录(可放在其他目录中)。

    # mkdir android_source
        # cd android_source

第4步:初始化。

    # repo init -u https://android.googlesource.com/platform/manifest

上面的命令会要求repo下载最新的Android源代码,也就是master分支。如果想下载其他分支(如android-4.0.1_r1),可以使用下面的命令。

    # repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1

第5步:开始下载Android源代码。

    # repo sync

执行上面的命令后,Linux终端一开始会显示一些类似于如图4-1所示的信息。其中,会列出Android源代码中包含当前模块的分支([new branch]和[new tag]后面的都是分支)。在显示完这些信息后,会正式开始下载Android源代码。接下来就是漫长的等待。根据读者网速和机器性能不同,完成下载的时间会有很大差异。但据笔者多次测试,按国内大多数人能达到的网速,至少要4个小时才能下完。所以在下载之前可以准备几部电影,以免下载过程中无事可做。因为Git会尽可能地占用带宽。下载过程中网络几乎做不了其他的事情(通过技术手段限速的除外)。

▲图4-1 下载Android源代码

如果要备份Android源代码,最好不要把版本库(.git目录)删除,否则再执行repo sync命令会重新下载整个Android源代码。如果依靠版本库,执行repo sync命令只会下载变化的部分,如图4-2所示。

▲图4-2 更新最新的Android源代码(增量下载)

4.1.2 Android源代码目录结构解析

经过漫长的等待之后,多达4GB的Android源代码终于下载完了。那么Android源代码到底是什么样的呢?现在就来一窥究竟。

现在进入上一节建立的保存Android源代码的目录(android_source),可以看到如图4-3所示的目录结构(Android 4.0.3的源代码)。

▲图4-3 Android源代码的目录结构

从图4-2的目录结构可以看出,除了Makefile文件外,其他的全是目录。这些目录分别保存了Android源代码的不同子项目。它们的含义如表4-1所示。

表4-1 Android源代码目录含义

续表

4.1.3 下载Android源代码中的一部分

在4.1.1节下载了完整的Android源代码。但如果只需要一部分源代码该怎么办呢?难道要下载所有的Android源代码吗?答案是否定的。只要了解了Android源代码的目录结构,就可以下载任何我们想要的部分。

下载Android源代码中的某个子项目有两种方法。

方法1:使用repo sync命令

假设想单独下载<Android源代码目录>/bootable/recovery项目,可以在repo sync命令后跟工程名(执行repo sync命令前仍然需要使用repo init初始化)。

    # repo sync platform/bootable/recovery

执行上面的命令后,会在当前目录下生成两级目录:bootable/recovery,recovery项目的所有代码都在这个目录中(大概3.5MB,读者可以放心下载,一会就下载完了)。虽然成功获取了recovery项目的源代码,但repo sync后面跟的项目名如何获取呢?

读者可以对比platform/bootable/recovery和Android源代码的目录结构就会很容易发现,原来工程名就是要下载的工程所在的路径全名(如bootable/recovery)前面加上platform/。如下载拍照程序如源代码(工程路径:packages/apps/Camera)的命令如下:

    # repo sync platform/packages/apps/Camera

现在已初步解决了如何获取工程名的问题,但又产生了新问题。获取工程名的基础是已经下载了完整的Android源代码,再根据源代码的目录结构获取要下载工程的相对路径,最后才和platform/组合成工程名。那如果没获取Android源代码,如何获取工程名呢?答案就在4.1.1节的第4步中。

当执行repo init命令初始化的过程中,会在当前目录生成一个隐藏目录.repo。在.repo目录中有一个非常重要的文件:manifest.xml。该文件包含了当前Android源代码版本库中包含的所有工程的列表。如下面的代码就是manifest.xml文件中的部分内容。每一行表示一个工程。其中path属性表示工程的路径,name属性表示工程名。repo sync命令后跟的就是name属性的值。

        <project path="bionic" name="platform/bionic" />
        <project path="bootable/bootloader/legacy" name="platform/bootable/bootloader/legacy"/>
        <project path="bootable/diskinstaller" name="platform/bootable/diskinstaller" />
        <project path="bootable/recovery" name="platform/bootable/recovery" />

从 manifest.xml 文件中还可以得知当前下载的是 Android 源代码版本库的那个分支。如果在manifest.xml文件的开始部分找到如下的内容,说明当前下载的Android源代码分支是android-4.0.1_r1.2。

        <default revision="refs/tags/android-4.0.1_r1.2"
                      remote="aosp"
                      sync-j="4" />

方法2:使用git clone命令

使用git clone命令下载指定工程源代码的命令格式如下:

        git clone https://android.googlesource.com/<project name>

从上面的命令格式可以看出,用 git clone 命令下载 Android 源代码,也需要工程名。如下载recovery工程的命令如下:

        git clone https://android.googlesource.com/platform/bootable/recovery

执行上面的命令后,会在当前目录下生成一个recovery目录(包含所有recovery项目的源代码)。从这一点可以看出,用repo sync和git clone命令下载源代码主要有如下区别。

· repo sync会在当前目录生成一个隐藏目录.repo。该目录存放了下载Android源代码所需的全部信息。

· repo sync下载指定工程的源代码会按照实际的路径存放,如bootable/recovery。而git clone会只会建立工程目录(如receovery),并不会建立层次路径结构。

如果直接在浏览器中输入https://android.googlesource.com/platform/bootable/recovery,就会显示如何下载receovery工程的命令(输入其他工程也是一样),如图4-4所示。

▲图4-4 显示下载制定工程的命令的页面

注意

以前Android源代码的下载域名是android.git.kernel.org,但现在Google已经放弃了这个域名。新的域名是android.googlesource.com。

4.1.4 编译Android 源代码

由于 Android源代码中的每一个工程目录都有Android.mk文件,所以在编译整个Android源代码时会递归调用每一个工程目录中的Android.mk文件来编译当前的工程。编译Android 源代码的步骤如下(在编译之前要先进入Android源代码根目录)。

第1步:初始化编译环境

在Linux终端执行如下两条命令中的任何一个。

    # source build/envsetup.sh

    # . build/envsetup.sh

envsetup.sh脚本文件主要用来初始化一些编译命令,如mm、mmm等。执行envsetup.sh命令后会在Linux终端显示如图4-5所示的信息。

▲图4-5 初始化编译环境

在envsetup.sh脚本文件中定义了一些Shell函数。当使用source命令执行envsetup.sh后,就可以在Linux终端执行调用这些函数。其中help就是一个在envsetup.sh脚本文件中定义的命令。通过help命令可以显示在envsetup.sh脚本文件中定义的常用命令,如图4-6所示。

▲图4-6 在envsetup.sh脚本文件中定义的常用命令

其中mm和mmm是两个很重要的命令。这两个命令都可以编译Android源代码中指定的工程,它们的区别是mm命令必须进入指定工程的目录进行编译,而mmm可以在Android源代码目录结构中的任何一级目录编译任意指定的工程,只要指定工程路径即可。例如,要编译Calendar工程,可以使用下面两种方式。

    # cd /working/android2.3.4_src
        # source build/envsetup.sh
        # cd packages/apps/Calculator
        # mm

    # cd /working/android2.3.4_src
        # build/envsetup.sh
        # mmm packages/apps/Calculator

如果是第一次使用mm或mmm编译指定的工程,都会输出如图4-7所示的信息。

▲图4-7 编译Calculator工程

第2步:选择目标

使用 lunch 命令设置编译目标。如下面的命令设置编译目标为 full-eng,表示针对所有的移动设备,Android模拟器有效,并打开所有的调试选项(在LogCat视图中会输出调试信息)。

    # lunch full-eng

如果只执行lunch命令,会显示如图4-8所示的选择菜单,在android 4.x的源代码中有10个选项。默认是full-eng,可以输入其他的选项。可以输入英文选项名称或选项序号。

▲图4-8 选择一个编译目标

在选择完如图 4-8 所示的某个选项后(需要按回车键),会输出如图 4-9 所示的信息,其中TARGET_BUILD_VARIANT变量保存了选择的编译目标。

▲图4-9 选择编译目标后的输出信息

第3步:编译Android源代码

执行如下命令即可编译Android源代码。

    # make

为了缩短编译时间,可以在拥有多CPU、多核、超线程的PC上使用-jn命令行参数。如笔者的PC是4核的,所以如果使用下面的命令编译Android源代码时,make命令就会尽可能使用每一个CPU内核进行编译。

    # make –j4

如果在多核的PC上不加-jn命令行参数,make命令只会利用一个CPU核进行编译,这将大大降低Android源代码的编译效率。

在第1步还介绍了mm和mmm命令。这两个命令实际上是Shell函数。其内部也是调用了make命令。如果要编译指定的工程可以直接使用这两个命令。在envsetup.sh脚本文件中还定义了一个m函数用来编译完整的Android源代码。因此如果要编译整个Android源代码,可以使用m代替make。

不仅下载Android源代码需要较长的时间,完全编译Android源代码也需要很长的时间。当执行make -j4命令后就进入了漫长的等待。当数小时后编译完成时,会在Android源代码根目录中生成一个out目录,所有编译的目标文件都在这个目录中。下一节将介绍out目录中包含了哪些东西。

4.1.5 out目录结构分析

out目录是存放编译Android源代码生成的目标文件的默认目录。图4-10左侧黑框中是out目录的结构。右侧黑框中的3个img文件是生成的主要的镜像文件,可以使用Android模拟器或真机中测试。

▲图4-10 out目录的结构

out目录中只有两个直接子目录:host和target。host目录主要是一些编译过程中需要的一些库和工具。如out/host/common/obj/JAVA_LIBRARIES目录中包含编译过程中需要的一些Java Library。out/host/linux-x86/bin目录是中Linux平台下运行的一些工具,如aapt、adb、aidl等。由于笔者在Ubuntu Linux下编译Android源代码,因此生成的这些工具都在linux-x86目录中。当在其他平台上编译Android源代码是会生成可在相应平台运行的工具。

注意

host目录中的库和工具都是中PC上运行的。也就是说,在编译Android源代码时会生成在两种平台运行的库和工具。一种就是直接运行在Android平台上的(基于ARM架构,存放在out/target目录中),另外一种就是运行在编译Android源代码的平台上的(如Ubuntu Linux),存放在out/host目录中。

out/target目录中的目标文件(.jar、.so、.img等)都可以在Android系统上运行。out/target目录包含两个直接子目录:common和product。其中,common主要包含了在Android系统上运行的程序需要的 Java 库(out/target/common/obj/APPS、out/target/common/obj/JAVA_LIBRARIES)、存放资源 ID 的R.java(out/target/common/R/android/R.java)、官方文档(out/target/common/docs/doc-comment-check)等内容。

out/target/generic目录包含了最终要使用的目标文件。其中包括3个重要的img文件:system.img、ramdisk.img和userdata.img。其中,out/target/generic/system目录就是system.img解压后的目录。Android系统中大多数程序都在system目录中,包括apk应用程序(out/target/generic/system/app)。ramdisk.img文件是out/target/generic/root目录的压缩包,userdata.img文件是out/target/generic/data的压缩包。

out/target/generic/system目录的结构和Linux根目录类似(其实Android就是Linux系统的变种),如bin、usr、lib等。当然,Android也增加了很多特有的目录。如apps目录用于存储apk应用程序, media目录用于存储与Android系统的多媒体相关的资源(如音频、启动动画等)。

4.1.6 将自己的APK作为Android内置程序发布

为了测试system.img文件,本节将一个自己生成的apk程序嵌入system.img文件中,这样在测试system.img文件时这个apk程序就和其他原生的应用程序一样出现在应用程序列表中,而且该程序无法卸载(除非获取root权限,并直接将apk文件删除)。

将apk程序嵌入system.img文件最简单的方式就是直接将apk文件复制到system/app目录下,然后再从system目录生成system.img文件。不过在system/app文件中不光有apk文件,还有与apk文件对应的odex文件。所有系统自带的程序都有与之对应的odex文件。如Calendar.apk文件有与之对应的Calendar.odex文件。

研究过apk文件结构的读者应该会很清楚,apk文件实际上是zip格式的,直接解压即可。在apk文件中有一个classes.dex文件。该文件是由Java和NDK源代码编译而成的Dalivk虚拟机格式的目标文件。在system/app目录中的odex文件就是将apk文件和classes.dex文件分离后产生的,但并不是将classes.dex文件直接复制出来改个名就行,而要重新使用mm或mmm命令编译源代码生成apk和odex文件。

在<光盘根目录>/sources/showdatetime 目录中提供了一个用于显示当前日期和时间的 Android应用程序。读者也可以使用自己的Android 工程,但要为该工程编写一个Android.mk 文件,并将Android.mk文件放在Android工程的根目录。showdatetime工程的Android.mk文件的内容如下:

        LOCAL_PATH := $(call my-dir)
        include $(CLEAR_VARS)
        LOCAL_MODULE_TAGS := optional
        LOCAL_SRC_FILES := $(call all-java-files-under, src)
        LOCAL_SDK_VERSION := current
        LOCAL_PACKAGE_NAME := showdatetime
        include $(BUILD_PACKAGE)
        include $(CLEAR_VARS)

将showdatetime目录复制到<Android源代码目录>/packages/apps目录中。然后在Linux终端进入<Android源代码目录>/packages/apps/showdatetime目录,执行mm命令编译showdatetime工程。如果编译成功,会在<Android源代码目录>/out/target/product/generic/system/app目录中生成showdatetime.apk和showdatetime.odex文件。这里的showdatetime.apk文件中并没有classes.dex文件。

注意

编写可以嵌入ROM里的Android应用程序时不能在布局、菜单等资源文件中为属性直接赋字符串值,应将字符串定义成资源,然后再引用。如android:text属性不能直接设置成“确定”,应使用<string name="ok">确定</string>定义成字符串资源,再使用@string/ok 设置 android:text 属性。如果直接用字符串常量设置属性,在使用mm编译工程时会抛出This attribute must be localized编译错误。表明在编译时强制要求字符串资源本地化。

4.1.7 用模拟器测试system.img文件

在4.1.4节已经完全编译了Android源代码,并在<Android源代码目录>/out/target/product/generic目录生成了 system.img 文件。但在上一节添加了一个 showdatetime 程序,因此需要重新生成system.img文件。

生成system.img文件需要一个mkyaffs2image命令行工具。该命令行工具可以将system目录打包成system.img文件。读者可以在<Android源代码目录>/out/host/linux-x86/bin目录找到该文件,也可以使用<光盘根目录>/resources/tools目录中的mkyaffs2image。

在Linux终端进入<Android源代码目录>/out/target/product/generic目录,执行如下的命令重新生成system.img文件(最好先将原来的system.img文件备份)。

    # mkyaffs2image system system.img

执行下面的命令可以使用模拟器测试刚生成的system.img文件(需要在PATH环境变量中添加<Android SDK根目录>/tools目录)。

    # emulator -avd myavd -system system.img -data userdata.img

其中 myavd 是一个已创建好的 AVD。执行上面的命令后,Android 模拟器就会使用新的系统镜像(system.img)和新的用户数据镜像(userdata.img)启动,我们会在程序列表中看到“显示当前日期和时间”程序。该程序是嵌入 Android 系统中的,因此无法通过常规方法卸载。除非将showdatetime.apk文件和showdatetime.odex文件删除,再重新生成system.img文件,并使用该文件启动Android模拟器才能删除该程序。

4.2 下载和编译Linux内核源代码

4.1节下载的Android源代码并没有包含Linux内核源代码,因此,要想使用Linux内核,必须要单独下载Linux内核源代码。Android并不能直接使用从www.kernel.org下载的Linux内核。而必须从Google提供的网址下载可以中Android中使用的Linux内核源代码。

4.2.1 下载Linux内核源代码

执行下面的命令可以下载最新的Linux内核源代码。

    # git clone https://android.googlesource.com/kernel/common.git

执行上面的命令可能需要较长的时间下载 Linux 内核。下载完后,在当前目录下会有一个common目录,进入该目录,执行下面的命令查看当前有哪些远程版本库。

    # git branch –a

读者可以根据版本库情况从版本库导出最新的 Linux 内核,例如,可以使用下面的命令导出Linux 3.0的内核。

    # git checkout -b android-3.0 remotes/origin/android-3.0

Google专门为Android模拟器提供了一个Linux内核(goldfish),可以使用相面的命令下载该内核。

    # git clone http://android.googlesource.com/kernel/goldfish.git

下载完后,进入goldfish目录,执行下面的命令导出goldfish。

    # git checkout -b android-goldfish-2.6.29 remotes/origin/android-goldfish-2.6.29

4.2.2 Linux内核源代码的目录结构

读者可以打开下载完的Linux内核目录,并对照表4-2来了解Linux内核中各个目录的含义。

表4-2 Linux内核源代码目录的含义

续表

4.2.3 安装Android内核的编译环境

在Ubuntu Linux下编译Linux内核需要安装交叉编译器。所谓交叉编译器就是指在A架构的CPU上编译可在B架构CPU上运行的程序。如在X86 架构上编译可在ARM架构(多数移动设备使用的CPU架构)上运行的程序。那么为什么要这么做呢?主要是因为编译器无法在目标平台上安装或目标平台性能较低,所以不得已才在性能较高限制较少的平台上进行编译。各位读者可以想象,如果在手机上编译庞大的Linux内核会是怎样的情景呢?

在第2章已经介绍过如何安装交叉编译器。在编译Linux内核之前,需要配置Linux内核源代码根目录下的Makefile文件。现在打开Makefile文件,找到ARCH和CROSS_COMPILE,并将这两个变量设置成如下的值。

        ARCH     ?= arm
        CROSS_COMPILE?= arm-none-linux-gnueabi-

其中 ARCH 变量表示将 Linux 源代码编译成可在 ARM 架构上运行的 Linux 内核。CROSS_COMPILE变量表示交叉编译器的前缀。读者可以打开交叉编译者的bin 目录,可以看到很多以arm-none-linux-gnueabi-开头的命令,如arm-none-linux-gnueabi-gcc、arm-none-linux-gnueabi-gdb等。由于编译Linux内核可能会使用到很多这样的命令,为了方便,Linux内核允许值只设置这些命令的前缀,就可以找到相应的编译命令。

经过笔者测试,goldfish 和最新的 Linux 内核使用第 2 章下载的交叉编译器都没问题。只有OK6410开发板带的Linux内核(Linux 2.6.36)使用第2章下载的交叉编译器生成的zImage文件无法正常启动开发板。为此在随书光盘中提供了一个较低版本的交叉编译器。压缩包路径如下:

<光盘根目录>/resources/compilers/arm-none-linux-gnueabi-arm-2008q3-72-for-linux.tar.bz2

读者可按如下步骤安装交叉编译器。

第1步:准备工作

将arm-linux-gcc-4.3.2.tgz文件和arm-none-linux-gnueabi-arm-2008q3-72-for-linux.tar.bz2文件复制到工作目录。

第2步:解压编译器

使用下面的命令解压上面的两个压缩文件。

    # tar zxvf arm-linux-gcc-4.3.2.tgz –C /
        # tar jxvf arm-none-linux-gnueabi-arm-2008q3-72-for-linux.tar.bz2 –C /

第3步:验证交叉编译器是否安装成功

    # ls /usr/local/arm -l

执行上面的命令后如果输出如图4-11所示的信息,表明交叉编译器安装成功。

▲图4-11 验证交叉编译器是否安装成功

第4步:安装libncurses5

    # apt-get install libncurses5-dev

安装libncurses5主要是为了配置内核(make menuconfig)。libncurses5可以为字符界面的Linux终端提供一个美观的界面。

在随书光盘中带的虚拟环境包含了已安装完的交叉编译器,路径如下:

/root/compilers/arm-none-linux-gnueabi

在虚拟环境中PATH变量是指向第2章下载的交叉编译器的,为了方便使用上面路径的交叉编译器,在/root/kernel/linux_kernel_2.6.36 目录中提供了一个 compile.sh 脚本文件,读者可以在虚拟环境中直接执行compile.sh脚本文件来编译Linux内核。

4.2.4 配置和编译Linux内核

假设Linux内核源代码目录是/root/linux_kernel,使用下面的命令可以编译Linux内核。

    # export PATH=/root/compilers/arm-none-linux-gnueabi/bin:$PATH
        # cd ~/linux_kernel
        # make clean
        # make

其中make clean命令用于清除大多数由编译生成的文件(.o、.ko等文件),但保留配置文件。除了make clean命令外,还有另外两个清除得更彻底的命令:make mrproper和make distclean。这两个命令的描述如下。

□ make mrproper:清除所有由编译产生的文件 + 清除配置文件。

该命令除了会清除所有由编译参赛的文件外,还会清除 scripts/basic、scripts/kconfig、include/config include/generated arch/x86/include/generated等目录的配置文件以及Linux内核源代码根目录的.config、.config.old文件。

□ make distclean:make mrproper +删除编辑器留下的备份文件和补丁文件。

该命令除了会清除make mrproper命令清除的所有文件外。还会从scripts/mod、scripts/selinux/genheaders、scripts/selinux/mdp等目录清除相应的备份文件和补丁文件以及.version 和include/linux/version.h文件。因此make distclean是最彻底的清除命令。

一般新下载的Linux源代码根目录都没有.config文件。而这个文件决定了编译生成的Linux内核由哪些功能和模块组成。因此,第一次拿到Linux内核时应先使用下面的4个命令中的一个配置Linux内核,并中Linux内核源代码根目录产生一个.config文件。当然,在配置Linux内核前一般需要确定自己的需求,例如,需要什么样的驱动模块、是否支持调试、支持的CPU类型、支持的文件系统等。

下面4个配置命令必须在Linux源代码根目录执行。

□ make config:该命令以文本形式配置Linux内核。执行该命令后会显示如图4-12所示的若干个询问项,要求用户做出回答。配置项后面中括号中的值是该配置项当前的值。如果中括号中有多个值,第一个值是默认值。如果不设置,直接按回车就会保留默认值。

▲图4-12 使用make config命令配置Linux内核

□ make menuconfig:以如图4-13所示的字符界面风格的菜单形式配置Linux内核。

▲图4-13 使用make menuconfig命令配置Linux内核

在如图4-13所示的配置界面后面带“--->“的菜单项说明还有子菜单。按回车键就可以进入子菜单。很多菜单项前面有中括号([ ])。中括号里面可以是“M”或“*”,也可以为空。有的菜单项前的中括号里只能设置“*”或空。中括号里的值表明当前菜单项对应的Linux内核组件嵌入Linux内核的方式。[M]表示以模块形式(.ko文件)动态安装在Linux内核中。[Y]表示Linux内核组件会嵌入Linux内核。当Linux内核成功启动后,这些组件会自动装载。如果为空表明该Linux内核组件不会包含到当前的Linux内核中。通过按“M”键可以将中括号中的值设置成“M”,按“Y”键可以将中括号中的值设置成“Y”。按空格键可以在[M]、[Y]和空格之间进行切换。最后退出设置界面后(通过<Exit>退出),如果修改了设置项,会显示如图4-14所示的界面,询问时候保持刚才的设置。如果选择“Yes”,则会根据刚才的设置重新生成.config文件。

▲图4-14 询问是否保持设置

□ make oldconfig:make oldconfig与make config类似,也是以字符界面逐项要求用户配置。但make oldconfig显示的要求用户参与的配置项要比make config少。大多数配置项都会给出默认值,并不需要用户进行配置。只有后面带(NEW)的配置项才需要用户配置(这些配置项都是当前Linux内核版本新加入的功能),如图4-15所示。

▲图4-15 使用make oldconfig命令配置Linux内核

□ make xconfig:以图形界面形式配置Linux内核。由于make xconfig命令显示的图形界面使用了QT的共享库,因此,如果执行make xconfig命令的Ubuntu Linux未安装QT,可以使用下面的命令安装QT。

    # apt-get install libqt4-dev

安装完QT后,执行make xconfig命令后,会显示如图4-16所示的界面。在界面中可以通过上面的“保存”安装保存设置,也可以使用“打开”按钮打开其他的配置文件。

▲图4-16 使用make xconfig命令配置Linux内核

在配置中有一项很重要,就是交叉编译器的签名。在上一节已经在配置交叉编译器时设置了Makefile文件的CROSS_COMPILE变量,该变量的值就是交叉编译器的前缀。我们也可以通过上面介绍的4个命令来设置交叉编译器的前缀。笔者推荐使用make menuconfig命令,因为执行该命令并不需要安装额外的Library,而且可以用可视化方式配置Linux内核。

执行make menuconfig命令,进入“General setup”菜单项的子菜单。选择“Cross-compiler tool prefix)菜单项,如图4-17所示。

▲图4-17 General setup子菜单

按回车键后,会弹出一个输入框,要求输入交叉编译器的前缀,如图 4-18 所示。输出正确的前缀后,退出保存设置即可。

▲图4-18 设置交叉编译器的前缀

在Linux内核中有很多预定义的配置文件,但文件名不叫.config,而且也不在Linux源代码根目录中。这些配置文件分布在arch/<cpu>/configs目录中。其中<cpu>表示不同的CPU架构。例如,与arm架构有关的配置文件就在arch/arm/configs目录中。在configs目录中包含了若干个配置文件,每个配置文件都细分了ARM CPU的类型,例如,本书使用的是S3C6410 ARM 11,因此,可以将s3c6400_defconfig配置文件复制到Linux内核源代码根目录,并改名为.config,然后,再使用make menuconfig命令配置Linux内核。这样可以省去配置所有配置项的麻烦。

在结束所有的设置后,执行make命令编译Linux内核。如果成功编译了Linux内核,会显示如图4-19所示的信息。

▲图4-19 成功编译Linux内核

成功编译 Linux 内核后,会在<Linux 内核源代码根目录>/arch/arm/boot 目录生成一个 zImage文件。该文件就是Linux内核的二进制版本。可以直接使用zImage安装到开发板上(见下一章的介绍),也可以使用下面的命令在Android模拟器中测试Linux内核(需要使用由goldfish编译而成的zImage文件)。

    # emulator –avd myavd -kernel /root/kernel/goldfish/arch/arm/boot/zImage

其中myavd是一个AVD名称。

4.3 小结

Android移植主要就是Linux内核的移植。而Linux内核移植主要是Linux驱动的移植。所以为了开发和测试Linux驱动。需要在Ubuntu Linux下搭建两套开发环境:Android应用程序开发环境和Linux内核开发环境。本章的主要目的就是介绍如何搭建这两种开发环境。其中Linux内核开发环境是本章的重点。为了方便读者,在随书光盘中已带了本书需要的全部工具和源代码。如果读者不想重新配置开发环境,在随书光盘中还为读者提供了一个VMWare虚拟机环境。系统为Ubuntu Linux 11.10,登录用户名是root,登录密码是androidkernel。所有的环境都配置完了,读者可以直接在上面做实验。