Inicio del sistema Android (1) - proceso de inicio del proceso init

AndroidEl arranque del dispositivo debe pasar por tres fases: Boot Loader, Linux Kernely Androidservicios del sistema. Estrictamente hablando, Androidel sistema es en realidad una serie de "procesos de servicio" que se ejecutan en Linuxel kernel, y el "ancestro" de estos procesos de servicio es initel proceso.

Proceso de inicio del sistema Android

Boot LoaderEs un pequeño programa antes de que se ejecute el kernel del sistema operativo. A través de este pequeño programa, el dispositivo de hardware se puede inicializar y se puede establecer el mapa de asignación del espacio de memoria, para llevar el entorno de software y hardware del sistema a un estado adecuado, a fin de preparar el entorno correcto para el llamada final del kernel del sistema operativo.

initEl proceso es Androidel primer proceso en el espacio de usuario en el sistema, PID(número de proceso) es 1, es Androidun paso clave en el proceso de inicio del sistema, como el primer proceso, se le asignan muchas responsabilidades de trabajo extremadamente importantes, se init.rcconstruye analizando archivos El estado de ejecución inicial del sistema, es decir, Androidla mayoría de los servicios del sistema init.rcse describen en el archivo de script y se inician de acuerdo con ciertas condiciones.

proceso de inicio

initEl inicio del proceso ha hecho mucho trabajo, en general, principalmente hace las siguientes tres cosas:

  • Crear ( mkdir) y montar ( mount) los directorios de archivos necesarios para el inicio;
  • Inicializa e inicia el servicio de propiedad( property service);
  • Analice init.rcel archivo de configuración e inicie Zygoteel proceso;

initUn proceso se compone de varios archivos de origen, que se encuentran en el directorio de origen system/core/init.

Después de cargar el núcleo, primero buscará el archivo ( ) Linuxen los archivos del sistema e iniciará el proceso. init.rc/system/core/rootdir/init.rcinitSe ha ajustado la función de entrada del proceso, ya no es la función de la anterior , ** se reemplaza por la función de , la finalidad de esta es separar el trabajo de cada etapa, haciendo más concisa la lógica del código, **El código es el Android 10siguienteinit/system/core/init/init.cppmainsystem/core/init/main.cppmain

// /system/core/init/main.cpp
int main(int argc, char** argv) {
    #if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
    #endif

    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv); // 1
}

La primera etapa: crear y montar el directorio de archivos requerido para el inicio

1El código en el comentario es initla primera etapa del proceso, que se implementa first_state_init.cppen el archivo ( /system/core/init/first_stage_init.cpp), y el código se ve así:

// /system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers(); 
    }
 
    boot_clock::time_point start_time = boot_clock::now(); // 用于记录启动时间

    umask(0); // Clear the umask. 清理umask

    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    
    /*---------- 创建和挂载启动所需的文件目录 ----------*/
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755")); // 挂载 tmpfs 文件
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL)); // 挂载 devpts 文件系统

#define MAKE_STR(x) __STRING(x)
  
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC))); // 挂载 proc 文件系统
#undef MAKE_STR
    
    CHECKCALL(chmod("/proc/cmdline", 0440)); // 8.0 新增,收紧了 cmdline 目录的权限
    gid_t groups[] = {AID_READPROC}; // 8.0 新增,增加了个用户组
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL)); // 挂载的 sysfs 文件系统
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL)); // 8.0 新增

    // 提前创建了 kmsg 设备节点文件,用于输出 log 信息
    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }

    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));

    CHECKCALL(mkdir("/mnt/vendor", 0755)); // 创建可供读写的 vendor 目录
    CHECKCALL(mkdir("/mnt/product", 0755)); // 创建可供读写的 product 目录

    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));

    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));
#undef CHECKCALL
    SetStdioToDevNull(argv);
	// 初始化 Kernel Log 系统,这样就可以从外界获取 Kernel 日志
    InitKernelLogging(argv); 
    ...
}

Como se puede ver en el código anterior, initla primera etapa del proceso se utiliza principalmente para crear y montar los directorios de archivos necesarios para el iniciotmpfs , en los que se montan los sistemas de archivos , , y, que devptsson todos directorios de tiempo de ejecución del sistema, es decir, para digamos, solo existirá cuando el sistema esté funcionando y desaparecerá cuando el sistema se detenga.procsysfsselinuxfs5

