零蚀
前言
-
前言
-
这两周都在折腾 如何将源码编译的的事, 并且走了很多弯路,真实痛苦的过程,因为下载和编译时间都很漫长,在空闲时间里,我总结了几点,
- 首先,源码的编译是一个 一百个人编译,一百个哈姆雷特的故事(bug),是一个耗时且艰巨的任务,要有足够耐性。
- 第二,像init,AMS,WMS,System server,Binder等这些知识点首先要了解是什么,但不能深入,因为日常很难用到,很容易忘,有时间多看看View等实用的低层实现&原理,这样在工作上会更舒服一些。
-
平心静气面对一切,一切困难都不在是困难。所以先回顾一下我失败的案例 0.0
-
-
不成功的案例回顾
- 首先需要获取android源码,在百度上获取的源码资源总是会有各种问题(因为编译和网上都是三步搞定,而这里确实bug连着bug),所以按照官网的步骤来,🔗 清华大学开源软件镜像站-Android 镜像,首先获取repo,由于google的很难搞,所以直接用🔗 repo,当我们下载好了repo并设置好配置之后。我们可以下载自己的源码了。
# 构建manifest repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-7.0.0_r6 # 同步代码(这里拉代码最好再创将一个文件夹放代码) repo sync
-
在使用mac的过程中我们需要注意几点,首先,需要分区,因为编译源码需要大小写区分的格式(分区大小要在100G及以上,一般6.0的源码已经30G了,如果编译的话100G不一定够)。其次我们下载之后源码编译的时候对macos是有需求的,这个要看文档 源码/build/core/combo/mac_verison.mk中,将"mac_sdk_versions_supported"的版本改成你的MacOS的版本,然后这里我还遇到一些源码中有问题,这里跟着提示改就行,然后就是Android7.0提示需要jdk1.8,那只能安排上了。然后编译中还是报了一些cpp过时导致的问题,这个是由于我们的xcode版本太高了,所以我们要降低我们的xcode版本(我这里下载了xcode 8.3),对应的下载链接为**[🔗 xcode 各版本下载链接],然后也有一些问题具体的操作与解决的博客,大佬说的非常详细,就不再赘述了,[🔗 Android 源码编译mac]**
-
关于这里面有个jdk的tool.jar找不到的问题,上述博客可能有一个地方没有描述,首先我试了下载1.8和1.7的jdk发现都找不到,然后看看报错的源头,发现报错的是
build/core/find-jdk-tools-jar.sh
,这里面负责寻找tool.jar,它里面代码很简单,它会在$ANDROID_JAVA_HOME
这个路径下找tool.jar,如果找不到的话就去一个默认的路径,所以我们只要在.bash_profile
设置一个全局变量ANDROID_JAVA_HOME
,将jdk的tool.jar,对应的父文件夹路径传进去就能避免这个错误。(反正大体上都是看报错的源头,然后找对应的文件,然后根据里面的c 或 py 或 sh 文件来定位这问题发生的原因) -
结果:依旧报错找不到系统文件和发生了python函数调用冲突问题。
-
源码工具(不推荐)
- 除了源码之外,我们还需要看源码的工具,windows提倡使用
Source Insight
,这里我打算使用的是sublime text
,这里简单说一下sublime 设置跳转的功能,首先我们下载一个已经处理过的(敏感词汇)sublime,然后需要安装package control,这里我们直接command+shift+p,选择install package control
,等待下好之后,我们再敲快捷键可以看到下列列表。我们输入install package
,我们可以看左下角的下载进度。
-
下载好
install package
后我们开始下载ctags,如果没有自己弹出弹窗列表,我们可以重新点击package control
(这个下载后在preference中可以找到),重新点击install package
,就会弹出列表。
-
然后我们来设置点击函数的跳转方式。按照下面的目录,然后我们可以看到打开了一个文件
Default.umblime-moucemap
,拷贝其中的内容,然后在按照下列操作打开Mouse Binding-User,将内容拷贝进去,如果里面有“ctrl+shif”需要改为“command”。
- 除了源码之外,我们还需要看源码的工具,windows提倡使用
源码编译
-
步骤
-
首先下载问题,下载是很容易出问题的,无论你下载什么版本,里面总有几个或者几十个文件是被qiang掉的,所以,无奈,等了半天,下的源码都是有问题的,所以我们去科学上网?当然拒绝的,所以我就放弃自己下载了,因为网上早有成片的热心群众上传了源码,所以找找,这里我提供一下我找到的Android8.0源码。【android 8 链接: 】 密码: rov9
-
其次不推荐用mac,虽然网上也有用mac完成的,但是对于不同的Android源码版本,不仅是要注意python版本(由于一些源码只支持py2.7,所以我切换后,还是不行,即使卸载py3,还是会报py不兼容的函数异常),java版本(android 7 以下对应 java 8 以下),你更要注意xcode版本,由于mac系统可能不兼容之前的xcode所以你懂的,你可能要切换你xcode toolchain文件内容,但是这依旧不能减少你的报错,你做了这一切还是大概率会发生报错。并且mac不支持大小写,要自己开一个分区,并且你编译一个源码最好要腾出一个200G及以上的分区(250G的同学就直接放弃吧),所以建议ubuntu(win没有试过,不做评价)。
-
首先我们需要下载一些依赖和工具,当然这些依赖不一定全,具体出问题,后面再看。网上太多版本,而且对应的版本也五花八门,但是真的操作了还是可能报错,所以具体问题具体对待。
sudo apt-get update sudo apt-get install libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-dev g++-multilib sudo apt-get install -y git flex bison gperf build-essential libncurses5-dev:i386 sudo apt-get install tofrodos python-markdown libxml2-utils xsltproc zlib1g-dev:i386 sudo apt-get install dpkg-dev libsdl1.2-dev libesd0-dev sudo apt-get install git-core gnupg flex bison gperf build-essential sudo apt-get install zip curl zlib1g-dev gcc-multilib g++-multilib sudo apt-get install libc6-dev-i386 sudo apt-get install lib32ncurses5-dev x11proto-core-dev libx11-dev sudo apt-get install libgl1-mesa-dev libxml2-utils xsltproc unzip m4 sudo apt-get install lib32z-dev ccache sudo apt-get install libssl-dev sudo apt-get install mesa-utils sudo apt install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev libgl1-mesa-dev libncurses5 libxml2-utils python xsltproc unzip
- 如果你只是想看源码,不需要跑系统,可以选择不整体编译。比如,我只是想在android源码搜索类或者引用关系,就不需要编译,如果你想改系统上的代码,并让你改的系统显示在硬件上(Android Room)的话就要进行编译。先说说如何不整体编译,直接看源码(可以点击类名跳转的那种),在源码的根目录下进行如下操作,其实这里主要就是为了生成Android.ipr,用Android studio 打开这个文件,就可以了。
make idegen development/tools/idegen/idegen.sh
- 然后我们来说说ROOM的这种。
# 如果是重新编译需要走下买的clobber来清理编译过程中的所有中间产物(clean只是清理out的结果) make clobber # 这里切换到源码根目录 . build/envsetup.sh # 这个envsetup.sh里面有一个方法是lunch,当你调用这个脚本后才可以使用这个函数来确定你要编译的类型。(反正我选arm,arm64,x86-64,mini都遇到问题模拟器黑屏问题,mini是编译直接报错,应该还需要额外配置) lunch # 这里多出的一步是针对Android 8/9出现的问题避免,因为服务分配的内存不够所以一定会报错的,这里不设置可能在1-10%内报错 export LC_ALL=C # 这里的-c时指的core,结果可用ulimit -a查询(89%报错时候解决) ulimit -c unlimited # Set ccache 由于这里没有设置缓存在98%卡死 prebuilts/misc/linux-x86/ccache/ccache -M 50G # Jack server configuration export JACK_SERVER_VM_ARGUMENTS="-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx6g" ./prebuilts/sdk/tools/jack-admin kill-server ./prebuilts/sdk/tools/jack-admin start-server # make 默认是单核处理 make -j4
- 这里注意配置的地方,这里mac上的parallels默认是不支持模拟器的,如果想在虚拟机中开启模拟器需要打开nested选项(CPU和内存–>高级设置)。
- 在编译的时候,我们可能还是遇到以下的问题,一般我们直接搜nijia的报错内容,网上会告诉你是内存的问题,要你再扩大内存,其实这个问题的重点是
No such file
,为什么会没有file,是因为你没有下这个工具,当然就找不到了,所以,如果有这种提示出现,大概率会提示你源码缺了什么东西。先姑且相信你的源码没有缺斤少两,去apt-get下找找,有没有对应的东西,有的话下一个,就ok了。 (当然如果是显示没有什么架构之类,或者ubuntu中编译找不到toolschain这种错误大概率是你的build文件路径里的envsetup.sh的层级结构有问题,导致在sh的文件层级中找不到源码build文件中的资源)
// 这里省略一大堆nijia,clang++的报错,只看最后几行 ... libncurses.so.5: cannot open shared object file: No such file or directory ... ninja: build stopped: subcommand faile ninja failed with: exit status 1
-
⚠️在完成编译的时候我们要记住几点,首先要按照上面流程走完,不要遗漏,其次,一定不要乱改动文件的内容,我们可能在网上看到有些报错是要改内容的,但是源码内容错了,所有的编译都会有,不会只有你有,所以源码是没问题的,第三,不要用复制拷贝的源码来编译,因为,里面有些文件名有特殊符号,可能冲突,所以要哪里解压哪里编译,不然也回报上面的问题,不过不会说那文件没找到,是直接报错了。
-
最后查阅很多的博客最后一步都是运行emulator来显示,但是这里使用emulator出现了黑屏,无论怎么也打不开,首先,这个问题我验证了一下发现,这里emulator是用的
prebuilts/android-emulator/linux-x86_64/emulator
目录的,我们应该用的是编译后的out文件下的emulator,但是out中没有找到emulator , 所以模拟器是黑的,可能要自己后续添加一个适合的emulaotr。prebuilts
文件中的是编译前就存在的,且这个文件中的emulator是3.10以下的版本,编译后out中的kernel文件中的kernel-qemu-armv7是只支持3.10+版本,所以肯定是不行。所以这个暂时延迟,后面有时间再捣鼓捣鼓。
-
启动
-
Android系统启动
- Android源码是非常大的,这里我用的是8.0 ,首先我们熟悉一下,Android系统启动前都做了什么,作为Android程序员,我们都用过adb shell,我们常见的无非通过这种方式来指令控制一个安卓机器,常见的文件有data,storage等,这些目录无需多言,(我们在看内部文件时候需要7.0以下的系统,不然很多文件我们是没有权限访问的,这里我选择的是5.0)。我们看一下Android系统中的启动文件。
-
这里有一个init文件,这就是系统初始化的文件,在Android程序启动的时候会有一个引导程序
Bootloader
来引导Android系统启动,为什么需要这个引导程序呢,因为我们在启动的时候,启动了两个东西一个是硬件,一个是软件,但是刚通电这两者并没有达到最好的状态,如果强行结合,强扭的瓜就不甜了,就会有很多问题出现,所以这时候,BootLoader
引导程序就来了,他会将系统的软硬件引导到一个合适的位置,这样来为系统调用配置一个正确的环境。 -
在引导程序结束之后接下来会走到
system/core/init/init.cpp
代码,其中有一行代码是调用了init.rc , 那么 这个文件是干嘛的呢,我们可以看见这个文件就在"adb shell"最外层的目录中。
parser.ParseConfig(”/init.rc") ;/
- 我们可以看看
init.rc
中都做了什么,我们用cat init.rc
来查看一下init.rc文件内容。我们可以看到了,这里挂载了一些系统,创建了一些基础的文件夹,设置了一些基础的权限,启动了一些服务,还有设置了守护线程等等。
# Create cgroup mount point for memory mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000 mkdir /sys/fs/cgroup/memory 0750 root system ..... # create basic filesystem structure mkdir /data/misc 01771 system misc mkdir /data/misc/adb 02750 system shell .... # Memory management. Basic kernel parameters, and allow the high # level system server to be able to adjust the kernel OOM driver # parameters to match how it is managing things. write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 chown root system /sys/module/lowmemorykiller/parameters/adj ..... # Permissions for System Server and daemons. chown radio system /sys/android_power/state .... ## Daemon processes to be run by init. ## service ueventd /sbin/ueventd .... on nonencrypted class_start main class_start late_start
- 上述代码中各有一个
on nonencrypted
我放在了最后,这里有一个命令叫做class_start 它对应着do_class_start函数,这个函数启动了一个服务,叫做main
,这个main函数又是在哪呢,我们再打开cat init.zygote32.rc
这个服务的初始化文件,可以发现这里定义了一个服务叫做zygote,然后将它命名为main
,所以这个服务是为了zygote(下面的代码中还显示如果有些服务停止了,zygote会重启它们)。
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd
-
什么是zygote,他是linux系统中第一个创建出来的init进程,我们的app上所有的进程都是由zygote分裂而来,而zygote也名副其实(受精卵),zygote创建之后会进行jvm的创建等等。
-
zygote 被init启动是在
app_main.cpp
中,zygote的进程都是通过fock自己来创建子进程,像SystemServer进程。
-
服务
- 除上述的内容之外,我们经常会遇到一种情况,比如我对手机进行了某些操作,当我关机再进来的时候那些操作还会进行同样的流程,不需要我再手动设置一遍,用window的小伙伴肯定知道,在设置中,有个注册表,还有属性服务,在注册表中其实记录的就是我们村下的一些属性值,当某个用户账号启动后,init就会启动服务属性来调用这些属性值,这部分的功能代码是
property_service.cpp
。
- 除上述的内容之外,我们经常会遇到一种情况,比如我对手机进行了某些操作,当我关机再进来的时候那些操作还会进行同样的流程,不需要我再手动设置一遍,用window的小伙伴肯定知道,在设置中,有个注册表,还有属性服务,在注册表中其实记录的就是我们村下的一些属性值,当某个用户账号启动后,init就会启动服务属性来调用这些属性值,这部分的功能代码是
小结
-
为什么不继续探索mac上 无法编译的问题,这里主要是硬件内存不允许的不可抗拒原因,其次,即使以后有这个条件,我想我也不会再这么做,因为,最终目的是源码,在这上面已经耗损了足够的时间了,其实已经很浪费时间了,所以 — 不值得。
-
我觉得我算很有耐心的了,在编译安卓源码过程中,5%崩溃,7%崩溃,30%崩溃,52%崩溃,89%崩溃,98%卡死(两次),每次失败需要重开,完整编译3或着4小时以上,[汗]还有比这更糟心的嘛!尤其是最后98%卡着不动,当时我真的有点崩溃,89%崩溃时,起码有迹可循,但是卡死,那时候我已经解除了一些限制,想不到还有什么缺失,在合理推断后解决了,还好第三次安全过了。
-
关于9.0的资源和文章可以参照这个大佬的博客Android源码开发篇 9.0/10.0/11.0源码下载编译