Android大脑--systemserver进程

news2024/12/27 18:28:37

用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章,技术文章也可以有温度。

本文摘要

系统native进程的文章就先告一段落了,从这篇文章开始写Java层的文章,本文同样延续自述的方式来介绍systemserver进程,通过本文您将了解到systemserver进程是啥?它包含那么多的服务,是如何管理它们的?如何启动它们的?(文中代码基于Android13)

本文大纲

我是谁

我的出生

我的启动

总结

1. 我是谁

我是一个进程,是一个既可以运行Java代码也可以运行C和C++代码的全能型选手,不像我的“叔叔辈儿”如logd进程、lmkd进程只能运行C和C++代码。我拥有全能型能力还要感谢我的“父亲”zygote,因为他是全能型的,所以我才把这些能力继承了过来。

我的uid是1000,uid是啥呢?它与你们人类的身份证一样,每个安装到Android设备上的程序都有一个uid,只要apk被安装就分配一个唯一的uid,只要apk不卸载,这个uid是不会变化的。uid为0也就是root用户的程序它可是拥有无限超级能力。

我其实还有一个真名叫system_process,我觉得这个名字不霸气、不能体现我的职责,因此我对外让大家都称呼我为systemserver,你看这名字多好系统服务,这名字多霸气、多威武。

1.1 我的父亲

我的“爷爷”init进程有非常非常多的孩子,这或许是偶像的力量吧,我的“父亲”zygote也有非常非常多的孩子。凡是Android中能运行Java代码的进程都是zygote的孩子,并且它们也都是我的“弟弟”,为啥是弟弟呢?因为我是zygote的第一个孩子,也就是“长子”。

其实我对我的“父亲”zygote也是有蛮多怨言的,为啥这样说呢?因为他只负责孵化子进程,而孵化完毕后,他就做一个甩手掌柜的,对于子进程不管不顾,他甚至完全不知道自己孵化了多少子进程,而这些任务却偏偏都落在了我的身上,我还得负责管理这些子进程。这或许就是“”长兄如父“”的完美体现吧。

1.2 我的弟弟们

我所有的“弟弟们”,它们若想要“出生”的话,都需要经过我向zygote发送孵化它们的请求,zygote孵化出它们后,我负责来管理它们,比如当Android设备处于低内存状态时候,我会尝试把最不常用、处于后台的子进程杀掉。**不知道你们有没有发现个问题,我的uid是1000并非是0,那我怎么具有杀其他进程的能力的呢?**先卖个关子,在后面会介绍到我如何具有此能力。

我不单单充当“长子”的角色,我还把我自己比作Android的大脑,这可不是我骄傲事实确实是这样的,所有可运行Java代码进程都需要以我为中心,我离了它们是可以的,它们可是万万离不开我,比如这些进程想要启动Activity,那需要经过我的ActivityTaskManagerService服务的校验,再比如某个进程需要发出振动的话,那也需要经过我的VibratorManagerService服务进而由它发向hal层发出振动的命令。

(如下图展示app进程与systemserver进程的一些交互信息)

image

大家可别把我想象的如超人那样,我也不是事无巨细的“诸葛亮”,我能完成这么多复杂的工作,完全是靠我拥有的各种service (服务),这些service会处理各自擅长的事情,像上面提到的ActivityTaskManagerService和VibratorManagerService就是其中两个service,那来看下我拥有的service吧。

1.3 拥有的service

下图是我拥有的部分service
image

那就来简单介绍下部分service吧

Installer

在installd进程中介绍过它,它与apk的安装有关系,它对installd进程做了封装,这样就可以在Java层使用installd的功能了。

PowerStatsService

想要知道电量的使用情况,可以找它啊,它可是电量使用情况的专家

ActivityTaskManagerService

该服务与启动Activity有非常大的关系,进程中启动Activity必定要经过该服务

PowerManagerService

电源管理服务是一个非常重要的系统服务,它负责管理系统的电源状态,包括设备的唤醒、休眠、屏幕亮度调节、电池电量管理等

ThermalManagerService

该服务是温度监控服务,当温度超过一定阈值后,手机会做出一些策略比如cpu降频操作,保证手机温度不至于一直上升

DisplayManagerService

该服务负责管理设备上的所有显示相关的资源和操作。它负责屏幕的开启与关闭、屏幕亮度的调整、显示分辨率的变更、屏幕旋转以及多窗口管理等任务

UserManagerService

该服务主要是管理设备用户,一个设备默认情况下有一个主用户它的id是0,可以为设备增加用户,用户之间的数据是隔离的

好了,由于时间关系,我不可能把所有的service都介绍完毕,就先介绍以上这些service

service在什么环境下工作呢?

先来说下这里的环境一词,其实指的是在哪个线程工做。

首先需要肯定的一点是不可能所有的service都在我systemserver的唯一的一个主线程里面工作,如果是这样这些service肯定会出现各种问题,其次是性能低下。

其实对于这一点我是没有制定规则的,每个service的复杂程度不一样,因此有的service会启动自己的线程来做工作,而有的service是与别的service共享一个线程来做工作,有的service就在主线程里面做自己的工作。

1.4 service如何让使用者使用

我既然拥有这么多的service,那如何让使用者使用这些service的能力呢?