Cuatro tipos de sistemas de archivos:

  • tmpfs: un sistema de archivos de memoria virtual que almacena todos los archivos en la memoria virtual. Si tmpfsse desmonta el sistema de archivos, todos los contenidos que se encuentran debajo ya no existirán. tmpfsSe pueden usar ambos RAM, o se puede usar la partición de intercambio, y el tamaño se cambiará de acuerdo con las necesidades reales. tmpfsLa velocidad de es asombrosa, después de todo, reside en RAM, incluso con la partición de intercambio, el rendimiento sigue siendo muy bueno. Dado que tmpfsreside en RAM, su contenido no es persistente. Después de apagar la alimentación, tmpfsel contenido del contenido desaparece, que también es tmpfsla causa raíz de la llamada;
  • devpts: proporciona una interfaz estándar para el pseudoterminal y su punto de montaje estándar es /dev/pts. Siempre que ptyel dispositivo compuesto principal esté abierto, /dev/ptmxse creará /dev/ptsdinámicamente un nuevo ptyarchivo de dispositivo en ;
  • proc: Un sistema de archivos virtual muy importante, que puede considerarse como la interfaz de la estructura de datos interna del kernel, a través del cual podemos obtener información del sistema y también puede modificar parámetros específicos del kernel en tiempo de ejecución;
  • sysfs: procSimilar al sistema de archivos, también es un sistema de archivos virtual que no ocupa espacio en el disco. Por lo general, se monta /sysen el directorio. sysfsEl kernel introduce el sistema de archivos Linux 2.6, que organiza los dispositivos y buses conectados al sistema en un archivo jerárquico para que se pueda acceder a ellos en el espacio del usuario;

Fase 2: inicializar e iniciar el servicio de propiedad

// system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {
    
    
    ...
    // 1 初始化属性服务
    property_init();
    ...
    // 创建 epoll 句柄
    Epoll epoll;
    if (auto result = epoll.Open(); !result) {
    
    
        PLOG(FATAL) << result.error();
    }
    // 2 创建 Handler 处理子进程的终止信号,如果子进程(Zygote)异常退出,init进程会调用该函数中设定的信号处理函数来进行处理 
    InstallSignalFdHandler(&epoll);

    property_load_boot_defaults(load_debug_prop);
    UmountDebugRamdisk();
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
    // 3 启动属性服务
    StartPropertyService(&epoll);
	...
}

1Llame a la función en el comentario property_init()para inicializar la propiedad y 3llame StartPropertyServicea la función en el comentario para iniciar el servicio de propiedad.

2Llamar a la función en el comentario InstallSignalFdHandlerse usa para configurar la función de procesamiento de señales del subproceso, principalmente para evitar initque el subproceso del proceso se convierta en un proceso zombi. Para evitar la aparición de procesos zombis, el sistema enviará una señal cuando el proceso secundario se suspenda o finalice SIGCHLD, y InstallSignalFdHandlerla función se utiliza para recibir SIGCHLDla señal (solo procesa SIGCHLDla señal de finalización del proceso internamente).

proceso zombi

Proceso zombi y daño: En UNIX/Linux, el proceso principal utiliza forkel método para crear un proceso secundario. Después de que finaliza el proceso secundario, si no se puede notificar al proceso principal, aunque el proceso secundario ya haya salido, se le reserva una determinada cantidad de tiempo. en la tabla de procesos del sistema Información (como número de proceso, estado de salida, tiempo de ejecución, etc.), este proceso se denomina proceso zombie. La tabla de procesos del sistema es un recurso limitado. Si la tabla de procesos del sistema está agotada por procesos zombis, es posible que el sistema no pueda crear nuevos procesos.

servicio de atributos

WindowHay un administrador de registro en la plataforma, y ​​el contenido del registro toma la forma de pares clave-valor para registrar cierta información de uso de los usuarios y el software. Incluso si el sistema o el software se reinicia, aún puede realizar el trabajo de inicialización correspondiente de acuerdo con los registros anteriores en el registro. AndroidTambién se proporciona un mecanismo similar, denominado servicios de propiedad.

initCuando comience el proceso, se iniciará el servicio de atributos y se le asignará memoria para almacenar estos atributos. Si necesita leer estos atributos directamente, el código relacionado con el servicio de atributos en la función de tendrá las siguientes dos líneas init.cpp:main

// /system/core/init/init.cpp
property_init();
StartPropertyService(&epoll);

