码迷,mamicode.com
首页 > 系统相关 > 详细

Zygote进程的启动流程

时间:2015-09-17 10:17:05      阅读:234      评论:0      收藏:0      [点我收藏+]

标签:

     Zygote进程时由Android系统的第一个进程init启动起来的。init进程时在内核加载完成之后就启动起来的,它在启动的过程中,会读取根目录下的一个脚本文件init.rc,以便可以将其他需要开机启动的进程也一起启动起来。

     Zygote进程在脚本文件init.rc中的启动脚本如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666
     第一行表示Zygote进程是以服务的形式启动的,并且它对应的应用程序文件为/system/bin/app_process。接下来的四个选项是Zygote进程的启动参数。其中最后一个参数"--start-system-server"表示Zygote进程在启动完成之后,需要马上将System进程也启动起来。

     第二行表示Zygote进程在启动的过程中,需要在内部创建一个名称为"zygote"的Socket。这个Socket是用来执行进程间通信的,它的访问权限被设置为666,即所有用户都可以对它进行读和写。


     我们先分析Zygote是如何在启动的过程中创建一个名称为"zygote"的Socket。

     由于Zygote进程在脚本init.rc中配置了以服务的形式来启动,因此,init进程在启动时,就会调用函数service_start来启动它,如下:

void service_start(struct service *svc, const char *dynamic_args)//svc用来描述一个即将要启动的服务,dynamic_args为NULL
{
    ......
    pid_t pid;
    ......
    pid = fork();//init进程fork出Zygote进程

    if (pid == 0) {//Zygote进程
        struct socketinfo *si;
        ......
        for (si = svc->sockets; si; si = si->next) {//svc->sockets描述即将要启动的服务的Socket列表
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid);//name为socket,socket_type为stream,创建一个Socket,返回一个描述符
            if (s >= 0) {
                publish_socket(si->name, s);//create_socket返回的描述符最后会通过函数publish_socket发布到系统中
            }
        }

       ......

        setpgid(0, getpid());

       /* as requested, set our gid, supplemental gids, and uid */
        if (svc->gid) {
            setgid(svc->gid);
        }
        if (svc->nr_supp_gids) {
            setgroups(svc->nr_supp_gids, svc->supp_gids);
        }
        if (svc->uid) {//由于svc->uid为空,所有Zygote进程的uid还为0,依然是root权限
            setuid(svc->uid);
        }

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {//svc->args[0]为/system/bin/app_process,svc->args静态启动参数,注意这个数组里面有"--start-system-server"
                ERROR("cannot execve(‘%s‘): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            ......
        }
        _exit(127);
    }

    ......
}
    接着我们分析函数create_socket和public_socket的实现,以便可以了解它们是如何为Zygote进程创建一个名称为"zygote"的Socket。

    函数create_socket的实现如下所示。

int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid)
{
    struct sockaddr_un addr;
    int fd, ret;

    fd = socket(PF_UNIX, type, 0);//创建一个socket,这个Socket使用文件描述符fd描述
    ......

    memset(&addr, 0 , sizeof(addr));
    addr.sun_family = AF_UNIX;//创建了一个类型为AF_UNIX的Socket地址addr,这个Socket地址有一个对应的设备文件,即ANDROID_SOCKET_DIR/name,如下一行代码所示
    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",//name为zygote,ANDROID_SOCKET_DIR为/dev/socket
             name);

    ......

    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));//bind将文件描述符fd所描述的Socket与Socket地址addr绑定起来之后,我们就可以得到一个设备文件/dev/socket/zygote
    ......

    chown(addr.sun_path, uid, gid);//chown设置设备文件/dev/socket/zygote的用户ID,用户组ID
    chmod(addr.sun_path, perm);//chmod设置设备文件/dev/socket/zygote的访问权限,参数perm为666,因为设备文件/dev/socket/zygote是可以被任意用户访问的。

    ......

    return fd;//返回描述符
}
    函数publish_socket的实现如下:

