Análise relacionada à inicialização do processo de inicialização do Android

Índice

1. Visão Geral:

2. Arquitetura

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

3.1 kernel_init

3.2 do_basic_setup

4. Análise do código-fonte da inicialização do processo de inicialização

4.1 Entrada do processo de inicialização

4.2 ueventd_main

4.3 O processo init inicia o primeiro estágio

4.3.1 PrimeiroEstágioPrincipal

4.4 Carregar regras do SELinux

4.4.1 ConfiguraçãoSelinux

4.4.2 SelinuxInitialize()

4.5 O processo init inicia a segunda fase

4.5.1 SegundoEstágioPrincipal

5. Processamento de sinal

5.1 InstallSignalFdHandler

5.2 RegisterHandler

5.3 HandleSignalFd

5.4 ReapOneProcess

6. Atribuir serviço

6.1 property_init

6.2 StartPropertyService

6.3 handle_property_set_fd

7. O terceiro estágio init.rc

7.1 Ação

7.2 Comando

7.3 Serviço

7.4 Opções

7.5 importar

7.6 processo de análise init.rc

7.6.1 LoadBootScripts

7.6.2 Executando Ações

7.6.3 Inicialização do zigoto

8. Resumo


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 来启动其他进程,进入无限循环,进行子进程实时监控。

Acho que você gosta

Origin blog.csdn.net/s_nshine/article/details/131712990
Recomendado
Clasificación