Estas dos líneas de código se utilizan para inicializar la configuración del servicio de propiedades e iniciar el servicio de propiedades.

1 Inicialización y puesta en marcha del servicio de propiedad

property_initLa implementación específica de la función es la siguiente:

// /system/core/init/property_service.cpp
void property_init() {
    
    
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
    
    
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
    
    
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}

1La función de comentario __system_property_area_init()se utiliza para inicializar el área de memoria de atributos. A continuación, observe StartPropertyServiceel código de la función:

// /system/core/init/property_service.cpp
void StartPropertyService(Epoll* epoll) {
    
    
    selinux_callback cb;
    cb.func_audit = SelinuxAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    property_set("ro.property_service.version", "2");

    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr); // 1
    if (property_set_fd == -1) {
    
    
        PLOG(FATAL) << "start_property_service socket creation failed";
    }

    listen(property_set_fd, 8); // 2

    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
    
     // 3
        PLOG(FATAL) << result.error();
    }
}

1Crear sin bloqueo en el comentario Socket. 2Llame listena la función en el comentario property_set_fdpara escuchar, de modo que lo creado Socketse convierta en server, es decir, el servicio de atributos; listenel segundo parámetro de la función se establece en 8, lo que significa que el servicio de atributos puede 8proporcionar servicios para la mayoría de los usuarios que intentan establecer atributos al mismo tiempo. 3El código en el comentario property_set_fdse coloca epollpara epollmonitorear property_set_fd: property_set_fdcuando los datos llegan, initel proceso llamará RegisterHandlerpara su procesamiento.

En Linuxel nuevo kernel, se epollusa para reemplazar el kernel select, que se ha mejorado para manejar una gran cantidad de descriptores de archivos.Es una versión mejorada de la interfaz de multiplexación , que puede mejorar significativamente el rendimiento de los programas con solo una pequeña cantidad de descriptores de archivos. conexiones activas en un gran número de conexiones simultáneas La utilización del sistema bajo la condición de . El tipo de datos interno utilizado para ahorrar tiempo es un árbol rojo-negro, que tiene una velocidad de búsqueda rápida. La matriz utilizada para almacenar información tiene una velocidad de búsqueda muy lenta. Solo cuando se espera una pequeña cantidad de descriptores de archivo, la eficiencia de y será similar.epollLinuxpollLinuxI/Oselect/pollCPUepollselectepollselect

2 El servicio procesa las solicitudes de los clientes

De lo anterior, podemos saber que cuando el servicio de atributos recibe la solicitud del cliente, llamará handle_property_set_fda la función para procesarla:

// /system/core/init/property_service.cpp
static void handle_property_set_fd() {
    
    
    switch (cmd) {
    
    
        case PROP_MSG_SETPROP: {
    
    
            char prop_name[PROP_NAME_MAX];
            char prop_value[PROP_VALUE_MAX];
			// 如果Socket读取不到属性数据则返回
            if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
                !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
    
    
                PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
                return;
            }

            prop_name[PROP_NAME_MAX-1] = 0;
            prop_value[PROP_VALUE_MAX-1] = 0;

            const auto& cr = socket.cred();
            std::string error;
            uint32_t result =
                HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error); // 1
            if (result != PROP_SUCCESS) {
    
    
                LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
                    << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
                    << error;
            }

            break;
        }
        ...
    }

La función se encapsula aún más 1en el comentario , como se muestra a continuación:handle_property_set_fd

// /system/core/init/property_service.cpp
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr, std::string* error) {
    
    
    ...
	// 如果属性名称以 `ctl.` 开头,说明是控制属性
    if (StartsWith(name, "ctl.")) {
    
     // 1
        // 设置控制属性
        HandleControlMessage(name.c_str() + 4, value, cr.pid); // 2
        return PROP_SUCCESS;
    }

    // sys.powerctl is a special property that is used to make the device reboot.  We want to log
    // any process that sets this property to be able to accurately blame the cause of a shutdown.
    if (name == "sys.powerctl") {
    
    
        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
        std::string process_cmdline;
        std::string process_log_string;
        if (ReadFileToString(cmdline_path, &process_cmdline)) {
    
    
            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process
            // path.
            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
        }
        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
            << process_log_string;
    }

    if (name == "selinux.restorecon_recursive") {
    
    
        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
    }

    return PropertySet(name, value, error); // 3
}