static void publish_socket(const char *name, int fd)
{
    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
    char val[64];

    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
            name,
            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));//key值为ANDROID_SOCKET_zygote
    snprintf(val, sizeof(val), "%d", fd);//fd是一个文件描述符,指向了前面所创建的一个Socket,把fd格式化为一个字符串,并且保存在变量val中
    add_environment(key, val);//在系统中增加了一个名称为key的环境变量,并且将它的值设置为val。这就相当于将前面所创建的一个Socket的文件描述符保存在名称为"ANDROID_SOCKET_zygote"的环境变量中

    /* make sure we don‘t close-on-exec */
    fcntl(fd, F_SETFD, 0);
}
    在下面,我们就会看到,Zygote进程在启动的过程中,会根据这个环境变量的值来创建一个Server端Socket,等到启动完成之后,再在这个Server端Socket上等待Activtiy管理服务ActivityMangerService请求它创建新的应用程序进程。


    回到service_start中,继续执行如下代码:

if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {//svc->args[0]为/system/bin/app_process,svc->args静态启动参数,注意这个数组里面有"--start-system-server"
                ERROR("cannot execve(‘%s‘): %s\n", svc->args[0], strerror(errno));
            }
        }
    在Zygote进程中加载的应用程序文件为system/bin/app_process,因此,接下来我们就从这个应用程序文件的入口函数main开始分析Zygote进程的启动流程。
int main(int argc, const char* const argv[])
{
    // These are global variables in ProcessState.cpp
    mArgC = argc;
    mArgV = argv;
    
    mArgLen = 0;
    for (int i=0; i<argc; i++) {
        mArgLen += strlen(argv[i]) + 1;
    }
    mArgLen--;

    AppRuntime runtime;//创建了一个AppRuntime对象runtime
    const char *arg;
    const char *argv0;

    argv0 = argv[0];

    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // Everything up to ‘--‘ or first non ‘-‘ arg goes to the vm
    
    int i = runtime.addVmArguments(argc, argv);

    // Next arg is parent directory
    if (i < argc) {
        runtime.mParentDir = argv[i++];
    }

    // Next arg is startup classname or "--zygote"
    if (i < argc) {
        arg = argv[i++];
        if (0 == strcmp("--zygote", arg)) {//启动参数中有"--zygote"
            bool startSystemServer = (i < argc) ? 
                    strcmp(argv[i], "--start-system-server") == 0 : false;//startSystemServer的值等于true,表示Zygote进程在启动完成之后,需要将System进程启动起来
            setArgv0(argv0, "zygote");
            set_process_name("zygote");//设置进程名为zygote
            runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer);
        } else {
            ......
        }
    } else {
        ......
    }

}
    AppRuntime类的成员函数start是从其父类AndroidRuntime继承下来的,因此,接下来我们就继续分析AndroidRuntime类的成员函数start的实现。

