Android
El arranque del dispositivo debe pasar por tres fases: Boot Loader
, Linux Kernel
y Android
servicios del sistema. Estrictamente hablando, Android
el sistema es en realidad una serie de "procesos de servicio" que se ejecutan en Linux
el kernel, y el "ancestro" de estos procesos de servicio es init
el proceso.
Boot Loader
Es 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.
init
El proceso es Android
el primer proceso en el espacio de usuario en el sistema, PID
(número de proceso) es 1
, es Android
un paso clave en el proceso de inicio del sistema, como el primer proceso, se le asignan muchas responsabilidades de trabajo extremadamente importantes, se init.rc
construye analizando archivos El estado de ejecución inicial del sistema, es decir, Android
la mayoría de los servicios del sistema init.rc
se describen en el archivo de script y se inician de acuerdo con ciertas condiciones.
init
El 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.rc
el archivo de configuración e inicieZygote
el proceso;
init
Un 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 ( ) Linux
en los archivos del sistema e iniciará el proceso. init.rc
/system/core/rootdir/init.rc
init
Se 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 10
siguienteinit
/system/core/init/init.cpp
main
system/core/init/main.cpp
main
// /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
1
El código en el comentario es init
la primera etapa del proceso, que se implementa first_state_init.cpp
en 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, init
la 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 devpts
son 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.proc
sysfs
selinuxfs
5
Cuatro tipos de sistemas de archivos:
tmpfs
: un sistema de archivos de memoria virtual que almacena todos los archivos en la memoria virtual. Sitmpfs
se desmonta el sistema de archivos, todos los contenidos que se encuentran debajo ya no existirán.tmpfs
Se pueden usar ambosRAM
, o se puede usar la partición de intercambio, y el tamaño se cambiará de acuerdo con las necesidades reales.tmpfs
La velocidad de es asombrosa, después de todo, reside enRAM
, incluso con la partición de intercambio, el rendimiento sigue siendo muy bueno. Dado quetmpfs
reside enRAM
, su contenido no es persistente. Después de apagar la alimentación,tmpfs
el contenido del contenido desaparece, que también estmpfs
la 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 quepty
el dispositivo compuesto principal esté abierto,/dev/ptmx
se creará/dev/pts
dinámicamente un nuevopty
archivo 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
:proc
Similar al sistema de archivos, también es un sistema de archivos virtual que no ocupa espacio en el disco. Por lo general, se monta/sys
en el directorio.sysfs
El kernel introduce el sistema de archivosLinux 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);
...
}
1
Llame a la función en el comentario property_init()
para inicializar la propiedad y 3
llame StartPropertyService
a la función en el comentario para iniciar el servicio de propiedad.
2
Llamar a la función en el comentario InstallSignalFdHandler
se usa para configurar la función de procesamiento de señales del subproceso, principalmente para evitar init
que 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 InstallSignalFdHandler
la función se utiliza para recibir SIGCHLD
la señal (solo procesa SIGCHLD
la señal de finalización del proceso internamente).
proceso zombi
Proceso zombi y daño: En UNIX/Linux
, el proceso principal utiliza fork
el 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
Window
Hay 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. Android
También se proporciona un mecanismo similar, denominado servicios de propiedad.
init
Cuando 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_init
La 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";
}
}
1
La función de comentario __system_property_area_init()
se utiliza para inicializar el área de memoria de atributos. A continuación, observe StartPropertyService
el 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();
}
}
1
Crear sin bloqueo en el comentario Socket
. 2
Llame listen
a la función en el comentario property_set_fd
para escuchar, de modo que lo creado Socket
se convierta en server
, es decir, el servicio de atributos; listen
el segundo parámetro de la función se establece en 8
, lo que significa que el servicio de atributos puede 8
proporcionar servicios para la mayoría de los usuarios que intentan establecer atributos al mismo tiempo. 3
El código en el comentario property_set_fd
se coloca epoll
para epoll
monitorear property_set_fd
: property_set_fd
cuando los datos llegan, init
el proceso llamará RegisterHandler
para su procesamiento.
En Linux
el nuevo kernel, se epoll
usa 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.epoll
Linux
poll
Linux
I/O
select/poll
CPU
epoll
select
epoll
select
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_fd
a 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 1
en 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 HandlePropertySet
divide 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 1
el nombre del atributo en el comentario ctl.
comienza con , significa que es un atributo de control. Si se satisface el permiso del cliente, HandleControlMessage
se 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 3
para PropertySet
modificar 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;
}
PropertySet
La función modifica principalmente atributos comunes. En primer lugar, es necesario juzgar si el atributo es legal. Si es legal, 1
busque 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.rc
el 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.rc
es un archivo de configuración muy importante, es un script escrito por Android
el lenguaje de inicialización ( ), este lenguaje contiene principalmente tres tipos de sentencias: , , y .Android Init Language
5
Action(行为)
Command(命令)
Service(服务)
Option(选项)
Import(引入)
init.rc
El archivo se divide aproximadamente en dos partes, una parte es la "lista de acciones" ( ) on
que comienza con la palabra clave y la otra parte es la "lista de servicios" ( ) que comienza con la palabra clave .action list
service
service 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 init
algunos 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 init
y on boot
es Action
una declaración de tipo, su formato es el siguiente:
on <triggger> [&& <trigger>]* // 设置触发器
<command>
<command> // 动作触发之后要执行的命令
Para saber cómo crear Zygoet
, mira principalmente Service
la declaración de tipo , su formato es el siguiente:
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
Service
Tipo 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.0
archivo . Los scripts de inicio se definen en, por ejemplo :init.rc
.rc
Zygote
init.zygoteXX.rc
init.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 Service
el formato de la declaración de tipo. Service
Se utiliza para notificar init
al proceso que cree Zygote
un proceso llamado , la ruta de ejecución de este proceso es /system/bin/app_process64
, y el siguiente código es app_process64
el parámetro que se le pasará. class main
Se refiere Zygote
a classname
para main
.
declaración Service
de tipo de análisis
init.rc
Tanto Action
la declaración de tipo como Service
la declaración de tipo tienen clases correspondientes para analizar, Action
la declaración de tipo usa ActionParser
para analizar y Service
la declaración de tipo usa ServiceParser
para analizar. Debido a que el análisis principal está aquí Zygote
, solo se presenta ServiceParser
.
ServiceParser
El código de implementación está system/core/init/service.cpp
en . A continuación, verifique ServiceParser
cómo analizar el tipo de declaración mencionado anteriormente Service
, se usarán dos funciones: una es que ParseSection
analizará el archivo Service
del .rc
, por ejemplo, como se mencionó anteriormente init.zygote64.rc
, ParseSection
la función se usa principalmente para construir Service
el 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();
}
EndSection
La función devuelve la llamada ServiceManager.AddService
a la función, la siguiente es AddService
la función:
// system/core/init/service.cpp
void ServiceList::AddService(std::unique_ptr<Service> service) {
services_.emplace_back(std::move(service)); // 1
}
1
El código en el comentario Service
agrega el objeto a Service
la lista vinculada. Service
En términos generales, el proceso de análisis consiste en crear Service
el objeto de acuerdo con los parámetros, luego completar Service
el objeto de acuerdo con el contenido del campo de opción y finalmente Service
agregar el objeto a la lista vinculada vector
de tipo Service
.
Fase cuatro: iniciar Zygote
el proceso
Después de comprender Service
el proceso de análisis, el siguiente paso es init
cómo iniciar el proceso Service
Aquí explicamos principalmente cómo iniciar Zygote
el proceso Service
. En Zygote
el script de inicio de , puede conocer el comportamiento Zygote
de . Tiene el siguiente código de configuración:classname
main
init.rc
// /system/core/rootdir/init.rc
on nonencrypted
class_start main // 1
class_start late_start
Entre ellos, class_start
es uno COMMAND
, y la función correspondiente es do_class_start
. Los comentarios 1
que comienzan con los classname
de main
son Service
, por lo tanto , se utilizan para iniciar la función definida de la siguiente manera Zygote
:classname
main
class_start main
Zygote
do_class_start
builtins.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á Service
la lista enlazada, encontrará classname
el que main
es Zygote
y ejecutará StartIfNotDisabled
la 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 Service
no se encuentra la opción .rc
configurada en el archivo correspondiente disable
, se llamará a la función Start()
para iniciarla Service
, y la opción no está configurada en Zygote
el correspondiente , entonces verifique la función:init.zygote64.rc
disabled
Start()
// /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 Service
si 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 pid
el valor, 2
si en el comentario pid == 0
, indica la lógica del código actual ejecutado en subprocesos. 3
Llame a la función en el comentario execv
, Service
el proceso secundario se iniciará e ingresará la función Service
del main
.
Si es Service
, Zygote
la Zygote
ruta de ejecución es system/bin/app_process64
, y el archivo correspondiente es app_main.cpp
, entonces entrará en la función app_main.cpp
de main
, es decir, en la función Zygote
de 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.");
}
}
1
El código en el comentario es llamar runtime.start
a la función para iniciar Zygote
, hasta ahora, Zygote
ha comenzado.
referencia
init.rc Análisis detallado
de SystemServer Service y ServiceManager Service
[Arranque del sistema Android 10] Series – init (proceso Tianzi No. 1)