如果使用者都与我systemserver位于同一进程的话,这些事情就特别好办了,但却不是这样的使用者它们绝大多数都是位于别的进程,那这种情况就有些棘手了。

虽然棘手,但是是有解的,大家上眼了先看下下面这张类图

image

看了这张图是不是有一种似曾相识的感觉,没错这就是使用aidl生成的类图结构,大家都知道aidl其实是为binder通信设计的,那service如果让使用者使用的话那就是使用binder通信了,先暂停一下,继续把上面的类图交代清楚 (熟悉的小伙伴可以跳过这部分)

IXXXManager

它是一个接口,定义了该service提供的能力,它继承了IInterface接口。每个service都有自己的对应接口,比如ActivityManagerService对应的是IActivityManager接口、ActivityTaskManagerService对应的是IActivityTaskManager

IXXXManager.Stub

该类是一个抽象类,它实现了IXXXManager接口,同时也继承了Binder类。

XXXService

该类就是每个service了,每个service又继承了IXXXManager.Stub,也就是说每个service其实又是Binder的子类。下面是几个例子

public class ActivityTaskManagerService extends IActivityTaskManager.Stub 

public class ActivityManagerService extends IActivityManager.Stub
IXXXManager.Stub.Proxy

该类如它的名字就是一个代理类,它实现了IXXXManager接口,该类有一个属性mRemote它一般是BinderProxy类型的。而该类是让使用者来使用的,调用该类的相应方法,就可以通过binder通信最终调用到对应service的能力了。

同样每个service都有自己对应的IXXXManager.Stub.Proxy,比如ActivityManagerService对应的是IActivityManager.Stub.Proxy、ActivityTaskManagerService对应的是IActivityTaskManager.Stub.Proxy

上面的类图就是每个service让使用者使用的一个框架,说直白点就是使用了binder通信,因为binder通信是client/server模式,每个service是Binder的子类,那它就是server端,而使用者要想使用对应service的能力那就需要使用对应的IXXXManager.Stub.Proxy类了。

既然每个service是一个Binder,那就有必要把自己注册在ServiceManager中,只有注册了使用者才可以使用。(如下伪代码)

XXXService service = new XXXService()
ServiceManager.addService("XXXService",service)

使用者使用的话从ServiceManager中获取对应的service的BinderProxy对象,并把它作为IXXXManager.Stub.Proxy类的参数,来构造IXXXManager.Stub.Proxy这样的一个对象 ,通过该对象即可使用service的能力。(如下伪代码)

BinderProxy bp = ServiceManager.getService("XXXService")
IXXX.Stub.Proxy proxy = new IXXX.Stub.Proxy(bp)
# 就可以调用proxy对象来使用service的功能了

1.5 小结

我是一个既能运行Java代码又能运行C/C++代码的全能型进程,我在所有可运行Java代码的进程中的地位犹如人类的大脑,那可是妥妥的C位。我的地位虽然如此高贵,但我可不是一个集权者,我拥有很多的service,是它们替我分担了我的职责,它们有条不紊的处理着各种需求。其他可运行Java代码的进程要想使用这些service的能力,那就需要通过binder通信来使用。

好了,关于我就先介绍到此,作为C位的我,大家对我的出生一定很感兴趣,那就来讲下我的出生吧。

2. 我的出生

我的出生其实是被动的,为什么说是被动的呢?我的出生并不是我主动发起的,像我的“叔叔辈儿”、“弟弟们”他们的出生基本都是由他们主动发起的,比如logd进程会主动配置rc脚本文件,在把脚本文件交给init进程。而我的出生是在我完全不知晓的情况下由我的“父亲”zygote直接决定的。

我的出生大致分为三步:孵化前孵化孵化后,那就按这三步来讲下我的出生吧

2.1 孵化前

在孵化前,我的“父亲”zygote已经做了很多的工作的:加载JVM、加载公共的jni方法、提前预加载classes、预加载resource、预加载字体等。

其中预加载classes,会把这些classes加载到BootClassLoader.getInstance()对象中,该ClassLoader可是顶级ClassLoader,还有预加载的这些classes主要是framework.jar下面的类。预加载resource,会把这些resource加载到Resources.getSystem()对象中。

除了上面的工作,还有一个工作就是准备孵化时候要用到的参数,这些参数有几个是需要重点介绍下的。

setuid
--setuid=1000

如上设置了我systemserver进程的uid为1000

setgid
--setgid=1000

设置了我systemserver进程所属的组id是1000

capabilities

Linux Capabilities是Linux操作系统中的一种权限管理机制,它允许对进程的权限进行细粒度的控制,以实现最小特权原则。这一机制将root用户的特权划分为具体的功能集,允许将部分root特权授予非root进程,从而增强了系统的安全性。

Linux Capabilities是在Linux内核2.2之后引入的,它将传统上与超级用户root(UID=0)关联的特权细分为不同的功能组。这些功能组作为线程(Linux中并不真正区分进程和线程)的属性存在,每个功能组都可以独立启用和禁用。这样一来,权限检查的过程就变成了:在执行特权操作时,如果线程的有效身份不是root,就去检查其是否具有该特权操作所对应的capabilities,并以此为依据,决定是否可以执行特权操作

上面是capabilities功能的介绍,用一句话概括:就是说非root用户进程也可以通过设置capabilities,来让该进程具有相应的特权。

