Android设备的启动必须经历3个阶段,即Boot Loader、Linux Kernel和Android系统服务,默认情况下它们都有各自的启动界面。严格来说,Android系统实际上是运行于Linux内核之上的一系列“服务进程”,并不算一个完整意义上的“操作系统”。这些进程是维持设备正常工作的关键,而它们的“老祖宗”就是init。
init的PID值为0,它通过解析init.rc脚本来构建出系统的初始运行形态。
init.rc实例分析
export PATH /sbin:/system/sbin:/system/bin #响应boot事件,设置系
统环境变量
export LD_LIBRARY_PATH /system/lib #响应boot事件,设置库路径
mkdir /dev #创建/dev目录
mkdir /proc #创建/proc目录
mkdir /sys #创建/sys目录。这时还没有超出on boot的作用范围,下同
mount tmpfs tmpfs /dev
mkdir /dev/pts
mkdir /dev/socket
mount devpts devpts /dev/pts
mount proc proc /proc
mount sysfs sysfs /sys #以上几行用于挂载文件系统以及创建新的目录
write /proc/cpu/alignment 4 #打开文件,并写入数值
ifup lo #建立lo网络连接
hostname localhost #设置主机名
domainname localhost #设置域名
mount yaffs2 mtd@system /system
mount yaffs2 mtd@userdata /data
import /system/etc/init.conf #导入另一个配置文件
class_start default #启动所有标志为default的服务
service adbd /sbin/adbd #启动adbd服务进程
user adb
group adb # Adbd是android debug bridge daemon的缩写,它为开发者与设
备之间建立了一条通道。
service usbd /system/bin/usbd -r
user usbd
group usbd
socket usbd 666 #启动usbd服务。
service zygote /system/bin/app_process -Xzygote /system/bin --
zygote
socket zygote 666 #启动zygote服务。Zygote是系统的“孵化器”,负责生产
“进程”
on device-added-/dev/compass
start akmd #当增加了/dev/compass节点后,启动akmd服务
on device-removed-/dev/compass
stop akmd #当移除了/dev/compass节点后,停止akmd服务
service akmd /sbin/akmd
disabled
user akmd
group akmd #因为这里对akmd服务使用了disabled选项,所以系统不会主动去启
动它。而是要等到上面
#描述的/dev/compass节点出现时,才显式地调用此服务
作为Android系统的第一个进程,init将通过解析init.rc来陆续启动其他关键的系统服务进程——其中最重要的就是ServiceManager、Zygote和SystemServer。
Android的“DNS服务器”——ServiceManager
ServiceManager是Binder机制中的“DNS服务器”,负责域名(某Binder服务在ServiceManager注册时提供的名称)到IP地址(由底层Binder驱动分配的值)的解析。
ServiceManager是在Init.rc里描述并由init进程启动的。如下所示:
/*sytem/core/rootdir/Init.rc*/
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
可以看到,servicemanger是一个Linux程序。它在设备中的存储路径是/system/bin/service- manager,源码路径则是/frameworks/native/cmds/servicemanager。
ServiceManager所属class是core,其他同类的系统进程包括ueventd、console(/system/bin/sh)、adbd等。根据core组的特性,这些进程会同时被启动或停止。另外,critical选项说明它是系统的关键进程——意味着如果进程不幸地在4分钟内异常退出超过4次,则设备将重启并进入还原模式。当ServiceManager每次重启时,其他关键进程如Zygote、media、surfaceflinger等也会被restart。
“孕育”新的线程和进程——Zygote
Zygote这个词的字面意思是“受精卵”,因而可以“孕育”出一个“新生命”。正如其名所示,Android中大多数应用进程和系统进程都是通过Zygote来生成的。
以init.zygote64.rc为例,相关代码如下:
service zygote /system/bin/app_process64 -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
从上面这段脚本描述可以看出:
ServiceName: zygote
Path: /system/bin/app_process64
Arguments: -Xzygote /system/bin --zygote --start-system-server
Zygote所属class为main,而不是core。和其同class的系统进程有netd、debuggerd、rild等。
从zygote的path路径可以看出,它所在的程序名叫“app_process64”,而不像ServiceManager一样在一个独立的程序中。通过指定–zygote参数,app_process可以识别出用户是否需要启动zygote。app_process又是何方神圣呢?先来看看它的Android.mk:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
app_main.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
liblog \
libbinder \
libandroid_runtime
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
include $(BUILD_EXECUTABLE)
上述是构建Multilib(64位和32位系统)的一个编译脚本范例。其中LOCAL_MULTILIB用于指示你希望针对的硬件平台架构。可选值如下:“32” :表示只编译32位版本。“64”: 表示只编译64位版本。 “both” :表示同时编译32位和64位的版本。“” :表示由系统根据其他变量来决定要编译的目标。
从上面的描述可以很明显地看到,app_process其实扮演了一个类似于“壳”的角色,那么它容纳了哪些“内容”呢?
/*frameworks/base/cmds/app_process/App_main.cpp*/
int main(int argc, char* const argv[])
{…
AppRuntime runtime(argv[0], computeArgBlockSize(argc,
argv));//Android运行时环境
…
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) { //当前进程是否用于承载
zygote
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {//
是否需要启动system server
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
…
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit",
args);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit",
args);
} else {
…
}
}
这个函数用于解析启动app_process时传入的参数,具体如下。
–zygote: 表示当前进程用于承载zygote。
–start-system-server:是否需要启动system server。
–application:启动进入独立的程序模式。
–nice-name:此进程的“别名”。
对于非zygote的情况下,在上述参数的末尾会跟上main class的名称,而后的其他参数则属于这个class的主函数入参;对于zygote的情况,所有参数则会作为它的主函数入参使用。
在我们这个场景中,init.rc指定了–zygote选项,因而app_process接下来将启动“ZygoteInit”并传入“start-systemserver”。之后ZygoteInit会运行于Java虚拟机上,为什么?
原因就是runtime这个变量——它实际上是一个AndroidRuntime对象,其start函数源码如下:
/*frameworks/base/core/jni/AndroidRuntime.cpp*/
void AndroidRuntime::start(const char* className, const char*
options)
{
…
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {//启动虚拟机
return;
}
onVmCreated(env);//虚拟机启动后的回调
…
}
我们这里假设VM可以成功启动,并进入ZygoteInit的执行中:
/*frameworks/base/core/java/com/android/internal/os/ZygoteInit.ja
va*/
public static void main(String argv[]) {
try {…
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;//需要启动System
Server
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList =
argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName =
argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command
line argument: " + argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list
supplied.");
}
registerZygoteSocket(socketName);//注册一个Socket
preload();//预加载各类资源
…
if (startSystemServer) {
startSystemServer(abiList, socketName);//后面对此
函数进行详细分析
}
Log.i(TAG, "Accepting command socket connections");
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
ZygoteInit的主函数并不复杂,它主要完成两项工作:
/*
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
*/
private static boolean startSystemServer(String abiList, String
socketName)
throws MethodAndArgsCaller, RuntimeException {
…
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--
setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,
1032,3001,3002,3003,3006,3007",
"--capabilities=" + capabilities + "," +
capabilities,
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities); //果然需要
fork一个新进程
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
if (pid == 0) {//子进程,即System Server所承载进程
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
handleSystemServerProcess(parsedArgs);//启动各System
Server
}
return true;
}
forkSystemServer在内部利用UNIX的fork机制创建了一个新进程;而这个“新生儿”(即pid == 0分支)会在随后的执行过程中通过handleSystemServerProcess来启动各种支撑系统运行的System Server。
System Server的启动是在startSystemServer中完成的。Zygote首先会利用Zygote.forkSystemServer来孵化出一个子进程,然后在pid==0的分支中调用handleSystem ServerProcess,后者在函数的末尾又会进一步调用RuntimeInit.zygoteInit:
/*frameworks/base/core/java/com/android/internal/os/RuntimeInit.j
ava */
public static final void zygoteInit(int targetSdkVersion,
String[] argv,
ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {…
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}
函数zygoteInit通过3个方面来完成初始化,分别是:
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,
1009,1010,1018,1032,3001,3002,3003,3006,3007",
"--capabilities=" + capabilities + "," +
capabilities,
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
换句话说,startClass对应的就是com.android.server.SystemServer。因而applicationInit最终将调用
main@SystemServer:
public static void main(String[] args) {
new SystemServer().run();
}
经过上面的初始化后,程序现在会有两个分支,其一是nativeZygoteInit主导的本地系统服务的启动;另一个则是applicationInit负责的Java层系统服务的启动。
Zygote和System Server的启动流程
Android的“系统服务”——SystemServer
SystemServer是Android进入Launcher前的最后准备。 一旦我们在init.rc中为Zygote指定了启动参数–start-system-server,那么ZygoteInit就会调用startSystemServer来进入SystemServer。而且系统服务又分别分为Java层和本地层两类。其中Native层服务的实现体在android_servers中,需要在run@SystemServer中首先通过System.loadLibrary(“android_servers”)加载到内存中才能使用。而nativeInit则负责为启动本地层服务而努力。
再回到Java层来看一下这类系统服务是如何管理的。从代码中可以看到,这部分Server又可细分为3类,如下所示:
/*frameworks/base/services/java/com/android/server/SystemServer.j
ava*/
private void startBootstrapServices() {
mInstaller =
mSystemServiceManager.startService(Installer.class);
mActivityManagerService =
mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceMan
ager);
mPowerManagerService =
mSystemServiceManager.startService(PowerManagerService.class);
mActivityManagerService.initPowerManagement();
mDisplayManagerService =
mSystemServiceManager.startService(DisplayManagerService.class);
mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR
_DEFAULT_DISPLAY);
// Only run "core" apps if we're encrypting the device.
String cryptState = SystemProperties.get("vold.decrypt");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only
parsing core apps");
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core
apps");
mOnlyCore = true;
}
// Start the package manager.
Slog.i(TAG, "Package Manager");
mPackageManagerService =
PackageManagerService.main(mSystemContext, mInstaller,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
Slog.i(TAG, "User Service");
ServiceManager.addService(Context.USER_SERVICE,
UserManagerService.getInstance());
// Initialize attribute cache used to cache resources
from packages.
AttributeCache.init(mSystemContext);
// Set up the Application instance for the system process
and get started.
mActivityManagerService.setSystemProcess();
}
上面的函数中被调用最多的语句是mSystemServiceManager.startService。目前所有System
Service都统一由SystemServiceManager来管理。System Service Manager首先会启动Installer,这是为了让Installer可以优先完成初始化,并完成关键目录(如/data/user)的创建。这些都是其他服务可以顺利启动的先决条件。接下来启动的系统服务是ActivityManagerService,在AMS之后相继启动的服务包括电源管理Power Manager、Display Manager、PackageManager等,最后调用setSystemProcess来添加进程相关的服务,如meminfo、gfxinfo、dbinfo、cpuinfo等,从而完成最核心部分系统服务的启动。
从物理设备的角度来看,External Storage由如下几种类型组成:
on post-fs-data
…
start vold
Vold在启动以后,会通过NETLINK和内核取得联系,并根据后者提供的event来构建存储设备管理系统。和以往版本不同的是,Vold的配置文件不再是vold.fstab,而变成了/fstab.
AOSP/device/fugu/fstab.fugu
Android 6.0及以后版本中,根据设备具体情况不同主要有如下几种典型配置:
(1)Emulated primary only
即只有Emulated Storage的情况,此时fstab.device的配置范例如
下:
/devices//xhci-hcd.0.auto/usb auto auto defaults
voldmanaged=usb:auto
(2)Physical primary only
即只有一个外置物理存储设备的情况,此时fstab.device的配置范
例如下:
/devices/platform/mtk-msdc.1/mmc_host* auto auto defaults
voldmanaged=sdcard0:auto,encryptable=userdata,noemulatedsd
(3)Emulated primary, physical secondary
有两个外置的物理存储设备,它们会被分别设定为primary和secondary,此时fstab.device的配置范例如下:
/devices/platform/mtk-msdc.1/mmc_host* auto auto defaults
voldmanaged=sdcard1:auto,encryptable=userdata
Vold在启动过程中会通过process_config函数来处理fstab配置文件,并把它们存储在VolumeManager的全局变量中。后续当收到内核的NetlinkEvent (add)时,VolumeManager再在handleBlockEvent中根据
规则判断本次事件是否和之前记录的fstab配置相匹配——如果答案是肯定的话,则新创建一个Disk对象来管理,并将它们统一添加到mDisks中,如图所示。
我们不难发现,Emulated Storage所需的存储空间来源于设备的data分区。换句话说,Emulated Storage的存储空间和data分区是共享存储区域的。Emulated Storage当然也是由Volume Manager来统一管理的,如下所示:
/*system/vold/VolumeManager.cpp*/
int VolumeManager::start() {…
CHECK(mInternalEmulated == nullptr);
mInternalEmulated =
std::shared_ptr<android::vold::VolumeBase>(
new android::vold::EmulatedVolume("/data/media"));
mInternalEmulated->create();
return 0;
}
VolumeManager::start会被vold的main函数调用,因而从Vold的角度来看所有Android设备都是带有Emulated Storage的,只不过最终是否需要执行mount操作则由MountService来决定。从EmulatedVolume构造函数的参数可以看到,它在data分区中对应的路径是/data/media。VolumeManager的全局变量mInternalEmulated用于记录系统的EmulatedStorage。
接下来mInternalEmulated->create()除了给Storage创建运行环境外,还会向MountService发送一个VolumeCreated的消息,并将自身的状态迁移到kUnmounted。MountService收到这一信息后,会根据系统的实际情况决定是否挂载这个Storage——如果答案是肯定的话,那么它会回应一个mount指令给vold,而后者对此的处理过程中会进一步调用到doMount函数——这个函数最关键的步骤之一是fork一个新进程,用于运行/system/bin/sdcard。
例如domount @EmulatedVolume.cpp中的如下代码段:
if (!(mFusePid = fork())) {
if (execl(kFusePath, kFusePath,
"-u", "1023", // AID_MEDIA_RW
"-g", "1023", // AID_MEDIA_RW
"-m",
"-w",
mRawPath.c_str(),
label.c_str(),
NULL)) {
PLOG(ERROR) << "Failed to exec";
}
LOG(ERROR) << "FUSE exiting";
_exit(1);
}
变量kFusePath指向的是“/system/bin/sdcard”,我们可以通过execl系统调用将其启动起来。Sdcard daemon对应的源代码目录是AOSP/system/core/sdcard。
Sdcard daemon属于Fuse Service。Fuse的全称是“Filesystem in Userspace”,即在用户态实现的一种File System。它的典型框架如图所示
当使用者(左半部分)希望访问FUSE文件系统时,这一请求会经过Kernel的VFS首先传递给FUSE对应的驱动模块,然后再通知到用户层的fuse管控程序(例如这个场景中的sdcard)。后者处理请求完成后会将结果数据返回给最初的调用者,从而完成一次交互过程。可见与传统的文件系统相比,FUSE文件系统因为处理层次较多,所以在效率上注定会存在不足的地方。不过“瑕不掩瑜”,FUSE文件系统的灵活性依然为其获得了广泛的应用
了解了FUSE文件系统后,我们再来看sdcard daemon是怎么做的。简单来说它会执行以下几个核心操作。
adb shell pm list permissions -g -d
Runtime Permission权限管理方式的一种很重要的特性就是要求应用程序的权限可以在运行过程中进行动态调整,而且不能导致应用程序的重启。这其中就涉及Package Manager Service、Activity
Manager Service、Zygote等多个系统服务,我们按照顺序逐一阐述。
首先需要关注的是应用程序启动时的初始化权限处理,此时AMS在startProcessLocked中会做如下处理:
/*frameworks/base/services/core/java/com/android/server/am/Activi
tyManagerService.java*/
private final void startProcessLocked(ProcessRecord app, String
hostingType,
String hostingNameStr, String abiOverride, String
entryPoint, String[] entryPointArgs) {…
try {
checkTime(startTime, "startProcess: getting gids from
package manager");
final IPackageManager pm =
AppGlobals.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName,
app.userId);
MountServiceInternal mountServiceInternal =
LocalServices.getService(
MountServiceInternal.class);
mountExternal =
mountServiceInternal.getExternalStorageMountMode(uid,
app.info.packageName);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
…
Process.ProcessStartResult startResult =
Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags,
mountExternal,
app.info.targetSdkVersion, app.info.seinfo,
requiredAbi,
instructionSet, app.info.dataDir,
entryPointArgs);
mountExternal是由MountService提供的,其代码实现如下所示:
public int getExternalStorageMountMode(int uid, String
packageName) {
// No locking - CopyOnWriteArrayList
int mountMode = Integer.MAX_VALUE;
for (ExternalStorageMountPolicy policy : mPolicies) {
final int policyMode = policy.getMountMode(uid,
packageName);
if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
mountMode = Math.min(mountMode, policyMode);
}
if (mountMode == Integer.MAX_VALUE) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
return mountMode;
}
这个函数的处理逻辑是:遍历所有的Policy规则,并从中挑选出数值最小的MountMode——按照由小而大的顺序排列,它们依次是:MOUNT_EXTERNAL_NONE、MOUNT_EXTERNAL_DEFAULT、
MOUNT_EXTERNAL_READ和MOUNT_EXTERNAL_WRITE。应用程序的MountMode的具体取值主要由PMS的checkUidPermission来判断,而后者则会根据APP在AndroidManifest中申请WRITE_MEDIA_STORAGE、READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE等权限的情况来给出结论
当Zygote孵化出一个应用程序进程后,会在MountEmulatedStorage中对Mount Mode做进一步处理,核心实现如下:
if (unshare(CLONE_NEWNS) == -1) {
ALOGW("Failed to unshare(): %s", strerror(errno));
return false;
}
String storageSource;
if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
storageSource = "/mnt/runtime/default";
} else if (mount_mode == MOUNT_EXTERNAL_READ) {
storageSource = "/mnt/runtime/read";
} else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
storageSource = "/mnt/runtime/write";
} else {
// Sane default of no storage visible
return true;
}
if (TEMP_FAILURE_RETRY(mount(storageSource.string(),
"/storage",
NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {…
}
透过不同的“View”所能“看到”的Mount Tree是不一样的,从而实现了程序在外部存储上的分权限管理。更为重要的是,采用这种实现方式在应对Runtime Permission变化时是不需要重启应用程序就可以生效的。具体来说,当程序取得Storage新的Runtime Permission以后,PMS会通过MountService向Vold的CommandListener发送一条名为“remount_uid”的命令,后者则进一步将消息传递给Vold的VolumeManager::remountUid函数——在这个函数中就可以对相应的进程进行“视角”的重新调整,从而达到我们的预期效果。