Hay dos tipos de atributos del sistema, uno son los atributos comunes y el otro son los atributos de control, que se utilizan para ejecutar algunos comandos, como la animación de inicio. Por lo tanto, se HandlePropertySetdivide en dos puntos de procesamiento, una parte se ocupa de los atributos de control y la otra parte se utiliza para tratar los atributos comunes. Aquí solo se analizan los atributos comunes. Si 1el nombre del atributo en el comentario ctl.comienza con , significa que es un atributo de control. Si se satisface el permiso del cliente, HandleControlMessagese llamará a la función para modificar el atributo de control. Si es un atributo ordinario, bajo la condición de la autoridad del cliente Mazu, se llamará al comentario en el comentario 3para PropertySetmodificar el atributo ordinario, como se muestra a continuación:

// /system/core/init/property_service.cpp
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
    
    
    size_t valuelen = value.size();
	// 判断是否合法
    if (!IsLegalPropertyName(name)) {
    
    
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }

    ...
	// 从属性存储空间查找该属性
    prop_info* pi = (prop_info*) __system_property_find(name.c_str()); // 1
    // 如果属性存在
    if (pi != nullptr) {
    
    
        // ro.* properties are actually "write-once". 如果属性名称以"ro."开头,则表示只读,不能修改,直接返回
        if (StartsWith(name, "ro.")) {
    
    
            *error = "Read-only property was already set";
            return PROP_ERROR_READ_ONLY_PROPERTY;
        }
		// 如果属性存在,就更新属性值
        __system_property_update(pi, value.c_str(), valuelen);
    } else {
    
    
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
    
    
            *error = "__system_property_add failed";
            return PROP_ERROR_SET_FAILED;
        }
    }

    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    // 属性名称以"persist."开头的处理部分
    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
    
    
        WritePersistentProperty(name, value);
    }
    property_changed(name, value);
    return PROP_SUCCESS;
}

PropertySetLa función modifica principalmente atributos comunes. En primer lugar, es necesario juzgar si el atributo es legal. Si es legal, 1busque el atributo en el espacio de almacenamiento de atributos en la anotación. Si el atributo existe, actualice el valor del atributo; de lo contrario, agregue el atributo Además, ro. persist.las propiedades cuyos nombres comienzan con se manejan en consecuencia.

La tercera etapa: analizar init.rcel archivo de configuración

// /system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    
    
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
    
    
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
    
    
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
    
    
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/product_services/etc/init")) {
    
    
            late_import_paths.emplace_back("/product_services/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
    
    
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
    
    
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
    
    
        parser.ParseConfig(bootscript);
    }
}  

init.rces un archivo de configuración muy importante, es un script escrito por Androidel lenguaje de inicialización ( ), este lenguaje contiene principalmente tres tipos de sentencias: , , y .Android Init Language5Action(行为)Command(命令)Service(服务)Option(选项)Import(引入)

Configuración de init.rc

init.rcEl archivo se divide aproximadamente en dos partes, una parte es la "lista de acciones" ( ) onque comienza con la palabra clave y la otra parte es la "lista de servicios" ( ) que comienza con la palabra clave .action listserviceservice list

La lista de acciones se usa para crear el directorio requerido y especificar permisos para algunos archivos específicos, y la lista de servicios se usa para registrar initalgunos subprocesos que el proceso necesita para comenzar. Como sigue:

// /system/core/rootdir/init.rc
on init
    sysclktz 0
    copy /proc/cmdline /dev/urandom
    copy /system/etc/prop.default /dev/urandom
    ...
on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain
    ...

on inity on bootes Actionuna declaración de tipo, su formato es el siguiente:

on <triggger> [&& <trigger>]*  // 设置触发器
   <command>
   <command>  // 动作触发之后要执行的命令

Para saber cómo crear Zygoet, mira principalmente Servicela declaración de tipo , su formato es el siguiente:

service ueventd /system/bin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0
    shutdown critical

ServiceTipo de formato de declaración:

service <name> <pathname> [<argument>]* // <service的名字><执行程序路径><传递参数>
	<option>  // option 是 service 的修饰词,影响什么时候、如何启动 service
	<option>
	...

Cabe señalar que el archivo se divide en , y cada servicio corresponde a un Android 8.0archivo . Los scripts de inicio se definen en, por ejemplo :init.rc.rcZygoteinit.zygoteXX.rcinit.zygote64.rc

// /system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary 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
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