/*
* 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".
*/
void AndroidRuntime::start(const char* className, const bool startSystemServer)
{
	......

	char* slashClassName = NULL;
	char* cp;
	JNIEnv* env;

	......

	/* start the virtual machine */
	if (startVm(&mJavaVM, &env) != 0)
		goto bail;

	/*
	* Register android functions.
	*/
	if (startReg(env) < 0) {
		LOGE("Unable to register all android natives\n");
		goto bail;
	}

	/*
	* We want to call main() with a String array with arguments in it.
	* At present we only have one argument, the class name.  Create an
	* array to hold it.
	*/
	jclass stringClass;
	jobjectArray strArray;
	jstring classNameStr;
	jstring startSystemServerStr;
	stringClass = env->FindClass("java/lang/String");
	assert(stringClass != NULL);
	strArray = env->NewObjectArray(2, stringClass, NULL);
	assert(strArray != NULL);
	classNameStr = env->NewStringUTF(className);
	assert(classNameStr != NULL);
	env->SetObjectArrayElement(strArray, 0, classNameStr);
	startSystemServerStr = env->NewStringUTF(startSystemServer ?
		"true" : "false");
	env->SetObjectArrayElement(strArray, 1, startSystemServerStr);

	/*
	* Start VM.  This thread becomes the main thread of the VM, and will
	* not return until the VM exits.
	*/
	jclass startClass;
	jmethodID startMeth;

	slashClassName = strdup(className);
	for (cp = slashClassName; *cp != ‘\0‘; cp++)
		if (*cp == ‘.‘)
			*cp = ‘/‘;

	startClass = env->FindClass(slashClassName);
	if (startClass == NULL) {
		......
	} else {
		startMeth = env->GetStaticMethodID(startClass, "main",
			"([Ljava/lang/String;)V");
		if (startMeth == NULL) {
			......
		} else {
			env->CallStaticVoidMethod(startClass, startMeth, strArray);
			......
		}
	}

	......
}
     startVm在Zygote进程中创建了一个虚拟机实例,然后startReg在这个虚拟机实例中注册了一系列JNI方法,这两步的执行流程可参考Dalvik虚拟机的启动过程分析Dalvik虚拟机总结

     最后调用com.android.internal.os.ZygoteInit类的静态成员函数main来进一步启动Zygote进程。env->CallStaticVoidMethod(startClass, startMeth, strArray)实际上通过Dalvik虚拟机去解释执行相关java代码,具体请参考Dalvik虚拟机的运行过程分析


     接着我们继续来分析com.android.internal.os.ZygoteInit类的静态成员函数main的实现。

public class ZygoteInit {
	......

	public static void main(String argv[]) {
		try {
			......

			registerZygoteSocket();//创建一个Server端Socket,这个Server端Socket是用来等待Activity管理服务ActivityManagerService请求Zygote进程创建新的应用程序进程的	
			
			......

			......

			if (argv[1].equals("true")) {
				startSystemServer();//启动System进程,以便它可以将系统的关键服务启动起来
			} else if (!argv[1].equals("false")) {
				......
			}

			......

			if (ZYGOTE_FORK_MODE) {//false
				......
			} else {
				runSelectLoopMode();
			}

			......
		} catch (MethodAndArgsCaller caller) {
			......
		} catch (RuntimeException ex) {
			......
		}
	}

	......
}
    我们先来分析registerZygoteSocket,如下:

   private static void registerZygoteSocket() {
        if (sServerSocket == null) {
            int fileDesc;
            try {
                String env = System.getenv(ANDROID_SOCKET_ENV);//获得一个名称为"ANDROID_SOCKET_zygote"的环境变量的值
                fileDesc = Integer.parseInt(env);//将它转换位一个文件描述符,我们知道这个文件描述符是用来描述一个类型为AF_UNIX的Socket。
            } catch (RuntimeException ex) {
                throw new RuntimeException(
                        ANDROID_SOCKET_ENV + " unset or invalid", ex);
            }

            try {
                sServerSocket = new LocalServerSocket(
                        createFileDescriptor(fileDesc));//创建了一个Server端Socket,并且保存在ZygoteInit的静态成员变量sServerSocket中
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket ‘" + fileDesc + "‘", ex);
            }
        }
    }
    然后我们来分析startSystemServer,如下:

public class ZygoteInit {
	......

	private static boolean startSystemServer()
			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,3001,3002,3003",
			"--capabilities=130104352,130104352",
			"--runtime-init",
			"--nice-name=system_server",
			"com.android.server.SystemServer",
		};
		ZygoteConnection.Arguments parsedArgs = null;

		int pid;

		try {
			parsedArgs = new ZygoteConnection.Arguments(args);

			......

			/* Request to fork the system server process */
			pid = Zygote.forkSystemServer(
				parsedArgs.uid, parsedArgs.gid,
				parsedArgs.gids, debugFlags, null,
				parsedArgs.permittedCapabilities,
				parsedArgs.effectiveCapabilities);//System进程uid为1000
		} catch (IllegalArgumentException ex) {
			......
		}

		/* For child process */
		if (pid == 0) {//子进程,System进程
			handleSystemServerProcess(parsedArgs);//System进程,后续再分析
		}

		return true;
	}
	
	......
}
     首先创建了字符串数组args,用来保存System进程的启动参数。接着调用forkSystemServer来创建一个子进程,这个子进程就是Android系统的System进程。这里可以看出,Android系统的System进程的用户id和用户组id都被设置为1000,shell中显示的就是system。

     返回到com.android.internal.os.ZygoteInit类的静态成员函数main,继续分析runSelectLoopMode,如下:

