Índice
2.1 Como é iniciado o processo de inicialização?
2.2 O que o processo Init fez depois de iniciado?
3. Análise do código-fonte do processo init de inicialização do kernel
4. Análise do código-fonte da inicialização do processo de inicialização
4.1 Entrada do processo de inicialização
4.3 O processo init inicia o primeiro estágio
4.3.1 PrimeiroEstágioPrincipal
4.4 Carregar regras do SELinux
4.5 O processo init inicia a segunda fase
7.6 processo de análise init.rc
1. Visão Geral:
O processo init é o primeiro processo no espaço do usuário no sistema Linux e o número do processo é 1.
- ROM de inicialização: quando o telefone estiver desligado, pressione e segure o botão Liga / Desliga para ligá-lo, o chip de inicialização começa a ser executado a partir do código predefinido solidificado na ROM e, em seguida, carrega o programa de inicialização na RAM;
- Boot Loader: Este é o programa de inicialização antes de iniciar o sistema Android, principalmente para verificar a RAM, inicializar parâmetros de hardware e outras funções.
Quando o bootloader iniciar, inicie o kernel. Após o kernel iniciar, inicie o processo init no espaço do usuário e, em seguida, leia a configuração relevante em init.rc através do processo init, para iniciar outros processos relacionados e outras operações.
O processo init recebe muito trabalho importante. A inicialização do processo init é dividida principalmente em dois estágios:
A primeira fase conclui o seguinte:
Salto ueventd/watchdogd e configuração de variável de ambiente
Montar sistema de arquivos e criar diretório
Inicializar saída de log, montar dispositivo de partição
Ativar política de segurança SELinux
Iniciar os preparativos antes do segundo estágio
A segunda fase completa o seguinte:
Inicializar o sistema de atributos
Executar o segundo estágio do SELinux e restaurar alguns contextos de segurança de arquivo
Criar um novo epoll e inicializar a função de processamento do sinal de término do processo filho
Definir outros atributos do sistema e habilitar serviços de atributos
2. Arquitetura
2.1 Como é iniciado o processo de inicialização?
O processo Init é o primeiro processo de espaço do usuário iniciado após o início do Kernel, com PID 1.
Depois que o kernel_init iniciar, conclua algumas operações de inicialização init e, em seguida, vá para o diretório raiz do sistema para encontrar os aplicativos definidos por ramdisk_execute_command e execute_command por sua vez. Se esses dois diretórios não puderem ser encontrados, vá para o diretório raiz para encontrar /sbin/init, / etc/init, /bin/init,/bin/sh Esses quatro aplicativos são iniciados, desde que um desses aplicativos seja iniciado, os outros não serão iniciados.
O sistema Android geralmente coloca um arquivo executável init no diretório raiz, ou seja, o processo init do sistema Linux executa diretamente o arquivo init após a conclusão da inicialização do kernel.
2.2 O que o processo Init fez depois de iniciado?
Após o início do processo de inicialização, primeiro monte o sistema de arquivos, depois monte a partição correspondente, inicie a política de segurança SELinux, inicie o serviço de atributo, analise o arquivo rc e inicie o processo de serviço de atributo correspondente, inicialize o epoll e defina o sinal, propriedade e keychord em sequência. A função de retorno de chamada correspondente quando um fd é legível. Entra no loop sem fio, usado para responder às mudanças e reconstruções de cada processo.
3. Análise do código-fonte do processo init de inicialização do kernel
3.1 kernel_init
kernel/msm-4.19/init/main.c
kernel/msm-4.19/init/main.c
kernel_init()
|
run_init_process(ramdisk_execute_command) //executa o arquivo executável, inicia o processo init
static int __ref kernel_init(void *unused)
{ kernel_init_freeable(); //faz algum processo init Operação de inicialização /* precisa terminar todo o código __init assíncrono antes de liberar a memória */ async_synchronize_full();// Aguarde até que todas as chamadas assíncronas sejam concluídas, antes de liberar memória, todos os códigos __init assíncronos devem ser concluídos free_initmem();// Libere tudo memória no segmento init.* mark_rodata_ro(); // arm64 implementação vazia system_state = SYSTEM_RUNNING;// define o estado do sistema para o estado em execução numa_default_policy(); // define a política de acesso à memória padrão do sistema NUMA flush_delayed_fput(); // Libera todas as estruturas de arquivo struct atrasadas if (ramdisk_execute_command) { //O valor de ramdisk_execute_command é "/init"
if (!run_init_process(ramdisk_execute_command)) //executa o programa init no diretório raiz
return 0;
pr_err("Falha ao executar %s\n", ramdisk_execute_command);
}
/*
* Tentamos cada um deles até que um seja bem-sucedido.
*
* O shell Bourne pode ser usado em vez do init se estivermos
* tentando recuperar uma máquina realmente quebrada.
*/
if (execute_command) { //Se o valor de execute_command for definido, vá para o diretório raiz para encontrar o aplicativo correspondente, e então inicie
if (!run_init_process(execute_command))
return 0;
pr_err("Falha ao executar %s. Tentando padrões...\n",
execute_command);
}
if (!run_init_process("/sbin/init") || //如果ramdisk_execute_command和execute_command定义的应用程序都没有找到,
//就到根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动
!run_init_process("/etc/init") ||
!run_init_process("/bin/init") ||
!run_init_process("/bin/sh"))
return 0;
panic("No init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
}
3.2 do_basic_setup
kernel_init_freeable()
|
do_basic_setup()
static void __init do_basic_setup(void)
{ cpuset_init_smp();//Para sistemas SMP, inicialize o subsistema cpuset do grupo de controle do kernel. usermodehelper_init();// Cria uma fila de trabalho de thread único do khelper para auxiliar na criação e execução de programas de espaço do usuário shmem_init();//Inicializa memória compartilhada driver_init();//Inicializa drivers de dispositivo init_irq_proc();//Cria /proc / diretório irq e inicialize os subdiretórios correspondentes a todas as interrupções no sistema do_ctors();// execute o construtor do kernel usermodehelper_enable();// habilite o usermodehelper do_initcalls();// atravesse o array initcall_levels, chame a função initcall dentro, aqui é principalmente para inicializar o dispositivo, driver e sistema de arquivos, //todas as funções são encapsuladas em uma matriz para passagem, principalmente com a finalidade de estender random_int_secret_init();//inicializar o pool de geração de números aleatórios }
4. Análise do código-fonte da inicialização do processo de inicialização
Analisamos principalmente o código de inicialização do Android Q(10.0).
Arquivos de código-fonte envolvidos:
plataforma/sistema/núcleo/init/main.cpp
plataforma/sistema/núcleo/init/init.cpp
plataforma/sistema/núcleo/init/ueventd.cpp
plataforma/sistema/núcleo/init/selinux.cpp
plataforma/sistema/núcleo/ init/subcontext.cpp
platform/system/core/base/logging.cpp
platform/system/core/init/first_stage_init.cpp
platform/system/core/init/first_stage_main.cpp
platform/system/core/init/first_stage_mount.cpp
platform /system/core/init/keyutils.h
platform/system/core/init/property_service.cpp
platform/external/selinux/libselinux/src/label.c
platform/system/core/init/signal_handler.cpp
platform/system/core /init/service.cpp
4.1 Entrada do processo de inicialização
前面已经通过kernel_init,启动了init进程,init进程属于一个守护进程,准确的说,它是Linux系统中用户控制的第一个进程,它的进程号为1。它的生命周期贯穿整个Linux内核运行的始终。Android中所有其它的进程共同的鼻祖均为init进程。
可以通过"adb shell ps |grep init" 的命令来查看init的进程号。
Android Q(10.0) 的init入口函数由原先的init.cpp 调整到了main.cpp,把各个阶段的操作分离开来,使代码更加简洁命令,接下来我们就从main函数开始学习。
[system/core/init/main.cpp]
/*
* 1.第一个参数argc表示参数个数,第二个参数是参数列表,也就是具体的参数
* 2.main函数有四个参数入口,
*一是参数中有ueventd,进入ueventd_main
*二是参数中有subcontext,进入InitLogging 和SubcontextMain
*三是参数中有selinux_setup,进入SetupSelinux
*四是参数中有second_stage,进入SecondStageMain
*3. A sequência de execução de main é a seguinte:
* (1) O processo init ueventd_main cria o processo filho ueventd,
* e confia o trabalho de criação do arquivo de nó de dispositivo para ueventd, e ueventd cria o arquivo de nó de dispositivo de duas maneiras
* (2) FirstStageMain inicia a primeira Fase 1
* (3)SetupSelinux carrega as regras do selinux, define logs do selinux e conclui o trabalho relacionado ao SELinux
* (4)SecondStageMain inicia a segunda fase
*/
int main(int argc, char** argv) { //When argv[0] Quando o conteúdo é ueventd, o valor de strcmp é 0,! strcmp é 1 //1 significa verdadeiro, e ueventd_main é executado, ueventd é o principal responsável pela criação de nós de dispositivo, configuração de permissão e outros trabalhos if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); } //Quando o número de parâmetros passados for maior que 1, execute as seguintes operações if (argc > 1) { //O parâmetro é subcontexto, inicialize o sistema de log, if (!strcmp( argv [1], "subcontexto")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
//参数为“selinux_setup”,启动Selinux安全策略
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
//参数为“second_stage”,启动init进程第二阶段
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// 默认启动init进程第一阶段
return FirstStageMain(argc, argv);
}
4.2 ueventd_main
代码路径:platform/system/core/init/ueventd.cpp
Android根文件系统的镜像中不存在“/dev”目录,该目录是init进程启动后动态创建的。
因此,建立Android中设备节点文件的重任,也落在了init进程身上。为此,init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd。
ueventd通过两种方式创建设备节点文件。
第一种方式对应“冷插拔”(Cold Plug),即以预先定义的设备信息为基础,当ueventd启动后,统一创建设备节点文件。这一类设备节点文件也被称为静态节点文件。
第二种方式对应“热插拔”(Hot Plug),即在系统运行中,当有设备插入USB端口时,ueventd就会接收到这一事件,为插入的设备动态创建设备节点文件。这一类设备节点文件也被称为动态节点文件。
int ueventd_main(int argc, char** argv) {
//设置新建文件的默认值,这个与chmod相反,这里相当于新建文件后的权限为666
umask(000);
//初始化内核日志,位于节点/dev/kmsg, 此时logd、logcat进程还没有起来,
//采用kernel的log系统,打开的设备节点/dev/kmsg, 那么可通过cat /dev/kmsg来获取内核log。
android::base::InitLogging(argv, &android::base::KernelLogger);
//注册selinux相关的用于打印log的回调函数
SelinuxSetupKernelLogging();
SelabelInitialize();
//解析xml,根据不同SOC厂商获取不同的hardware rc文件
auto ueventd_configuration = ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc",
"/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});
//冷启动
if (access(COLDBOOT_DONE, F_OK) != 0) {
ColdBoot cold_boot(uevent_listener, uevent_handlers);
cold_boot.Run();
}
for (auto& uevent_handler : uevent_handlers) { uevent_handler->ColdbootDone(); } //Ignora o sinal de finalização do processo filho (SIGCHLD, SIG_IGN); // Coleta e filhos pendentes que saíram entre a última chamada para waitpid() e a configuração SIG_IGN // para . SIGCHLD acima } //Ouve o uevent do driver e executa o processamento "hot swap" uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) { for (auto& uevent_handler : uevent_handlers) {
uevent_handler->HandleUevent(uevent); //Hot start, cria dispositivo
}
return ListenerAction::kContinue;
});
return 0;
}
4.3 O processo init inicia o primeiro estágio
Caminho do código: platform\system\core\init\first_stage_init.cpp
O trabalho principal do primeiro estágio do processo init é montar partições, criar nós de dispositivo e alguns diretórios de chave, inicializar o sistema de saída de log e ativar as políticas de segurança do SELinux
A primeira fase conclui o seguinte:
/* 01. Crie um diretório do sistema de arquivos e monte o sistema de arquivos relacionado */
/* 02. Proteger a entrada e saída padrão/inicializar o sistema de log do kernel*/
4.3.1 PrimeiroEstágioPrincipal
NULO)); #define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
// 非特权应用不能使用Andrlid cmdline
CHECKCALL(chmod("/proc/cmdline", 0440));
gid_t groups[] = {AID_READPROC};
CHECKCALL(setgroups(arraysize(groups), groups));
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
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)));
//这对于日志包装器是必需的,它在ueventd运行之前被调用
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
//在第一阶段挂在tmpfs、mnt/vendor、mount/product分区。其他的分区不需要在第一阶段加载,
//只需要在第二阶段通过rc文件解析来加载。
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
//创建可供读写的vendor目录
CHECKCALL(mkdir("/mnt/vendor", 0755));
// /mnt/product is used to mount product-specific partitions that can not be
// part of the product partition, e.g. because they are mounted read-write.
CHECKCALL(mkdir("/mnt/product", 0755));
// 挂载APEX,这在Android 10.0中特殊引入,用来解决碎片化问题,类似一种组件方式,对Treble的增强,
// 不写谷歌特殊更新不需要完整升级整个系统版本,只需要像升级APK一样,进行APEX组件升级
CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
// /debug_ramdisk is used to preserve additional files from the debug ramdisk
CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
#undef CHECKCALL
//把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
SetStdioToDevNull(argv);
//在/dev目录下挂载好 tmpfs 以及 kmsg
//这样就可以初始化 /kernel Log 系统,供用户打印log
old_root_info.st_dev); }
SetInitAvbVersionInRecovery();
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
//Selinux_steup
// para iniciar o processo de inicialização: /system/bin/init selinux_setup
const char* path = "/system/bin/init";
const char* args[] = {caminho, "selinux_setup", nullptr};
execv(caminho, const_cast<char**>(args));
PLOG(FATAL) << "execv(\"" << caminho << "\") falhou";
retornar 1;
}
4.4 Carregar regras do SELinux
SELinux是「Security-Enhanced Linux」的简称,是美国国家安全局「NSA=The National Security Agency」
和SCC(Secure Computing Corporation)开发的 Linux的一个扩张强制访问控制安全模块。
在这种访问控制体系的限制下,进程只能访问那些在他的任务中所需要文件。
selinux有两种工作模式:
permissive,所有的操作都被允许(即没有MAC),但是如果违反权限的话,会记录日志,一般eng模式用
enforcing,所有操作都会进行权限检查。一般user和user-debug模式用
不管是security_setenforce还是security_getenforce都是去操作/sys/fs/selinux/enforce 文件, 0表示permissive 1表示enforcing
4.4.1 SetupSelinux
说明:初始化selinux,加载SELinux规则,配置SELinux相关log输出,并启动第二阶段
代码路径: platform\system\core\init\selinux.cpp
/* Esta função inicializa o selinux, então executa o init para rodar no init selinux */
int SetupSelinux(char** argv) { //Inicializa o log do Kernel InitKernelLogging(argv); //Reinicia o bootloader quando a versão de depuração init falha if ( REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } //Registra um retorno de chamada para definir o log do selinux que precisa ser gravado em kmsg SelinuxSetupKernelLogging(); //Carrega as regras do SELinux SelinuxInitialize(); /* *Estamos no domínio do kernel e queremos mudar para o domínio domínio de inicialização. Sistemas de arquivos que armazenam selabels em seus xattrs (como ext4) não requerem um restorecon explícito, * mas outros sistemas de arquivos sim. Especialmente para ramdisks, como imagens de recuperação para dispositivos a/b, esta é uma etapa necessária. *Na verdade, ele está atualmente no domínio do kernel. Depois de carregar o Seliux, você precisa executar novamente o init para alternar para o modo de usuário do espaço C */
if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
//准备启动innit进程,传入参数second_stage
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
/*
*执行 /system/bin/init second_stage, 进入第二阶段
*/
PLOG(FATAL) << "execv(\"" << path << "\") failed";
return 1;
}
4.4.2 SelinuxInitialize()
/*加载selinux 规则*/
void SelinuxInitialize() {
LOG(INFO) << "Loading SELinux policy";
if (!LoadPolicy()) {
LOG(FATAL) << "Unable to load SELinux policy";
}
//获取当前Kernel的工作模式
bool kernel_enforcing = (security_getenforce() == 1);
//获取工作模式的配置
bool is_enforcing = IsEnforcing();
//如果当前的工作模式与配置的不同,就将当前的工作模式改掉
if (kernel_enforcing != is_enforcing) {
if (security_setenforce(is_enforcing)) {
PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")
<< ") failed";
}
}
if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) { LOG(FATAL) << "Não foi possível gravar em /sys/fs/selinux/checkreqprot: " << result.error(); } } /* * Carregar regras do SELinux * Existem dois casos distintos aqui, esses dois casos apenas distinguem onde carregar o arquivo de política de segurança, * o primeiro é lido em /vendor/etc/selinux/precompiled_sepolicy Veja, *O segundo é lido de /sepolicy, todos eles acabam chamando o método selinux_android_load_policy_from_fd */ bool LoadPolicy() { return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy(); }
4.5 O processo init inicia a segunda fase
O conteúdo principal da segunda etapa:
Crie uma chave de sessão de processo e inicialize o sistema de atributos
Execute o segundo estágio do SELinux e restaure alguns contextos de segurança de arquivo
Crie um novo epoll e inicialize a função de processamento de sinal de término de processo filho, consulte a Seção 5-Processamento de sinal
Inicie o servidor com atributos correspondentes para obter detalhes , consulte a Seção 6 para obter detalhes Seção de serviço de propriedade
Analisar init.rc e outros arquivos, criar ação e serviço do arquivo rc, iniciar outros processos, consulte a Seção 7-rc análise de arquivo para obter detalhes
4.5.1 SegundoEstágioPrincipal
int SecondStageMain(int argc, char** argv) {
/* 01. 创建进程会话密钥并初始化属性系统 */
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
//创建 /dev/.booting 文件,就是个标记,表示booting进行中
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
// 初始化属性系统,并从指定文件读取属性
property_init();
/* 02. 进行SELinux第二阶段并恢复一些文件安全上下文 */
SelinuxRestoreContext();
/* 03. 新建epoll并初始化子进程终止信号处理函数 */
Epoll epoll;
if (auto result = epoll.Open(); !result) {
PLOG(FATAL) << result.error();
}
InstallSignalFdHandler(&epoll);
/* 04. 设置其他系统属性并开启系统属性服务*/
StartPropertyService(&epoll);
/* 05 解析init.rc等文件,建立rc文件的action 、service,启动其他进程*/
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
}
代码流程详细解析:
int SecondStageMain(int argc, char** argv) {
/*
*init crash时重启引导加载程序
*这个函数主要作用将各种信号量,如SIGABRT,SIGBUS等的行为设置为SA_RESTART,一旦监听到这些信号即执行重启系统
*/
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
//把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
SetStdioToDevNull(argv);
//在/dev目录下挂载好 tmpfs 以及 kmsg
//这样就可以初始化 /kernel Log 系统,供用户打印log
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
// 01. 创建进程会话密钥并初始化属性系统
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
//创建 /dev/.booting 文件,就是个标记,表示booting进行中
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); //
Inicializa o sistema de propriedades e lê a propriedade
property_init() do arquivo especificado;
/*
* 1. Se os parâmetros forem leia da linha de comando ao mesmo tempo Passe com DT, a prioridade do DT é sempre maior que a da linha de comando
* 2. DT é device-tree, que significa árvore de dispositivos em chinês, que registra sua própria configuração de hardware e parâmetros operacionais do sistema,
*/
process_kernel_dt(); // Processa propriedades DT
process_kernel_cmdline(); // Processa propriedades da linha de comando
// Processa algumas outras propriedades
export_kernel_boot_props();
// Torna o tempo que init iniciado disponível para bootstat para log.property_set
("ro.boottime.init", getenv( "INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
// Definir versão libavb para correspondência OTA somente do Framework em agudos construir.
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Veja se é necessário carregar props de depuração para permitir adb root, quando o dispositivo estiver desbloqueado.
const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) { load_debug_prop = "true"s == force_debuggable_env; } // Para criar cmdline, selecione o método memcg bool memcg_enabled = android::base::GetBoolProperty("ro.boot.memcg",false); if (memcg_enabled) { // controle de memória raiz cgroup mkdir("/dev/memcg", 0700); chown("/dev/memcg",AID_ROOT,AID_SYSTEM);
mount("none", "/dev/memcg", "cgroup", 0, "memory");
// app mem cgroups, used by activity manager, lmkd and zygote
mkdir("/dev/memcg/apps/",0755);
chown("/dev/memcg/apps/",AID_SYSTEM,AID_SYSTEM);
mkdir("/dev/memcg/system",0550);
chown("/dev/memcg/system",AID_SYSTEM,AID_SYSTEM);
}
// 清空这些环境变量,之前已经存到了系统属性中去了
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
unsetenv("INIT_FORCE_DEBUGGABLE");
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
/*
* 02. Execute a segunda etapa do SELinux e restaure alguns contextos de segurança de
arquivos ; /* * 03. Crie um novo epoll e inicialize a função de processamento do sinal de término do processo filho * Crie uma instância epoll e retorne o descritor de arquivo de epoll */ Epoll epoll; if (resultado automático = epoll.Open(); !result) { PLOG(FATAL) << result.error(); } /* *Criar principalmente um manipulador para manipular o sinal de finalização do processo filho, registrar um sinal para epoll para monitoramento * para processamento de sub-herança */ InstallSignalFdHandler(&epoll) ; // Trabalho relacionado à configuração de propriedade padrão property_load_boot_defaults( load_debug_prop); UmountDebugRamdisk();
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
/*
*04. Definir outras propriedades do sistema e habilitar serviços de propriedade do sistema
*/
StartPropertyService(&epoll);
MountHandler mount_handler(&epoll);
//Configurar udc Contorller para armazenamento USB, sys/class/udc
set_usb_controller ();
// corresponde a correspondência entre comandos e funções
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
if (!SetupMountNamespaces()) { PLOG(FATAL) << "SetupMountNamespaces failed"; } // arquivo de inicialização Context subcontexts = InitializeSubcontexts(); /* *05 Analisa init.rc e outros arquivos, cria ação e serviço de arquivos rc e inicia outros processos */
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
// Ativar isso e permitir que o registro INFO seja descartado adiciona 0,2s ao
// tempo de inicialização do Nexus 9, portanto, está desativado por padrão.
if (false) DumpState();
// Quando o script GSI estiver em execução, verifique se o estado GSI está disponível.
if (android::gsi::IsGsiRunning()) { property_set("ro.gsid .image_running", "1"); } else { property_set("ro.gsid.image_running", "0"); } am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups"); // Executa a instrução cujo gatilho está ativado antecipadamente init no arquivo rc am.QueueEventTrigger("early-init");
// 等冷插拔设备初始化完成
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// 开始查询来自 /dev的 action
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
// 设备组合键的初始化操作
Keychords keychords;
am.QueueBuiltinAction(
[&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
for (const auto& svc : ServiceList::GetInstance()) {
keychords.Register(svc->keycodes());
}
keychords.Start(&epoll, HandleKeychord);
return Success();
},
"KeychordInit");
//Mostra o LOGO estático do Android na tela
am.QueueBuiltinAction(console_init_action, "console_init");
// Executa a trigger no rc file as The statement on init
am.QueueEventTrigger("init");
// Iniciando o autoteste BoringSSL, para conformidade com a certificação NIAP.
am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
// Repetir mix_hwrng_into_linux_rng in case /dev/hw_rand om ou /dev /random
// não estava pronto imediatamente após wait_for_coldboot_done
am. QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// Initialize binder before bringing up other system services
am.QueueBuiltinAction(InitBinder, "InitBinder");
// 当设备处于充电模式时,不需要mount文件系统或者启动系统服务
// 充电模式下,将charger假如执行队列,否则把late-init假如执行队列
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// 基于属性当前状态 运行所有的属性触发器.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
while (true) {
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}
//依次执行每个action中携带command对应的执行函数
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
auto next_process_action_time = HandleProcessActions();
// Se houver um processo que precisa ser reiniciado, acorde a tempo para isso.
if (next_process_action_time) { epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>( *next_process_action_time - boot_clock::now()); if (*epoll_timeout < 0ms) epoll_timeout = 0ms; } } // Se houver mais trabalho a fazer, acorde novamente imediatamente. if (am.HasMoreCommands()) epoll_timeout = 0ms; } // Resultado automático if (auto result = epoll.Wait(epoll_timeout); !result) { LOG(ERROR) << result.error(); }
}
retorna 0;
}
5. Processamento de sinal
init é um processo daemon. Para evitar que o processo filho do init se torne um processo zumbi (processo zumbi), o init precisa obter o código final do processo filho quando o processo filho terminar e remover o processo filho do programa tabela através do código final para evitar que ele se torne um processo zumbi. O processo filho do processo zumbi ocupa o espaço da tabela do programa (quando o espaço da tabela do programa atinge o limite superior, o sistema não pode mais iniciar novos processos, o que causará sérios problemas no sistema).
O processo de reinicialização do processo filho é mostrado na figura a seguir:
O principal trabalho de processamento de sinal:
Inicializar identificador de sinal de sinal
Processo filho de processamento de loop
Registrar identificador epoll Tratar
finalização de processo filho
Nota: EPOLL é semelhante a POLL, que é usado para disparar eventos no Linux, semelhante à função EventBus. Há muito tempo o Linux usa select para disparar eventos. Ele é processado por polling. Quanto mais fds polled, mais demorado será. Para processar um grande número de descritores, o EPOLL tem mais vantagens
5.1 InstallSignalFdHandler
No Linux, o processo pai sabe o final do processo filho capturando o sinal SIGCHLD. O sinal SIGCHLD será enviado quando o processo filho terminar. Depois de entender esses fundamentos, vamos dar uma olhada em como o processo init lida com esse sinal.
首先,新建一个sigaction结构体,sa_handler是信号处理函数,指向内核指定的函数指针SIG_DFL和Android 9.0及之前的版本不同,这里不再通过socket的读写句柄进行接收信号,改成了内核的信号处理函数SIG_DFL。
然后,sigaction(SIGCHLD, &act, nullptr) 这个是建立信号绑定关系,也就是说当监听到SIGCHLD信号时,由act这个sigaction结构体处理
最后,RegisterHandler 的作用就是signal_read_fd(之前的s[1])收到信号,触发handle_signal
终上所述,InstallSignalFdHandler函数的作用就是,接收到SIGCHLD信号时触发HandleSignalFd进行信号处理
信号处理示意图:
代码路径:platform/system/core/init.cpp
说明:该函数主要的作用是初始化子进程终止信号处理过程
static void InstallSignalFdHandler(Epoll* epoll) { // SA_NOCLDSTOP faz com que o processo init receba o sinal SIGCHLD somente quando seu processo filho termina const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP }; sigaction(SIGCHLD, &act, nullptr ); sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); if (!IsRebootCapable()) { // Se o init não tiver a capacidade de CAP_SYS_BOOT, ele está sendo executado no contêiner // neste cenário } if (sigprocmask(SIG_BLOCK, & mask, nullptr ) == -1) { PLOG(FATAL) << "falha ao bloquear sinais"; } // registrador Handler para desbloquear sinais em processos filhos
const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
if (result != 0) { LOG(FATAL) << "Falha ao registrar um fork handler: " << strerror(result); } //cria manipulador de sinal signal_fd = signalfd(-1, &mask, SFD_CLOEXEC); if (signal_fd == -1) { PLOG(FATAL) << "failed to create signalfd"; } //Registro do sinal, quando signal_fd recebe um sinal, aciona HandleSignalFd if ( auto resultado = epoll->RegisterHandler(signal_fd, HandleSignalFd); !resultado) { LOG(FATAL) << resultado.error(); } }
5.2 RegisterHandler
Caminho do código: /platform/system/core/epoll.cpp
Descrição: registro de sinal, adicione o identificador fd à fila de escuta de epoll_fd_
Result<void> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) { if (!events) { return Error() << "Deve especificar eventos"; } auto [ele, inserido] = epoll_handlers_.emplace(fd, std::move(handler)); if (!inserted) { return Error() << "Não é possível especificar dois manipuladores epoll para um determinado FD"; } epoll_event ev; ev.eventos = eventos; // os iteradores de std::map não são invalidados até serem apagados, então usamos o // ponteiro para a função std::no mapa diretamente para epoll_ctl. ev.data.ptr = reinterpret_cast<void*>(&it->second); // 将的可读事件加入到epoll_fd_的监听队列中
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) { Resultado<void> resultado = ErrnoError() << "epoll_ctl falhou ao adicionar fd"; epoll_handlers_.erase(fd); resultado de retorno; } retornar {}; }
5.3 HandleSignalFd
Caminho do código: platform/system/core/init.cpp
Descrição: monitore o sinal SIGCHLD e chame ReapAnyOutstandingChildren para encerrar o processo filho problemático
static void HandleSignalFd() { signalfd_siginfo siginfo; ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo))); if (bytes_read != sizeof(siginfo)) { PLOG(ERROR) << "Falha ao ler siginfo de signal_fd"; retornar; } //Segurança do SIGCHLD switch (siginfo.ssi_signo) { case SIGCHLD: ReapAnyOutstandingChildren(); quebrar; case SIGTERM: HandleSigtermSignal(siginfo); quebrar; padrão: PLOG(ERROR) << "signal_fd: sinal inesperado recebido " << siginfo.ssi_signo;
break;
}
}
5.4 ReapOneProcess
代码路径:/platform/system/core/sigchld_handle.cpp
说明:ReapOneProcess是最终的处理函数了,这个函数先用waitpid找出挂掉进程的pid,然后根据pid找到对应Service,
最后调用Service的Reap方法清除资源,根据进程对应的类型,决定是否重启机器或重启进程
void ReapAnyOutstandingChildren() { while (ReapOneProcess()) { } } static bool ReapOneProcess() { siginfo_t siginfo = {}; //Use a função waitpid para obter o pid do processo filho cujo status mudou //O sinalizador waitpid é WNOHANG, ou seja, não bloqueante Se retornar um valor positivo, significa que um processo desligou if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) { PLOG(ERROR) < < "waitid failed"; return false; } auto pid = siginfo.si_pid; if (pid == 0) return false; // Quando sabemos que existe um pid zumbi, usamos scopeguard para limpar o pid auto reaper = make_scope_guard( [pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); }); std::string name;
std::string wait_string;
Service* service = nullptr;
if (SubcontextChildReap(pid)) {
name = "Subcontext";
} else {
//通过pid找到对应的service
service = ServiceList::GetInstance().FindService(pid, &Service::pid);
if (service) {
name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
if (service->flags() & SVC_EXEC) {
auto exec_duration = boot_clock::now() - service->time_started();
auto exec_duration_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
} else if (service->flags() & SVC_ONESHOT) {
auto exec_duration = boot_clock::now() - service->time_started();
auto exec_duration_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
.count();
wait_string = StringPrintf(" oneshot service took %f seconds in background",exec_duration_ms / 1000.0f);
}
} else {
name = StringPrintf("Untracked pid %d", pid);
}
}
if (siginfo.si_code == CLD_EXITED) {
LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
} else {
LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
}
//没有找到service,说明已经结束了,退出
if (!service) return true;
service->Reap(siginfo);//清除子进程相关的资源
if (service->flags() & SVC_TEMPORARY) {
ServiceList::GetInstance().RemoveService(*service); //移除该service
}
return true;
}
6.属性服务
我们在开发和调试过程中看到通过property_set可以轻松设置系统属性,那干嘛这里还要启动一个属性服务呢?这里其实涉及到一些权限的问题,不是所有进程都可以随意修改任何的系统属性,
Android将属性的设置统一交由init进程管理,其他进程不能直接修改属性,而只能通知init进程来修改,而在这过程中,init进程可以进行权限控制,我们来看看具体的流程是什么
6.1 property_init
Caminho do código: platform/system/core/property_service.cpp
Descrição: Inicializa o sistema de atributos, lê o atributo do arquivo especificado, registra-se no SELinux e executa o controle de permissão de atributo
Limpe o cache, aqui é principalmente para limpar várias listas vinculadas e mapeamentos na memória, crie um novo diretório property_filename, o valor desse diretório é /dev/_properties_
Em seguida, chame CreateSerializedPropertyInfo para carregar as informações de categoria de algumas propriedades do sistema e, finalmente, grave a lista vinculada carregada em um arquivo e mapeie-a para a memória
void property_init() { // Selinux_callback cb; cb.func_audit = PropertyAuditCallback; selinux_set_callback(SELINUX_CB_AUDIT, cb); mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); CreateSerializedPropertyInfo(); if (__system_property_area_init()) { LOG(FATAL) << "Falha ao inicializar área de propriedade"; } if (!property_info_area.LoadDefaultPath()) { LOG(FATAL) << "Falha ao carregar arquivo serializado de informações de propriedade"; } }
Use CreateSerializedPropertyInfo para carregar os contextos dos seguintes diretórios:
1) Relacionado ao SELinux
/system/etc/selinux/plat_property_contexts
/vendor/etc/selinux/vendor_property_contexts /
vendor/etc/selinux/nonplat_property_contexts / product
/etc/selinux/product_property_contexts
/odm/etc/selinux/odm_property_contexts
/plat_property_contexts
/vendor_property_contexts
/nonplat_property_contexts
/product_property_contexts
/odm_property_contexts
6.2 StartPropertyService
Caminho do código: platform/system/core/init.cpp
Descrição: Iniciar o serviço de propriedade
Primeiro, crie um soquete e retorne o descritor de arquivo e, em seguida, defina a simultaneidade máxima como 8. Outros processos podem notificar o processo init para modificar as propriedades do sistema por meio desse soquete.
Por fim, registre o evento epoll, ou seja, chame handle_property_set_fd quando a alteração de property_set_fd for monitorada
void StartPropertyService(Epoll* epoll) { property_set("ro.property_service.version", "2"); // Estabelece conexão de soquete if (resultado automático = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, false, 0666, 0, 0 , {})) { property_set_fd = *result; } else { PLOG(FATAL) << "start_property_service socket creation failed: " << result.error(); } // Máximo de 8 escutas simultâneas listen(property_set_fd, 8); / / Registre property_set_fd, quando o identificador for alterado, manuseie-o por meio de handle_property_set_fd if (resultado automático = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); ! resultado) { PLOG(FATAL) << resultado.error();
}
}
6.3 handle_property_set_fd
代码路径:platform/system/core/property_service.cpp
说明:建立socket连接,然后从socket中读取操作信息,根据不同的操作类型,调用HandlePropertySet做具体的操作
HandlePropertySet是最终的处理函数,以"ctl"开头的key就做一些Service的Start,Stop,Restart操作,其他的就是调用property_set进行属性设置,不管是前者还是后者,都要进行SELinux安全性检查,只有该进程有操作权限才能执行相应操作
static void handle_property_set_fd() { static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */ // Aguardando conexão do cliente int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC); if (s == -1) { return; } ucred cr; socklen_t cr_size = sizeof(cr); // Obtém as credenciais do processo conectado a este socket if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); PLOG(ERROR) < < "sys_prop: incapaz de obter SO_PEERCRED"; return; } // Estabelece conexão de soquete SocketConnection socket(s, cr); uint32_t timeout_ms = kDefaultSocketTimeout;
uint32_t cmd = 0;
// 读取socket中的操作信息
if (!socket.RecvUint32(&cmd, &timeout_ms)) {
PLOG(ERROR) << "sys_prop: error while reading command from the socket";
socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}
// 根据操作信息,执行对应处理,两者区别一个是以char形式读取,一个以String形式读取
switch (cmd) {
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];
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): erro ao ler nome/valor do soquete";
retornar;
}
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
std::string source_context;
if (!socket.GetSourceContext(&source_context)) { PLOG(ERROR) << "Não foi possível definir a propriedade '" << prop_name << "': getpeercon() falhou"; retornar; } const auto& cr = socket.cred(); std::erro de string; uint32_t resultado = HandlePropertySet(prop_name, prop_value, source_context, cr, &error); se (resultado!
LOG(ERRO) << "Não foi possível definir a propriedade '" << prop_name << "' de uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr. pid << ": " << erro;
}
quebrar;
}
case PROP_MSG_SETPROP2: { std::string nome; std::valor da string; if (!socket.RecvString(&name, &timeout_ms) || !socket.RecvString(&value, &timeout_ms)) { PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): erro ao ler nome/valor do socket"; soquete. SendUint32(PROP_ERROR_READ_DATA); retornar; } std::string source_context;
if (!socket.GetSourceContext(&source_context)) {
PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
return;
}
const auto& cr = socket.cred();
std::string error;
uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
socket.SendUint32(result);
break;
}
default:
LOG(ERROR) << "sys_prop: invalid command " << cmd;
socket.SendUint32(PROP_ERROR_INVALID_CMD);
break;
}
}
7.第三阶段init.rc
当属性服务建立完成后,init的自身功能基本就告一段落,接下来需要来启动其他的进程。但是init进程如何其他其他进程呢?其他进程都是一个二进制文件,我们可以直接通过exec的命令方式来启动,例如 ./system/bin/init second_stage,来启动init进程的第二阶段。但是Android系统有那么多的Native进程,如果都通过传exec在代码中一个个的来执行进程,那无疑是一个灾难性的设计。
在这个基础上Android推出了一个init.rc的机制,即类似通过读取配置文件的方式,来启动不同的进程。接下来我们就来看看init.rc是如何工作的。
init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本。
init.rc在手机的目录:./init.rc
init.rc主要包含五种类型语句:
Action
Command
Service
Option
Import
7.1 Action
动作表示了一组命令(commands)组成.动作包括一个触发器,决定了何时运行这个动作
Action: 通过触发器trigger,即以on开头的语句来决定执行相应的service的时机,具体有如下时机:
na inicialização inicial; Acionado no estágio inicial da inicialização;
na inicialização; Acionado no estágio de inicialização;
na inicialização tardia; Acionado no estágio final da inicialização;
na inicialização/carregador: Acionado quando o sistema inicia/carrega;
na propriedade :<chave>= <valor>: Disparado quando o valor do atributo atende a condição;
7.2 Comando
command é o comando na lista de comandos da ação, ou o comando de parâmetro da opção onrestart no serviço, e os comandos serão executados um a um quando ocorrer o evento correspondente.
Comandos comumente usados estão listados abaixo
class_start <service_class_name>: Inicia todos os serviços pertencentes à mesma classe;
class_stop <service_class_name>: Interrompe o serviço da classe especificada
start <service_name>: Inicia o serviço especificado, se já estiver iniciado, pule-o;
pare <service_name>: Interrompa a execução de um serviço
setprop <nome> <valor>: define o valor da propriedade
mkdir <caminho>: cria o diretório especificado
symlink <target> <sym_link>: cria um link simbólico <sym_link> conectado a <target>;
escreve <path> <string >: escreva para Escreva uma string no caminho do arquivo;
exec: fork e execute, ele bloqueará o processo init até que o programa seja concluído;
exprot <nome> <nome>: defina a variável de ambiente;
loglevel <nível>: defina o log level
hostname <name>: define o nome do host
import <filename> : importa um arquivo de configuração init adicional
7.3 Serviço
Serviço O serviço, começando com o serviço, é iniciado pelo processo init e geralmente é executado em um subprocesso do init, portanto, é necessário determinar se o arquivo executável correspondente existe antes de iniciar o serviço.
命令:service <name><pathname> [ <argument> ]* <option> <option>
parâmetro
significado
<nome>
Indica o nome deste serviço
<nome do caminho>
Como o caminho onde esse serviço está localizado é um arquivo executável, deve haver um caminho de armazenamento.
<argumento>
Parâmetros para iniciar o serviço
<opção>
Opções de restrição para este serviço
O processo filho gerado pelo init é definido no arquivo rc, e cada serviço irá gerar um processo filho por bifurcação quando for iniciado.
Por exemplo: service servicemanager /system/bin/servicemanager significa que o nome do serviço é servicemanager e o caminho de execução do serviço é /system/bin/servicemanager.
7.4 Opções
Opções é opcional para Serviço, usado em conjunto com serviço
desabilitado: Não inicia automaticamente com a classe, apenas inicia de acordo com o nome do serviço;
oneshot: Não reinicia após o término do serviço;
usuário/grupo: define o usuário/grupo de usuários que executa o serviço, o padrão é root;
classe: define o nome da classe a que pertence, quando Quando a classe inicia/sai, o serviço também inicia/pára, o padrão é default; onrestart:
executa o comando correspondente quando o serviço reinicia;
socket: cria um socket chamado /dev/socket/ <nome>
crítico: o serviço dentro do tempo especificado Se você continuar reiniciando, o sistema será reiniciado e entrará no modo de recuperação
padrão: significa desativado=falso, oneshot=falso, crítico=falso.
7.5 importar
Usado para importar outros arquivos rc
Comando: importar <nome do arquivo>
7.6 processo de análise init.rc
7.6.1 LoadBootScripts
Caminho do código: platform\system\core\init\init.cpp
Descrição: Se não houver nenhuma configuração especial ro.boot.init_rc, analise ./init.rc
把/system/etc/init,/product/etc/init,/product_services/etc/init,/odm/etc/init,
/vendor/etc/init Esses caminhos são adicionados aos caminhos analisados após init.rc, após a conclusão da análise init.rc, analise os arquivos rc nesses diretórios
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); } }
Depois do Android7.0, o init.rc foi dividido, cada serviço tem seu próprio arquivo rc, eles são basicamente carregados em /system/etc/init, /vendor/etc/init, /odm/etc/init Aguarde o diretório, depois a análise do init.rc estiver concluída, ele analisará os arquivos rc nesses diretórios para executar as ações relacionadas.
Caminho do código: platform\system\core\init\init.cpp
Descrição: Criar objetos de análise do analisador, como serviço, on, objetos de importação
Analisador CreateParser(ActionManager& action_manager, ServiceList& service_list) { Analisador do analisador; parser.AddSectionParser( "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt)); parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts)); parser.AddSectionParser("importar", std::make_unique<ImportParser>(&parser)); analisador de retorno; }
7.6.2 Executando Ações
Adicione Actions relacionadas à fila de trigger em ordem, a ordem é early-init -> init -> late-init. Em seguida, no loop, execute todas as funções de execução com Command na Action na fila de trigger.
am.QueueEventTrigger("early-init");
am.QueueEventTrigger("init");
am.QueueEventTrigger("late-init");
...
while (true) {
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
}
7.6.3 Zygote启动
从Android 5.0的版本开始,Android支持64位的编译,因此zygote本身也支持32位和64位。通过属性ro.zygote来控制不同版本的zygote进程启动。
在init.rc的import段我们看到如下代码:
import /init.${ro.zygote}.rc // 可以看出init.rc不再直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件
init.rc位于/system/core/rootdir下。在这个路径下还包括四个关于zygote的rc文件。
分别是init.zygote32.rc,init.zygote32_64.rc,init.zygote64.rc,init.zygote64_32.rc,由硬件决定调用哪个文件。
这里拿64位处理器为例,init.zygote64.rc的代码如下所示:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main # class é uma opção, especificando o tipo de serviço zygote como
prioridade principal -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system # socket palavra-chave indica uma opção, crie um socket chamado dev/socket/zygote, o tipo é stream, a permissão é 660 socket
usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake # onrestart é uma opção, indicando o comando a ser executado quando o zigoto reiniciar.
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
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server 解析:
service zygote :init.zygote64.rc 中定义了一个zygote服务。 init进程就是通过这个service名称来创建zygote进程
/system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server解析:
zygote这个服务,通过执行进行/system/bin/app_process64 并传入4个参数进行运行:
参数1:-Xzygote 该参数将作为虚拟机启动时所需的参数
参数2:/system/bin 代表虚拟机程序所在目录
参数3:--zygote 指明以ZygoteInit.java类中的main函数作为虚拟机执行入口
参数4:--start-system-server 告诉Zygote进程启动systemServer进程
8.总结
init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略。
init进程第二阶段主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux做准备,那么第二阶段就是真正去把这些功能落实。
init进行第三阶段主要是解析init.rc 来启动其他进程,进入无限循环,进行子进程实时监控。