Analice el significado del código anterior de acuerdo con Serviceel formato de la declaración de tipo. ServiceSe utiliza para notificar inital proceso que cree Zygoteun proceso llamado , la ruta de ejecución de este proceso es /system/bin/app_process64, y el siguiente código es app_process64el parámetro que se le pasará. class mainSe refiere Zygotea classnamepara main.

declaración Servicede tipo de análisis

init.rcTanto Actionla declaración de tipo como Servicela declaración de tipo tienen clases correspondientes para analizar, Actionla declaración de tipo usa ActionParserpara analizar y Servicela declaración de tipo usa ServiceParserpara analizar. Debido a que el análisis principal está aquí Zygote, solo se presenta ServiceParser.

ServiceParserEl código de implementación está system/core/init/service.cppen . A continuación, verifique ServiceParsercómo analizar el tipo de declaración mencionado anteriormente Service, se usarán dos funciones: una es que ParseSectionanalizará el archivo Servicedel .rc, por ejemplo, como se mencionó anteriormente init.zygote64.rc, ParseSectionla función se usa principalmente para construir Serviceel estante del; el otro es ParseLineSection, resuelve las subclaves para siempre. El código se ve así:

// system/core/init/service.cpp
Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
                                            const std::string& filename, int line) {
    
    
    if (args.size() < 3) {
    
     // 判断Service是否有name与可执行程序
        return Error() << "services must have a name and a program";
    }

    const std::string& name = args[1];
    if (!IsValidName(name)) {
    
     // 检查Service的name是否有效
        return Error() << "invalid service name '" << name << "'";
    }

    filename_ = filename;

    Subcontext* restart_action_subcontext = nullptr;
    if (subcontexts_) {
    
    
        for (auto& subcontext : *subcontexts_) {
    
    
            if (StartsWith(filename, subcontext.path_prefix())) {
    
    
                restart_action_subcontext = &subcontext;
                break;
            }
        }
    }

    std::vector<std::string> str_args(args.begin() + 2, args.end());

    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
    
    
        if (str_args[0] == "/sbin/watchdogd") {
    
    
            str_args[0] = "/system/bin/watchdogd";
        }
    }

    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args); // 1
    return Success();
}

Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
    
    
    return service_ ? service_->ParseLine(std::move(args)) : Success();
}

En el comentario 1, se construye un objeto de acuerdo con los parámetros Service. La función se llama después de analizar los datos EndSection:

// system/core/init/service.cpp
Result<Success> ServiceParser::EndSection() {
    
    
    if (service_) {
    
    
        Service* old_service = service_list_->FindService(service_->name());
        if (old_service) {
    
    
            if (!service_->is_override()) {
    
    
                return Error() << "ignored duplicate definition of service '" << service_->name()
                    << "'";
            }

            if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) {
    
    
                return Error() << "cannot update a non-updatable service '" << service_->name()
                    << "' with a config in APEX";
            }

            service_list_->RemoveService(*old_service);
            old_service = nullptr;
        }

        service_list_->AddService(std::move(service_));
    }

    return Success();
}

EndSectionLa función devuelve la llamada ServiceManager.AddServicea la función, la siguiente es AddServicela función:

// system/core/init/service.cpp
void ServiceList::AddService(std::unique_ptr<Service> service) {
    
    
    services_.emplace_back(std::move(service)); // 1
}

1El código en el comentario Serviceagrega el objeto a Servicela lista vinculada. ServiceEn términos generales, el proceso de análisis consiste en crear Serviceel objeto de acuerdo con los parámetros, luego completar Serviceel objeto de acuerdo con el contenido del campo de opción y finalmente Serviceagregar el objeto a la lista vinculada vectorde tipo Service.

Fase cuatro: iniciar Zygoteel proceso

Después de comprender Serviceel proceso de análisis, el siguiente paso es initcómo iniciar el proceso ServiceAquí explicamos principalmente cómo iniciar Zygoteel proceso Service. En Zygoteel script de inicio de , puede conocer el comportamiento Zygotede . Tiene el siguiente código de configuración:classnamemaininit.rc

// /system/core/rootdir/init.rc
on nonencrypted
    class_start main // 1
    class_start late_start

Entre ellos, class_startes uno COMMAND, y la función correspondiente es do_class_start. Los comentarios 1que comienzan con los classnamede mainson Service, por lo tanto , se utilizan para iniciar la función definida de la siguiente manera Zygote:classnamemainclass_start mainZygotedo_class_startbuiltins.cpp