还记得我systemserver可以杀掉一些处于后台的进程来释放内存吗?杀掉其他进程的特权就是通过capabilities设置的,当然还设置了别的特权,如下相关代码:

long capabilities = posixCapabilitiesAsBits(
    //主要用于控制进程对共享内存片段的锁定能力            
    OsConstants.CAP_IPC_LOCK,
    //杀掉其他进程的能力
    OsConstants.CAP_KILL,
    //用于控制对网络配置和管理的访问权限
    OsConstants.CAP_NET_ADMIN,
    //它允许进程绑定到1024以下的端口号。这些端口号通常被认为是特权端口
    OsConstants.CAP_NET_BIND_SERVICE,
    //它允许进程发送广播消息到网络
    OsConstants.CAP_NET_BROADCAST,
    //它允许进程创建RAW和PACKET套接字以及进行任意地址的绑定
    OsConstants.CAP_NET_RAW,
    //它允许用户或进程加载(或卸载)内核模块
    OsConstants.CAP_SYS_MODULE,
    //它允许进程调整其他进程或自身进程的nice值,从而影响其调度优先级
    OsConstants.CAP_SYS_NICE,
    //它允许进程跟踪系统上的任何其他进程,这种能力通常与调试和安全审计相关
    OsConstants.CAP_SYS_PTRACE,
    //它允许进程设置系统时间
    OsConstants.CAP_SYS_TIME,
    //它允许进程进行TTY设备的配置任务。
    OsConstants.CAP_SYS_TTY_CONFIG,
    //它允许进程设置唤醒系统(或唤醒挂起系统)的定时器
    OsConstants.CAP_WAKE_ALARM,
    //它允许进程阻止系统进入挂起(suspend)状态
    OsConstants.CAP_BLOCK_SUSPEND
        );

在配置了上面的这些capabilities后,我systemserver也就具有了这些特权了。

入口类
com.android.server.SystemServer

入口类就是当systemserver进程被fork成功后,最先开始执行的类。如上这个类就是SystemServer类

这些参数配置完毕后,孵化前的准备工作就完成了。

下面是相关代码,请自行取阅:

//ZygoteInit类

private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        long capabilities = posixCapabilitiesAsBits(
                OsConstants.CAP_IPC_LOCK,
                OsConstants.CAP_KILL,
                OsConstants.CAP_NET_ADMIN,
                OsConstants.CAP_NET_BIND_SERVICE,
                OsConstants.CAP_NET_BROADCAST,
                OsConstants.CAP_NET_RAW,
                OsConstants.CAP_SYS_MODULE,
                OsConstants.CAP_SYS_NICE,
                OsConstants.CAP_SYS_PTRACE,
                OsConstants.CAP_SYS_TIME,
                OsConstants.CAP_SYS_TTY_CONFIG,
                OsConstants.CAP_WAKE_ALARM,
                OsConstants.CAP_BLOCK_SUSPEND
        );
        
        省略代码······

        String[] args = {
                "--setuid=1000",
                "--setgid=1000",
                "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
                        + "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
                "--capabilities=" + capabilities + "," + capabilities,
                "--nice-name=system_server",
                "--runtime-args",
                "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
                "com.android.server.SystemServer",
        };
        ZygoteArguments parsedArgs;

        int pid;

        try {
            ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args);
            try {
                parsedArgs = ZygoteArguments.getInstance(commandBuffer);
            } catch (EOFException e) {
                throw new AssertionError("Unexpected argument error for forking system server", e);
            }
            commandBuffer.close();
            // 设置允许所有的app可以调试
            Zygote.applyDebuggerSystemProperty(parsedArgs); 
            Zygote.applyInvokeWithSystemProperty(parsedArgs);

            省略其他代码······
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        省略其他代码······
    }

2.2 孵化

现在进入最激动人心的阶段孵化,因为孵化前已经把参数等信息都准备好了,那孵化就是调用系统的fork方法来创建子进程,当然在调用fork方法的时候是需要把从zygote进程继承来的fd关闭掉的,否则会影响原先的父进程。在fork成功后,会把传递的这些参数设置给我systemserver进程。

因为fork机制采用的是写时复制,即如果子进程没有对父子进程共享的区域进行操作的话,则这片区域是共享的。我的“父亲”zygote在孵化之前做的准备工作:如加载JVM、加载jni方法、预加载classes、预加载resource,都是我与我父亲共享的。

上面的话是不是有些懵,还是看下图吧,毕竟一图胜千言
image

在孵化之前zygote加载了JVM、预加载的classes加载到了BootClassLoader.getInstance()方法返回的BootClassLoader对象中、预加载的resources加载到了Resources.getSystem()方法返回的Resources对象中,当然还有别的。

而在孵化之后,systemserver进程就把zygote进程中的JVM实例、BootClassLoader.getInstance()返回的对象、Resources.getSystem()方法返回的Resources对象及其他的实例都完完全全的共享过来了。这也就是zygote提前做各种准备能在子进程中生效的具体原因。

2.3 孵化后

在孵化后会有关闭socket创建ClassLoader打开binder进入入口类这几件事情要做,那接下来就介绍下。

2.3.1 关闭socket