public class ZygoteInit {
	......

	private static void runSelectLoopMode() throws MethodAndArgsCaller {
		ArrayList<FileDescriptor> fds = new ArrayList();
		ArrayList<ZygoteConnection> peers = new ArrayList();
		FileDescriptor[] fdArray = new FileDescriptor[4];//首先创建了一个大小为4的Socket文件描述符数组fdArray,表示Zygote进程做多能同时处理4个Socket连接	

		fds.add(sServerSocket.getFileDescriptor());//将ZygoteInit类的静态成员变量sServerSocket所描述的一个Socket的文件描述符添加到Socket文件描述符列表fds中
		peers.add(null);

		int loopCount = GC_LOOP_COUNT;
		while (true) {
			int index;

			......


			try {
				fdArray = fds.toArray(fdArray);//将保存在Socket文件描述符列表fds中的Socket文件描述符转移到Socket文件描述符数组fdArray中
				index = selectReadable(fdArray);//来监控保存在这个数组中的Socket是否有数据可读。
			} catch (IOException ex) {
				throw new RuntimeException("Error in select()", ex);
			}

			if (index < 0) {
				throw new RuntimeException("Error in select()");
			} else if (index == 0) {//接收到连接,
				ZygoteConnection newPeer = acceptCommandPeer();
				peers.add(newPeer);
				fds.add(newPeer.getFileDesciptor());
			} else {//接收到Activity管理服务ActivityManangerService发送过来的创建应用程序进程请求
				boolean done;
				done = peers.get(index).runOnce();//来创建一个应用程序进程

				if (done) {
					peers.remove(index);
					fds.remove(index);
				}
			}
		}
	}

	......
}      
    selectReadable来监控保存在这个数组中的Socket是否有数据可读,一个数据可读就意味着它接收到了一个连接或者一个请求。如果index为0,说明它接收到了一个连接。index大于0,说明接收到请求。

    ZygoteInit中静态函数startSystemServer中调用了Zygote.forkSystemServer,ZygoteInit中静态函数runSelectLoopMode中调用了selectReadable,都是native方法,如下:

native public static int forkAndSpecialize(int uid, int gid, int[] gids,
            int debugFlags, int[][] rlimits);
static native int selectReadable(FileDescriptor[] fds) throws IOException;
    我们刚刚说过了com.android.internal.os.ZygoteInit类的静态成员函数main,是Dalvik虚拟机解释执行的;那么执行到这些native方法,实际上JNI方法是直接在本地操作系统上执行的,而不是由Dalvik虚拟机解释器执行。具体请参考Dalvik虚拟机JNI方法的注册过程分析Dalvik虚拟机总结。相关直接在本地操作系统执行代码如下:

void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
    bool fromJni, JValue* pResult, va_list args)
{
    ......

    if (dvmIsNativeMethod(method)) {
        TRACE_METHOD_ENTER(self, method);
        /*
         * Because we leave no space for local variables, "curFrame" points
         * directly at the method arguments.
         */
        (*method->nativeFunc)(self->curFrame, pResult, method, self);//直接去执行
        TRACE_METHOD_EXIT(self, method);
    } else {
        dvmInterpret(self, method, pResult);//解释执行
    }

    ......
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

Zygote进程的启动流程

标签:

原文地址:http://blog.csdn.net/jltxgcy/article/details/48488921

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!