// /system/core/init/builtins.cpp
static Result<Success> do_class_start(const BuiltinArguments& args) {
    
    
    // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
        return Success();
    // Starting a class does not start services which are explicitly disabled.
    // They must  be started individually.
    for (const auto& service : ServiceList::GetInstance()) {
    
     // 1
        if (service->classnames().count(args[1])) {
    
    
            if (auto result = service->StartIfNotDisabled(); !result) {
    
    
                LOG(ERROR) << "Could not start service '" << service->name()
                    << "' as part of class '" << args[1] << "': " << result.error();
            }
        }
    }
    return Success();
}

En el comentario 1, recorrerá Servicela lista enlazada, encontrará classnameel que maines Zygotey ejecutará StartIfNotDisabledla función, como se muestra a continuación:

// /system/core/init/service.cpp
Result<Success> Service::StartIfNotDisabled() {
    
    
    if (!(flags_ & SVC_DISABLED)) {
    
     // 1
        return Start();
    } else {
    
    
        flags_ |= SVC_DISABLED_START;
    }
    return Success();
}

En el comentario 1, si Serviceno se encuentra la opción .rcconfigurada en el archivo correspondiente disable, se llamará a la función Start()para iniciarla Service, y la opción no está configurada en Zygoteel correspondiente , entonces verifique la función:init.zygote64.rcdisabledStart()

// /system/core/init/service.cpp
Result<Success> Service::Start() {
    
    
    ...
    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    // 如果Service已经运行,则不启动
    if (flags_ & SVC_RUNNING) {
    
     
        if ((flags_ & SVC_ONESHOT) && disabled) {
    
    
            flags_ |= SVC_RESTART;
        }
        // It is not an error to try to start a service that is already running.
        return Success();
    }
    ...
    // 判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
    
    
        flags_ |= SVC_DISABLED;
        return ErrnoError() << "Cannot find '" << args_[0] << "'";
    }
    ...
    // 如果子进程没有启动,则调用fork函数创建子进程
    pid_t pid = -1; // 1
    if (namespace_flags_) {
    
    
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
    
    
        pid = fork();
    }

    // 当前代码逻辑在子进程中运行
    if (pid == 0) {
    
     // 2
        umask(077);
		...
        if (!ExpandArgsAndExecv(args_, sigstop_)) {
    
    
            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
        }

        _exit(127);
    }
}
    
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
    
    
    std::vector<std::string> expanded_args;
    std::vector<char*> c_strings;

    expanded_args.resize(args.size());
    c_strings.push_back(const_cast<char*>(args[0].data()));
    for (std::size_t i = 1; i < args.size(); ++i) {
    
    
        if (!expand_props(args[i], &expanded_args[i])) {
    
    
            LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
        }
        c_strings.push_back(expanded_args[i].data());
    }
    c_strings.push_back(nullptr);

    if (sigstop) {
    
    
        kill(getpid(), SIGSTOP);
    }

    // 调用execv函数,启动service子进程
    return execv(c_strings[0], c_strings.data()) == 0; // 3
}

Primero juzgue Servicesi se ha estado ejecutando, si se está ejecutando, no volverá a iniciarse, si el programa va al comentario 1, significa que el subproceso no se ha iniciado, luego llame fork()a la función para crear un subproceso, y devuelva pidel valor, 2si en el comentario pid == 0, indica la lógica del código actual ejecutado en subprocesos. 3Llame a la función en el comentario execv, Serviceel proceso secundario se iniciará e ingresará la función Servicedel main.

Si es Service, Zygotela Zygoteruta de ejecución es system/bin/app_process64, y el archivo correspondiente es app_main.cpp, entonces entrará en la función app_main.cppde main, es decir, en la función Zygotede main, el código es el siguiente:

// frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
    
    
    ...
    if (zygote) {
    
    
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote); // 1
    } else if (className) {
    
    
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
    
    
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

1El código en el comentario es llamar runtime.starta la función para iniciar Zygote, hasta ahora, Zygoteha comenzado.

referencia

init.rc Análisis detallado
de SystemServer Service y ServiceManager Service
[Arranque del sistema Android 10] Series – init (proceso Tianzi No. 1)

Supongo que te gusta

Origin blog.csdn.net/xingyu19911016/article/details/127451545
Recomendado
Clasificación