因为fork机制创建子进程,会把父进程打开的socket对应的fd也继承过来,因此需要把继承过来的socket对应的fd关闭掉。zygote是会启动一个ServerSocket,如果想要孵化子进程,则其他进程与该ServerSocket建立连接即可。因此systemserver进程需要把继承过来的ServerSocket关闭掉。

相关的代码,请自行取阅:

  //ZygoteInit类
  private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        省略代码······
        
        //在fork成功后,下面的代码会分别由zygote的线程和systemserver的线程执行,
        //pid==0 代表 systemserver的线程会执行
        if (pid == 0) {
            省略代码······
            //在systemserver进程把zygote socket关闭掉,因为systemserver进程不需要它
            //查看下面的closeServerSocket方法
            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }

        return null;
    }
    
  //ZygoteServer类
  void closeServerSocket() {
        try {
            if (mZygoteSocket != null) {
                FileDescriptor fd = mZygoteSocket.getFileDescriptor();
                mZygoteSocket.close();
                if (fd != null && mCloseSocketFd) {
                    Os.close(fd);
                }
            }
        } catch (IOException ex) {
            Log.e(TAG, "Zygote:  error closing sockets", ex);
        } catch (ErrnoException ex) {
            Log.e(TAG, "Zygote:  error closing descriptor", ex);
        }

        mZygoteSocket = null;
    }
2.3.2 创建ClassLoader

为啥要创建ClassLoader呢?

原因是这样的,当前被孵化出来的systemserver进程,它其实与zygote共享一个BootClassLoader对象,而该ClassLoader只是加载了进程之间公用的classes,而systemserver自己特有的各种classes在共享的BootClassLoader对象中是找不到的,比如入口类SystemServer在BootClassLoader找不到。那咋办呢,答案当然是创建自己的ClassLoader,并且把自己的各种jar、so文件路径设置给ClassLoader,同时该ClassLoader的parent是共享的BootClassLoader对象。这样通过自己的创建的ClassLoader就可以找到systemserver进程自己特有的classes,同时也能找到zygote之前预加载的classes。

下面是相关代码,请自行取阅:

  //ZygoteInit类
  private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        省略代码······
        
        //在fork成功后,下面的代码会分别由zygote的线程和systemserver的线程执行,
        //pid==0 代表 systemserver的线程会执行
        if (pid == 0) {
            省略代码······
            //查看下面handleSystemServerProcess方法
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
    
    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        省略代码······
        if (parsedArgs.mInvokeWith != null) {
            省略代码······
        } else { 
            //创建ClassLoader
            ClassLoader cl = getOrCreateSystemServerClassLoader();
            if (cl != null) {
                //为当前线程设置ClassLoader
                Thread.currentThread().setContextClassLoader(cl);
            }

            省略代码······
        }

        /* should never reach here */
    }
3.2.3 打开binder

打开binder这可是非常重要的一步,只有执行了这步被孵化的进程才具有了binder通信的能力,直接看代码吧

//ZygoteInit类
  private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        省略代码······
        
        //在fork成功后,下面的代码会分别由zygote的线程和systemserver的线程执行,
        //pid==0 代表 systemserver的线程会执行
        if (pid == 0) {
            省略代码······
            //查看下面handleSystemServerProcess方法
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
    
    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        省略代码······
        if (parsedArgs.mInvokeWith != null) {
            省略代码······
        } else { 
            省略代码······
            //查看下面 zygoteInit 方法
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mDisabledCompatChanges,
                    parsedArgs.mRemainingArgs, cl);
        }
    }
    
    public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        省略代码······
        //nativeZygoteInit 方法会调用到下面的 com_android_internal_os_ZygoteInit_nativeZygoteInit 方法
        ZygoteInit.nativeZygoteInit(); 
    }
    
    //core/jni/AndroidRuntime.cpp
    static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
    {
      //会调用到下面的onZygoteInit
      gCurRuntime->onZygoteInit();
    }
    
    //cmds/app_process/app_main.cpp
    virtual void onZygoteInit()
    {
        //打开binder驱动
        sp<ProcessState> proc = ProcessState::self();
        //启动binder线程池
        proc->startThreadPool();
    }
3.2.4 进入入口类

这是孵化后的最后一步,也是systemserver进入自己代码的起始点,还记得入口类是SystemServer吧,而这一步就是要进入SystemServer的main方法。

下面是相关方法,请自行取阅:

//ZygoteInit类
  private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        省略代码······
        
        //在fork成功后,下面的代码会分别由zygote的线程和systemserver的线程执行,
        //pid==0 代表 systemserver的线程会执行
        if (pid == 0) {
            省略代码······
            //查看下面handleSystemServerProcess方法
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
    
    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        省略代码······
        if (parsedArgs.mInvokeWith != null) {
            省略代码······
        } else { 
            省略代码······
            //查看下面 zygoteInit 方法
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mDisabledCompatChanges,
                    parsedArgs.mRemainingArgs, cl);
        }
    }
    
    public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        省略代码······
        //查看下面的 applicationInit方法
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
                classLoader);
    }
    
    //RuntimeInit类
    protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        省略代码······
        //查找 static main方法
        //查看下面的 findStaticMain 方法
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }
    
    protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;

        try {
            //查找SystemServer的Class
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            省略代码······
        }

        Method m;
        try {
            //查找SystemServer的main方法
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }

        int modifiers = m.getModifiers();
        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
            throw new RuntimeException(
                    "Main method is not public and static on " + className);
        }

        //返回MethodAndArgsCaller实例,它持有找到的main方法的Method实例
        return new MethodAndArgsCaller(m, argv);
    }

