Android 进阶——系统启动之Android进程造物者Zygote 进程启动详解
Posted CrazyMo_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 进阶——系统启动之Android进程造物者Zygote 进程启动详解相关的知识,希望对你有一定的参考价值。
引言
前面系列文章介绍了android系统的第一个用户进程——init进程由解析init.rc脚本启动,完成属性系统的初始化等工作后紧接着启动Android系统上的造物者——Zygote进程,这篇文章接着分析。
写时拷贝(Copy On Write):Linux 进程基本上都是通过fork 创建的,fork出来的子进程除了内核中一些核心数据结构和父进程不同之外,其余大部分的内存空间都是和父进程共享的,当且仅当子进程需要去改写这些共享内存时,内核才会为子进程分配一个新的页面,并将老页面的数据复制一份到新页面中。
以后更多精选系列文章以高度完整的系列形式发布在公众号,真正的形成一套完整的技术栈,欢迎关注,目前第一个系列是关于Android系统启动系列的文章,大概有二十几篇干货:
一、Zygote进程概述
Zygote 中文翻译受精卵,是由Android系统第一个用户进程init进程启动起来的,在Android 系统中,所有的应用程序进程包括用于运行系统关键核心服务的SystemServer进程都是由Zygote 进程fork 而来的,就像受精卵一样具有孵化的功能,因此又被它称之为进程孵化器。即Zygote 通过 fork自身(复制自身)方式创建System进程和应用程序进程的。而且Zygote 进程在启动时会自主在内部创建一个VM实例,而通过fork 而得到的所有子进程自然继承Zygote的空间,换言之,System进程和应用进程内部也持有一个VM实例的拷贝,这样进程才可以通过这个VM实例运行Java 语言开发的程序组件。
fork 函数执行成功后,会有两次返回,其中一次是在当前进程中返回,另一次是在新创建出来的子进程中返回(当返回值等于0时)。
二、Zygote进程的启动
1、Init进程解析init.rc脚本触发Zygote进程的启动
1.1、init.rc 脚本触发zygote 以service 形式启动
Zygote进程是通过aosp/system/core/rootdir/init.rc
脚本启动的,如下所示部分内容
import /init.environ.rc
import /init.usb.rc
import /init.$ro.hardware.rc
import /init.usb.configfs.rc
import /init.$ro.zygote.rc
....
在第五行init.$ro.zygote.rc
可以看到有一个全局系统属性名为ro.zygote(我设备得到的值zygote64_32)
adb shell getprop | grep ro.zygote
其实这是一个init.rc 的动态引入的语法,则代表引入的.rc文件名为 init.zygote64_32.rc,内容如下:
service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
由上得知,定义了两个Zygote服务:zygote和zygote_secondary,分别对应不同的可执行文件,前者是app_process32,后者是app_process64。但它们进程的可执行文件都是app_process,对应的源码在/frameworks/base/cmds/app_process/app_main.cpp。
1.2、解析 init.zygote64_32.rc 中的service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote 指令
当解析到第一行的命令时,遇到service
指令时就会相应地调用system/core/init/service.cpp # Service::start函数来执行启动工作,对应的含义依次为zygote进程所对应的应用程序文件为/system/bin/app_process32
,就紧接着跟着进程启动需要传递的参数,--start-system-server
表示Zygote进程启动完毕后需要马上将System进程启动。对应的app_process 启动命令格式:
app_process [虚拟机参数] 运行目录 参数 [Java类]
名称 | 说明 |
---|---|
虚拟机参数 | 以“-”开头,用于指定指定Android虚拟机时的参数 |
运行目录 | 程序的运行目录,一般是/system/bin |
参数 | 以“–”开头,比如–zygote 表示要启动的zygote进程,–application则表示以普通进程的方式执行Java代码 |
Java类 | 要执行的Java类,即执行这个类的静态方法main,需要注意传递参数“–zygote” 时不会执行这个类而是执行ZygoteInit这个类的main方法 |
1.3、调用system/core/init/service.cpp # Service::start 函数创建zygote进程和/system/core/init/util.cpp # create_socket函数创建名为zygote 的socket
bool Service::Start()
...
pid_t pid = fork();
if (pid == 0)
umask(077);
for (const auto& ei : envvars_)
add_environment(ei.name.c_str(), ei.value.c_str());
//遍历为Zygote进程配置的socket列表,并创建对应的socket并把返回的fd发布到系统中
for (const auto& si : sockets_)
int socket_type = ((si.type == "stream" ? SOCK_STREAM :
(si.type == "dgram" ? SOCK_DGRAM :
SOCK_SEQPACKET)));
const char* socketcon =
!si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
int s = create_socket(si.name.c_str(), socket_type, si.perm,
si.uid, si.gid, socketcon);
if (s >= 0)
PublishSocket(si.name, s);
...
std::vector<std::string> expanded_args;
std::vector<char*> strs;
expanded_args.resize(args_.size());
strs.push_back(const_cast<char*>(args_[0].c_str()));
for (std::size_t i = 1; i < args_.size(); ++i)
if (!expand_props(args_[i], &expanded_args[i]))
ERROR("%s: cannot expand '%s'/n", args_[0].c_str(), args_[i].c_str());
_exit(127);
strs.push_back(const_cast<char*>(expanded_args[i].c_str()));
strs.push_back(nullptr);
//str[0] 值等于/system/bin/app_process 通过shell 加载该文件
if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0)
ERROR("cannot execve('%s'): %s/n", strs[0], strerror(errno));
_exit(127);
if (pid < 0)
ERROR("failed to start '%s'/n", name_.c_str());
pid_ = 0;
return false;
...
return true;
第二行就表示在Zygote启动过程中需要再内部创建一个名为“zygote”的socket,用于执行与System 进程进行通信的。
/*
* create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
* ("/dev/socket") as dictated in init.rc. This socket is inherited by the
* daemon. We communicate the file descriptor's value via the environment
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
int create_socket(const char *name, int type, mode_t perm, uid_t uid,
gid_t gid, const char *socketcon)
struct sockaddr_un addr;
int fd, ret, savederrno;
char *filecon;
if (socketcon)
if (setsockcreatecon(socketcon) == -1)
ERROR("setsockcreatecon(/"%s/") failed: %s/n", socketcon, strerror(errno));
return -1;
//调用内核的socket函数创建并返回对应的fd,PF_UNIX表示创建用于本地进程间通信的类型socket,比如说AMS 在请求与Zygote进程通信时创建新的应用进程前,需要先打开下面绑定的设备文件来创建一个连接到Zygote 进程的客户端socket
fd = socket(PF_UNIX, type, 0);
if (fd < 0)
ERROR("Failed to open socket '%s': %s/n", name, strerror(errno));
return -1;
if (socketcon)
setsockcreatecon(NULL);
memset(&addr, 0 , sizeof(addr));
addr.sun_family = AF_UNIX;
//ANDROID_SOCKET_DIR 是一个宏 值为/dev/socket
snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
name);
ret = unlink(addr.sun_path);
if (ret != 0 && errno != ENOENT)
ERROR("Failed to unlink old socket '%s': %s/n", name, strerror(errno));
goto out_close;
filecon = NULL;
if (sehandle)
ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);
if (ret == 0)
setfscreatecon(filecon);
//bind 函数的作用就是将上面创建socket的返回的fd 与socket地址addr 绑定起来得到一个设备文件——dev/socket/zygote
ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
savederrno = errno;
setfscreatecon(NULL);
freecon(filecon);
if (ret)
ERROR("Failed to bind socket '%s': %s/n", name, strerror(savederrno));
goto out_unlink;
//配置权限
ret = lchown(addr.sun_path, uid, gid);
if (ret)
ERROR("Failed to lchown socket '%s': %s/n", addr.sun_path, strerror(errno));
goto out_unlink;
ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW);
if (ret)
ERROR("Failed to fchmodat socket '%s': %s/n", addr.sun_path, strerror(errno));
goto out_unlink;
INFO("Created socket '%s' with mode '%o', user '%d', group '%d'/n",
addr.sun_path, perm, uid, gid);
return fd;
out_unlink:
unlink(addr.sun_path);
out_close:
close(fd);
return -1;
socket 创建完毕之后还需要通过/system/core/init/service.cpp#PublishSocket 函数发布到系统中
void Service::PublishSocket(const std::string& name, int fd) const
std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
std::string val = StringPrintf("%d", fd);
add_environment(key.c_str(), val.c_str());
/* make sure we don't close-on-exec */
fcntl(fd, F_SETFD, 0);
2、Zygote进程加载system/bin/app_process文件完成启动
从上面我们可以知道Zygote进程在启动过程中会加载system/bin/app_process
程序文件,app_process 除了能启动Zygote进程,还可以使用它来执行Java类中的某个main方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zo0nLkyn-1638888067002)(assets/image-20210715154633309.png)]
2.1、执行/frameworks/base/cmds/app_process/app_main.cpp # main 函数
app_process/app_main.cpp的main函数主要作用就是解析传入的启动参数,完成以下工作。
2.1.1、创建AppRuntime 对象
在app_main.cpp文件里定义了AppRuntime 类(AppRuntime继承自AndroidRuntime类),主要用于创建和初始化虚拟机以及启动Java线程,AppRuntime 在一个进程中有且只有一个实例对象并保存在全局变量gCurRuntime指针中
\\frameworks\\base\\core\\jni\\AndroidRuntime.cpp
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
mExitWithoutCleanup(false),
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
SkGraphics::Init();//初始化skia 图形引擎
// Pre-allocate enough space to hold a fair number of options. 预分配空间来存放传入的虚拟机参数
mOptions.setCapacity(20);
assert(gCurRuntime == NULL); // one per process 确保只能被初始化一次
gCurRuntime = this;
2.1.2、给AppRuntime 配置参数和设置进程名称(即将当前进程名称设置为zygote)
2.1.3、解析启动参数
解析参数后niceName的值为zygote,startSystemServer的值为true,zygote为true。
2.1.4、AppRuntime #start函数执行ZygoteInit或者RuntimeInit类的main方法
如果启动的不是zygote进程则执行RuntimeInit类
int main(int argc, char* const argv[])
...
//创建runtime局部对象,Zygote 进程就是需要依靠它来继续完成启动的,如果启动的是Zygote进程那么ZygoteInit类中会进入等到Socket事件的循环,不会被销毁;而执行的是一个java类则会在执行完毕后被销毁。
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
argc--;
argv++;
// Parse runtime arguments. Stop at first unrecognized option.
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 = true;
niceName = ZYGOTE_NICE_NAME;
else if (strcmp(arg, "--start-system-server") == 0)
startSystemServer = true;
else if (strcmp(arg, "--application") == 0)
application = true;
else if (strncmp(arg, "--nice-name=", 12) == 0)
niceName.setTo(arg + 12);
...
Vector<String8> args;
if (!className.isEmpty())
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);
else
// We're in zygote mode.
maybeCreateDalvikCache();
if (startSystemServer)
args.add(String8("start-system-server"));
String8 abiFlag("--abi-list=");
args.add(abiFlag);
// In zygote mode, pass all remaining arguments to the zygote main() method.
for (; i < argc; ++i)
args.add(String8(argv[i]));
if (!niceName.isEmpty())
runtime.setArgv0(niceName.string());
set_process_name(niceName.string());//设置进程名称
if (zygote)
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
else if (className)
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
...
2.2、frameworks/base/core/jni/AndroidRuntime.cpp # start
Zygote进程在运行Java代码前还需要初始化整个Java运行环境。
2.2.1、设置环境变量ANDROID_ROOT的值为/system
2.2.2、进行JNI 接口初始化并启动虚拟机并触发onVmCreated 函数回调
实际上是触发AppRuntime#onVmCreated 回调。
2.2.3、startReg注册系统JNI函数
通过调用register_jni_procs函数将全局数组gRegJNI的本地JNI函数注册到虚拟机中。
2.2.4、JNI调用 com.android.internal.os.ZygoteInit
的main函数
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
//标志着Android系统启动,因为以后的应用进程都是由zygotefork出来的,所以不会再执行start函数了,如果系统反复出现这个日志且id都是zygote则说明Zygote进程在不断地被重启。
ALOGD(">>>>>> START %s uid %d <<<<<<\\n",className != NULL ? className : "(unknown)", getuid());
//定义变量直接初始化,其字面值是"start-system-server"的副本(不包括最后的那个空字符)
static const String8 startSystemServer("start-system-server");
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
for (size_t i = 0; i < options.size(); ++i)
if (options[i] == startSystemServer)
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
//环境变量ANDROID_ROOT的设置,即环境变量ANDROID_ROOT的值被设置成了/system
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL)
rootDir = "/system";
if (!hasDir("/system"))
return;
setenv("ANDROID_ROOT", rootDir, 1);
/* start the virtual machine */
//通过jni_invocation.Init(NULL)完成jni接口的初始化,并启动虚拟机的代码,后续再分析细节
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0)
return;
onVmCreated(env);
/*
* Register android functions.注册系统JNI函数
*/
if (startReg(env) < 0)
return;
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i)
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL)
ALOGE("JavaVM unable to locate class '%s'/n", slashClassName);
/* keep going */
else
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL)
ALOGE("JavaVM unable to find main() in '%s'/n", className);
/* keep going */
else
//jni调用传入的className 即 com.android.internal.os.ZygoteInit的main函数
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
free(slashClassName);
ALOGD("Shutting down VM/n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread/n");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly/n");
2.3、JNI调用/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java #main 方法
public static void main(String argv[])
...
boolean isFirstBooting = false;
ZygoteHooks.startZygoteNoThreadCreation();
try
boolean startSystemServer = false;
String socketName = "zygote";
...
for (int i = 1; i < argv.length; i++)
if ("start-system-server".equals(argv[i]))
startSystemServer = true;
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]);
registerZygoteSocket(socketName);
if(!isFirstBooting && !isBox)
if (startSystemServer)
startSystemServer(abiList, socketName);
preload();
...
runSelectLoop(abiList);
closeServerSocket();
catch (MethodAndArgsCaller caller)
caller.run();
catch (Throwable ex)
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
2.3.1、registerZygoteSocket(socketName) 创建一个Server端socket用于等待AMS 请求Zygote进程创建新的应用进程
private static void registerZygoteSocket(String socketName)
if (sServerSocket == null)
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try
//通过系统环境变量得到fd的值并转换为fd
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
catch (RuntimeException ex)
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
try
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
sServerSocket = new LocalServerSocket(fd);
catch (IOException ex)
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
2.3.2、调用preload方法预加载系统资源
包括系统预加载类、Framework资源和OpenGL资源,当应用被fork创建出来后,应用进程内自然就包含了这些资源,无需应用再次自己加载。
2.3.3、startSystemServer(abiList, socketName) fork 创建System进程(SystemServer进程)
/**
* Prepare the arguments and fork for the system server process.
*/
private static boolean startSystemServer(String abiList, String socketName)
throws MethodAndArgsCaller, RuntimeException
/* Hardcoded command line to start the system server */
//System进程启动的参数,可以看到System进程的用户ID和用户组ID都是1000,同时还有其他用户组的权限
String args[] =
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"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 */
//fork 创建System进程
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
catch (IllegalArgumentException ex)
throw new RuntimeException(ex);
/* For child process */
if (pid == 0)
if (hasSecondZygote(abiList))
if(isBox)
waitForSecondaryZygote(socketName);
Log.d(TAG,"--------call waitForSecondaryZygote,skip this---,abiList= "+abiList);
// 处理System进程启动事宜
handleSystemServerProcess(parsedArgs);
return true;
2.3.4、执行runSelectLoop(abiList) 方法等待处理AMS 请求Zygote 创建新应用进程
/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
*
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
while (true)
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i)
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
try
Os.poll(pollFds, -1);
catch (ErrnoException ex)
throw new RuntimeException("poll failed", ex);
for (int i = pollFds.length - 1; i >= 0; --i)
if ((pollFds[i].revents & POLLIN) == 0)
continue;
if (i == 0)
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
else
boolean done = peers.get(i).runOnce();
if (done)
peers.remove(i);
fds.remove(i);
简而言之,/frameworks/base/cmds/app_process/app_main.cpp # main 解析init.rc 脚本传入的参数,触发AppRuntime #start函数进而执行ZygoteInit#main 方法。
2.4、fork 创建SystemServer进程
Zygote进程在fork 创建SystemServer进程后返回到SystemServer进程继续进行相关System进程启动工作,至此Zygote 进程启动完毕,Zygote进程在初始化时就会创建Android虚拟机、注册JNI函数、预加载系统资源文件和Java 类。所有应用进程都共享和继承这些资源,Zygote进程启动工作完毕后,也会变成守护进程,负责处理启动App 应用程序的请求,预知后事如何请参见下文。
三、Zygote启动应用进程
预知后事如何请参见下文,不过得先启动AMS服务。
以上是关于Android 进阶——系统启动之Android进程造物者Zygote 进程启动详解的主要内容,如果未能解决你的问题,请参考以下文章
Android 进阶——系统启动之SystemServer创建并启动Installer服务
Android 进阶——系统启动之SystemServer创建并启动Installer服务
Android 进阶——系统启动之Framework 核心ActivitityManagerService服务启动
Android 进阶——系统启动之Framework 核心ActivitityManagerService服务启动