2.4 小结

以上就是我的出生过程,我的“父亲”zygote为了加快子进程的启动速度,在孵化之前做了很多准备工作:加载JVM、加载通用jni方法、预加载classes、预加载resource等,而我systemserver进程在fork成功后与zygote共享在准备阶段产生的这些实例,同时我还打开了binder驱动,这样我才具有了binder通信能力,我还创建了自己的ClassLoader,并且进入了SystemServer类的main方法,至此我“出生”了。

3. 我的启动

我虽然“出生”了,但是我就像人类的婴儿一样,还没有任何的能力,因此我需要经历启动过程,只有经历了启动过程我才真正的“长大成人”。

3.1 解决多service带来的启动问题

大家都知道我有很多很多的service,而启动过程其实就是把所有的service启动起来,service多是好事也同时是坏事,这不在启动过程中就体现出来了,因为service多,在启动过程中肯定会出现某个service的启动要依赖于另外的service,而另外的service又要依赖于别的service;甚至会出现更复杂的情况,某个或多个service要依赖多个其他的service,这种依赖关系就像一团乱麻着实令人头疼。

针对这种情况,确实花费了我很多的脑细胞,想出了管理service对service分类分发启动阶段信号这三个办法来解决此问题。

3.1.1 管理service

管理service就是要对这些service进行管理,不管理确实会出现各种乱套,那该如何管理呢?

老规矩先看下我设计的类图吧
image

如图,我主要设计了SystemService和SystemServiceManager两个类,先来解释下这两个类

SystemService

每个service作为能力提供方的话是Binder类的子类,而要作为一个service被systemserver进程管理的话,那它需要继承SystemService类

该类是一个抽象类,它定义了一些service都会用到的方法比如publishBinderService,它的主要作用就是service可以把自己注册到ServiceManager中。

还定义了一些一些抽象方法,其中onStartonBootPhase特别类似Activity的生命周期方法。

onStart方法

onStart被调用的话,则代表当前的service可以调用publishBinderService方法,把自己注册到ServiceManager中。

onBootPhase方法

systemserver会通过onBootPhase方法告知service当前启动到了哪一步了。

onUserStarting方法

systemserver会通过该方法告知service当前是哪个用户正在启动,其他的onUserXXX方法也是类似作用。

图中ActivityManagerService类为啥没有继承SystemService呢?主要原因是Java只支持单继承,因为ActivityManagerService已经继承了IActivityManager.Stub类

SystemServiceManager

而该类的主要作用就是管理所有的SystemService,它的mService属性存储了所有启动的SystemService。同时它拥有几个简化创建service流程的方法,暂且拿下面这个方法举例子

startService(Class)

该方法把构建service对象添加service对象调用service的onStart方法这几个步骤都集中起来。启动一个service只需要调用该方法,service只需要实现自己的onStart方法逻辑,onStart方法具体啥时候调用,service不需要关心。

3.1.2 对service分类

对service进行分类,把service分为bootstrap servicescore servicesother services,如下图:

image

bootstrap services代表别的service对它们的依赖最多,因此需要最先启动。

3.1.3 分发启动阶段信号

分发启动阶段信号就是告知每个service当前处于启动的哪个阶段了,service根据相应的阶段信号可以做一些自己的事情。

分发启动阶段信号这个词如果不好理解的话,那用个例子来解释:在下载文件的时候,是不是会显示下载进度,而启动阶段信号其实与下载进度非常类似,就是告知service当前的启动进度是啥样。

3.1.4 小结

service继承SystemService,并且把它们都添加到SystemServiceManager,这样就可以把所有的service管理起来;再对service进行分类,按类别来分别启动它们;把启动阶段信号通过SystemServiceManager发送给所有的service,这样service根据启动阶段信号来做自己的事情。以上三种办法可以解决启动service混乱的问题。

3.2 启动

既然多service启动的问题解决了,那咱就进入正题开始介绍我的启动过程吧,我把启动过程分为启动前启动bootstrap services启动core services启动other services进入无休工作模式,当然还有启动Apex services这一步,因为它不是重点在这就不赘述了,那就从这几步来介绍启动。

3.2.1 启动前

启动前是需要做一些准备工作的设置binder线程池个数开启Looper加载android_servers可执行文件创建Context,那就依次来介绍下。

设置binder线程池个数

普通进程打开binder驱动后,能启动最大的binder线程个数是15个,前面也一直在强调我可不是普通的进程,更何况我拥有非常多的service,这些service每个都是binder server,因此我启动的最大binder线程个数是31个

不知道大家有没有考虑过这样的问题:最大binder线程个数少的话会有啥问题?

其中一个重要的问题就是性能降低。我举个例子比如工厂里面正常工作量的话,15个人刚好干完,但是由于工作量加大,15个人的话就干不完了,那这种情况该咋办呢?其中一种是15个人加班干,另外一种是增加工人个数。同样binder最大线程数小的话,就会导致大量的请求积压在队列中,这样就导致性能下降。

下面是相关代码,请自行取阅:

//SystemServer方法
public static void main(String[] args) {
   //run方法在下面
   new SystemServer().run();
}

private void run() {
  省略代码······
  //sMaxBinderThreads的值为31,该方法设置开启binder线程的最大个数
  BinderInternal.setMaxThreads(sMaxBinderThreads);
  省略代码······
}
开启Looper

Looper在Android中的作用不必说,开启了Looper后,其他的线程或者当前线程就可以通过Handler把Message传递到当前线程。

加载android_servers可执行文件

android_servers可执行文件中包含了jni方法和很多native方法,使用System.loadLibrary方法可以把android_servers可执行文件加载到内存中,这样就可以保证jni方法可用

创建Context

Context在Android中的作用那可是非常重要,而在systemserver进程中也是需要Context对象的,会创建两个Context,一个用来做业务逻辑等处理,另一个用来做UI显示,在systemserver也是会显示一些UI的 (比如关机对话框)

下面是相关代码,请自行取阅:

//SystemServer方法
public static void main(String[] args) {
   //run方法在下面
   new SystemServer().run();
}

private void run() {
  省略代码······
  //createSystemContext方法在下面
  createSystemContext();
  省略代码······
}

private void createSystemContext() {
    //创建systemserver具有的ActivityThread对象
    ActivityThread activityThread = ActivityThread.systemMain();
    //获取系统Context对象
    mSystemContext = activityThread.getSystemContext();
    //为Context设置主题
    mSystemContext.setTheme(DEFAULT_SYSTEM_THEME); 

    //该方法会根据mSystemContext创建UI类型的Context,它主要用来显示UI
    final Context systemUiContext = activityThread.getSystemUiContext(); 
    //为systemUiContext设置主题
    systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
}
小结

启动前期主要是为后面启动各种service做准备,接下来看剩下的步骤吧。

3.2.2 启动bootstrap services

还记得我已经对service进行了分类,该阶段就是对bootstrap services进行启动,在此阶段同时还会启动一个Watchdog类,它的作用是监控systemserver进程的对应线程是否发生了锁等待情况,后面会再次详细介绍它。

在此阶段,会发出第一个启动阶段信号PHASE_WAIT_FOR_DEFAULT_DISPLAY (它的值为100),此信号的意思是现在进入等待获取默认显示屏阶段,已经启动的service都会收到该信号,只不过只有DisplayManagerService才关心此信号。DisplayManagerService会尝试获取是否有可用的显示屏,有的话则进入下阶段;否则等待一定时间后还是没有可用的显示屏,则整个启动过程失败。

下面是相关代码,请自行取阅:

//DisplayManagerService
//下面方法会阻塞整个启动流程
public void onBootPhase(int phase) {
        //收到 PHASE_WAIT_FOR_DEFAULT_DISPLAY 信号
        if (phase == PHASE_WAIT_FOR_DEFAULT_DISPLAY) {
            synchronized (mSyncRoot) {
                //获取超时时间
                long timeout = SystemClock.uptimeMillis()
                        + mInjector.getDefaultDisplayDelayTimeout();
                //若没有可用显示屏,则进行等待
                while (mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY) == null
                        || mVirtualDisplayAdapter == null) {
                    long delay = timeout - SystemClock.uptimeMillis();
                    //若超时依然没有获取到可用显示屏,则抛异常
                    if (delay <= 0) {
                        throw new RuntimeException("Timeout waiting for default display "
                                + "to be initialized. DefaultDisplay="
                                + mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY)
                                + ", mVirtualDisplayAdapter=" + mVirtualDisplayAdapter);
                    }
                    省略代码······
                    try {
                        mSyncRoot.wait(delay);
                    } catch (InterruptedException ex) {
                    }
                }
            }
        } 
        省略代码······
    }

3.2.3 启动core services

该阶段主要是启动类别为core的所有service,启动的service确实很多,我在这就不给大家一一介绍了,咱们进入下个阶段。

3.2.4 启动other services

同样此阶段会启动剩下的service,由于启动的service多,也不在此一一赘述了,当然在这个阶段还会启动systemui进程,并且还会启动launcher,在这里介绍下几个关键的启动阶段信号。

PHASE_WAIT_FOR_SENSOR_SERVICE (它的值是200),此信号的意思是进入等待传感器服务阶段,同样所有启动的service都会收到此信号,只有SensorService才会关心它,SensorService在等待期间会阻塞启动过程。

下面是相关代码,请自行取阅:

//SensorService
public void onBootPhase(int phase) {
    if (phase == SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE) {
        ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart,
                    START_NATIVE_SENSOR_SERVICE);
        synchronized (mLock) {
            mSensorServiceStart = null;
        }
    }
}

PHASE_LOCK_SETTINGS_READY (它的值是480),此信号的意思是settings相关的数据你们各service可以使用了,因此已经启动的service,收到该信号后若需要读取settings的数据就可以读取了。

PHASE_SYSTEM_SERVICES_READY (它的值是500),此信号的意思是core services可以被安全的使用了,比如PowerManagerService、PackageManagerService等可以被别的service使用了。

PHASE_ACTIVITY_MANAGER_READY (它的值是550),此信号的意思是ActivityManagerService已经准备好了,广播等都可以使用了

PHASE_BOOT_COMPLETED (它的值是1000),此信号的意思是启动阶段完成,那啥时候发该信号呢?launcher界面展示成功后才会发出该信号,所有已经启动的service收到此信号后就可以做自己该做的事情了。

下面是相关代码,请自行取阅:

//ActivityManagerService

//下面的方法是launcher界面做完动画后会调用
final void finishBooting() {
        省略代码······
        //让所有的service都知道启动完成了
        mSystemServiceManager.startBootPhase(t, SystemService.PHASE_BOOT_COMPLETED);
        省略代码······
    }

3.2.5 进入无休工作模式

在ActivityManagerService发出PHASE_BOOT_COMPLETED信号后,所有的service都欢腾了起来,每个service身上紧绷着的弦也终于放松了,从此刻开始我和我的service小伙伴们就进入了无休工作模式,人类啊千万别在抱怨你们的工作时长长了,在长能有我长吗?

进入无休工作模式的代码也特别简单,如下

//SystemServer
private void run() {
  省略代码······
  Looper.loop(); 
  省略代码·····
}

其实就是调用了Looper.loop()方法,该方法作用是让当前线程会一直循环下去。

3.2.6 小结

我的启动其实就是启动所有的service,而由于service很多它们之间又存在复杂的依赖,导致启动过程很复杂,为了解决此问题,我使用SystemService和SystemServiceManager来管理这些service;并且对这些service进行分类,这样启动过程就可以按照分类来依次启动它们;在启动过程中某些service肯定要依赖于别的service或者别的事件,针对这种情况我想了分发启动阶段信号这么个办法,发出相应的信号后,对该信号关心的service就可以着手处理相关的事情,其中PHASE_BOOT_COMPLETED阶段信号是最关键信号,代表整个启动完成,而发该信号的时机是launcher界面展示完成。收到该信号后,我和我的service就完全准备好了,我们进入无休的工作模式

总结

我是systemserver进程,我虽然是zygote进程的“长子”,但我同时也当担着“父亲”的职责,我会管理由zygote孵化的所有进程。我把我比作Android的大脑,App进程如果想启动Activity那需要找我的ActivityTaskManagerService,如果需要让设备振动那也需要找我的VibratorManagerService,当然还有很多很多的事情都需要找我的service。

这就是我一个独一无二的进程。

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2047633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day34-nginx常用模块

## 0. 网络面试题 网络面试题: TCP三次握手 TCP四次挥手 DNS解析流程 OSI七层模型 抓包工具 tcpdump RAID级别区别 开机启动流程 如何实现不同的网段之间通信(路由器) ip route add 192.168.1.0 255.255.255.0 下一跳的地址或者接口 探测服务器开启了哪些端口(无法登录服务器…

嵌入式开发如何看芯片数据手册

不管什么芯片手册&#xff0c;它再怎么写得天花乱坠&#xff0c;本质也只是芯片的使用说明书而已。而说明书一个最显著的特点就是必须尽可能地使用通俗易懂的语句&#xff0c;向使用者交代清楚该产品的特点、功能以及使用方法。 以TMP423为例&#xff0c;这是一个测量温度的芯…

【密码学】密钥管理:①基本概念和密钥生成

密钥管理是处理密钥从产生到最终销毁的整个过程的有关问题&#xff0c;包括系统的初始化及密钥的产生、存储、备份与恢复、装入、分配、保护、更新、控制、丢失、撤销和销毁等内容。 一、密钥管理技术诞生的背景 随着计算机网络的普及和发展&#xff0c;数据传输和存储的安全问…

蓝牙音视频远程控制协议(AVRCP) command跟response介绍

零.声明 本专栏文章我们会以连载的方式持续更新&#xff0c;本专栏计划更新内容如下&#xff1a; 第一篇:蓝牙综合介绍 &#xff0c;主要介绍蓝牙的一些概念&#xff0c;产生背景&#xff0c;发展轨迹&#xff0c;市面蓝牙介绍&#xff0c;以及蓝牙开发板介绍。 第二篇:Trans…

智慧运维:数据中心可视化管理平台

图扑智慧运维数据中心可视化管理平台&#xff0c;实时监控与数据分析&#xff0c;优化资源分配&#xff0c;提升运维效率&#xff0c;确保数据中心的安全稳定运行。

Linux进程间通信——匿名管道

文章目录 进程间通信管道匿名管道匿名管道使用 进程间通信 进程设计的特点之一就是独立性&#xff0c;要避免其他东西影响自身的数据 但有时候我们需要共享数据或者传递信息&#xff0c;传统的父子进程也只能父进程传递给子进程信息 因此进程间通信还是很必要的&#xff0c;…

Apollo9.0 PNC源码学习之Planning模块—— Lattice规划(三):静态障碍物与动态障碍物ST图构建

参考文章: (1)Apollo6.0代码Lattice算法详解——Part4:计算障碍物ST/SL图 (2)自动驾驶规划理论与实践Lattice算法详解 1 计算障碍物ST/SL图 计算障碍物ST/SL图主要函数关系图: // 通过预测得到障碍物list auto ptr_prediction_querier = std::make_shared<Predict…

2024新型数字政府综合解决方案(五)

新型数字政府综合解决方案通过集成人工智能、大数据、区块链和云计算技术&#xff0c;打造了一个智能化、透明化和高效的政务服务平台&#xff0c;旨在提升政府服务的响应速度、处理效率和数据安全性。该方案实现了跨部门的数据共享与实时更新&#xff0c;通过智能化的流程自动…

Waterfox vG6.0.8 官方版下载和安装步骤(一款响应速度非常快的浏览器)

前言 Waterfox 水狐浏览器&#xff0c;从字面上我们可以轻松的了解该款浏览器的一些特点。Waterfox是通过Mozilla官方认证的纯64位版火狐浏览器&#xff0c;而Waterfox 10采用Firefox 10官方源码编译而成&#xff0c;改进了大内存和64位计算的细节&#xff0c;在64位Windows系…

用Python读取Excel数据在PPT中的创建图表

可视化数据已成为提高演示文稿专业度的关键因素之一。使用Python从Excel读取数据并在PowerPoint幻灯片中创建图表不仅能够极大地简化图表创建过程&#xff0c;还能确保数据的准确性和图表的即时性。通过Python这一桥梁&#xff0c;我们可以轻松实现数据自动化处理和图表生成&am…

MyBatis全解

目录 一&#xff0c; MyBatis 概述 1.1-介绍 MyBatis 的历史和发展 1.2-MyBatis 的特点和优势 1.3-MyBatis 与 JDBC 的对比 1.4-MyBatis 与其他 ORM 框架的对比 二&#xff0c; 快速入门 2.1-环境搭建 2.2-第一个 MyBatis 应用程序 2.3-配置文件详解 (mybatis-config.…

软件需求设计分析报告(Word原件)

第1章 序言 第2章 引言 2.1 项目概述 2.1.1 项目背景 2.1.2 项目目标 2.2 编写目的 2.3 文档约定 2.4 预期读者及阅读建议 第3章 技术要求 3.1 软件开发要求 3.1.1 接口要求 3.1.2 系统专有技术 3.1.3 查询功能 3.1.4 数据安全 3.1.5 可靠性要求 3.1.6 稳定性要求 3.1.7 安全性…

练习:python条件语句、循环语句和函数的综合运用

需求描述&#xff1a; 期望输出效果&#xff1a; 练习成果&#xff1a; #简单的银行业务流程 many 50000 def main_menu():print("----------主菜单----------"f"\n{name}您好&#xff0c;欢迎来到ATM&#xff0c;请选择操作&#xff1a;""\n查询余…

鼠标手势软件,效率办公必备!移动鼠标即可执行命令

鼠标手势软件是一种通过在屏幕上绘制特定手势来触发预设操作或命令的工具&#xff0c;它能够极大地提高用户的操作效率&#xff0c;特别是在进行重复性工作时尤为明显。这类软件通常支持多种手势操作&#xff0c;如拖拽、双击、滚动等&#xff0c;并允许用户自定义手势以适应个…

【Linux】系列入门摘抄笔记-8-权限管理chmod/chown

Linux操作系统中文件的基本权限由9个字符组成&#xff0c;分别为属主、属组和其他用户&#xff0c;用于规定是否对文件有读、写和执行权限。 文件/目录的权限与归属 目录列表中&#xff0c;有9列 第一列&#xff1a;文件类型与权限&#xff08;共10个字符&#xff0c;分为四组…

RAG完整构建流程-从入门到放弃

RAG完整构建流程 LLM模型缺陷&#xff1a; ​ 知识是有局限性的(缺少垂直领域/非公开知识/数据安全) ​ 知识实时性(训练周期长、成本高) ​ 幻觉问题(模型生成的问题) ​ 方法&#xff1a;Retrieval-Augmented Generation&#xff08;RAG&#xff09; ​ 检索&#xff1…

网络安全实训第三天(文件上传、SQL注入漏洞)

1 文件上传漏洞 准备一句话文件wjr.php.png&#xff0c;进入到更换头像的界面&#xff0c;使用BP拦截选择文件的请求 拦截到请求后将wjr.php.png修改为wjr.php&#xff0c;进行转发 由上图可以查看到上传目录为网站目录下的upload/avator,查看是否上传成功 使用时间戳在线工具…

有哪些好用的桌面管理

Fences&#xff08;适用于Windows&#xff09;&#xff1a; Fences 是一个非常流行的 Windows 桌面组织工具&#xff0c;它允许用户将图标、文件、文件夹等拖放到名为“Fences”的区域中&#xff0c;从而保持桌面的整洁有序。Fences 还支持自动隐藏空白的 Fences&#xff0c;以…

PC端实现语音识别功能

场景需求&#xff1a; 点击录音按钮&#xff1a; 如没有检测到录音设备&#xff0c;提示&#xff1a;“无法找到麦克风设备&#xff0c;请检查设备连接”。如录音设备连接正常&#xff1a;录音按钮变成录制状态&#xff0c;开始录制声音&#xff0c;同时输入框禁止键盘输入。 再…

修改esxi root用户密码

登录vcenter&#xff0c;鼠标右键点击主机&#xff0c;选择主机配置文件------提取主机配置文件 然后对提取的文件输入一个名称清晰容易分辨的名称&#xff0c;如xxx主机修改密码配置文件。 选择顶部的菜单&#xff0c;选择“策略和配置文件” 选择左侧的主机配置文件&#xf…