Démarrage du système Android (1) - processus de démarrage du processus d'initialisation

AndroidLe démarrage de l'appareil doit passer par trois phases : Boot Loader, Linux Kernelet Androidservices système. Strictement parlant, Androidle système est en fait une série de "processus de service" s'exécutant sur Linuxle noyau, et "l'ancêtre" de ces processus de service est initle processus.

Processus de démarrage du système Android

Boot LoaderIl s'agit d'un petit programme avant l'exécution du noyau du système d'exploitation. Grâce à ce petit programme, le périphérique matériel peut être initialisé et la carte de mappage de l'espace mémoire peut être établie, de manière à amener l'environnement logiciel et matériel du système à un état approprié, afin de préparer l'environnement correct pour le dernier appel du noyau du système d'exploitation.

initLe processus est Androidle premier processus dans l'espace utilisateur du système, PID(numéro de processus) est 1, c'est Androidune étape clé dans le processus de démarrage du système, en tant que premier processus, il se voit confier de nombreuses responsabilités de travail extrêmement importantes, il est init.rcconstruit en analysant les fichiers L'état de fonctionnement initial du système, c'est-à-dire que Androidla plupart des services système init.rcsont décrits dans le fichier de script et démarrés selon certaines conditions.

processus d'initialisation

initLe démarrage du processus a fait beaucoup de travail, en général, il fait principalement les trois choses suivantes :

  • Créez ( mkdir) et montez ( mount) les répertoires de fichiers requis pour le démarrage ;
  • Initialiser et démarrer la propriété service( property service);
  • Analysez init.rcle fichier de configuration et démarrez Zygotele processus ;

initUn processus est composé de plusieurs fichiers source, situés dans le répertoire source system/core/init.

Une fois le noyau chargé, il recherchera d'abord le fichier ( ) Linuxdans les fichiers système et lancera le processus. init.rc/system/core/rootdir/init.rcinitLa fonction d'entrée du processus a été ajustée. Ce n'est plus la fonction de la précédente , mais a été remplacée par la fonction de . Android 10Le but de ceci est de séparer le travail de chaque étape et de rendre la logique du code plus concise. * *Le code est le suivant :init/system/core/init/init.cppmainsystem/core/init/main.cppmain

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

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

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

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

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

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

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

La première étape : créer et monter le répertoire de fichiers requis pour le démarrage

1Le code du commentaire est initla première étape du processus, qui est implémenté first_state_init.cppdans le fichier ( /system/core/init/first_stage_init.cpp), et le code ressemble à ceci :

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

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

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

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

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

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

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

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

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

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

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

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

Comme on peut le voir dans le code ci-dessus, initla première étape du processus est principalement utilisée pour créer et monter les répertoires de fichiers nécessaires au démarrage , dans lesquels tmpfs, devpts, et sont des systèmes de fichiers montés, qui sont tous procdes répertoires d'exécution du système, c'est-à-dire disons , n'existera que lorsque le système est en cours d'exécution et disparaîtra lorsque le système sera arrêté.sysfsselinuxfs5

Quatre types de systèmes de fichiers :

  • tmpfs: Un système de fichiers de mémoire virtuelle, qui stocke tous les fichiers dans la mémoire virtuelle, si tmpfsle système de fichiers est démonté, tout le contenu qu'il contient n'existera plus. tmpfsLes deux peuvent être utilisés RAM, ou une partition de swap peut être utilisée, et la taille sera modifiée en fonction des besoins réels. tmpfsLa vitesse de est incroyable, après tout, il réside dans RAM, même avec la partition de swap, les performances sont toujours très bonnes. Étant donné que tmpfsréside dans RAM, son contenu n'est pas persistant. Après la mise hors tension, tmpfsle contenu du contenu disparaît, ce qui est également tmpfsla cause première de l'appel ;
  • devpts: Fournit une interface standard pour le pseudo-terminal, et son point de montage standard est /dev/pts. Tant que ptyle périphérique composé principal de est ouvert, un nouveau fichier de périphérique /dev/ptmxsera créé dynamiquement /dev/ptssous ;pty
  • proc: Un système de fichiers virtuel très important, qui peut être considéré comme l'interface de la structure de données interne du noyau, à travers laquelle nous pouvons obtenir des informations système, et peut également modifier des paramètres spécifiques du noyau lors de l'exécution ;
  • sysfs: procSemblable au système de fichiers, il s'agit également d'un système de fichiers virtuel qui n'occupe aucun espace disque. Il est généralement monté /syssous le répertoire. sysfsLe système de fichiers est Linux 2.6introduit par le noyau, qui organise les périphériques et les bus connectés au système dans un fichier hiérarchique afin qu'ils soient accessibles dans l'espace utilisateur ;

Phase 2 : Initialiser et démarrer le service de propriété

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

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

1Appelez la fonction au niveau du commentaire property_init()pour initialiser la propriété et 3appelez StartPropertyServicela fonction au niveau du commentaire pour démarrer le service de propriété.

2L'appel de la fonction au niveau du commentaire InstallSignalFdHandlerest utilisé pour définir la fonction de traitement du signal du sous-processus, principalement pour empêcher initle sous-processus du processus de devenir un processus zombie. Afin d'empêcher l'émergence de processus zombies, le système enverra un signal lorsque le processus enfant est suspendu ou terminé SIGCHLD, et InstallSignalFdHandlerla fonction est utilisée pour recevoir SIGCHLDle signal (elle ne traite que SIGCHLDle signal de fin de processus en interne).

processus zombie

Processus zombie et dommage : dans UNIX/Linux, le processus parent utilise forkla méthode pour créer un processus enfant. Une fois le processus enfant terminé, si le processus parent ne peut pas être notifié, bien que le processus enfant soit déjà terminé, un certain temps lui est réservé dans la table des processus système. Informations (telles que le numéro de processus, l'état de sortie, le temps d'exécution, etc.), ce processus est appelé un processus zombie. La table de processus système est une ressource limitée. Si la table de processus système est épuisée par des processus zombies, le système peut ne pas être en mesure de créer de nouveaux processus.

service d'attribut

WindowIl y a un gestionnaire de registre sur la plate-forme, et le contenu du registre prend la forme de paires clé-valeur pour enregistrer certaines informations d'utilisation des utilisateurs et des logiciels. Même si le système ou le logiciel est redémarré, il peut toujours effectuer le travail d'initialisation correspondant en fonction des enregistrements précédents du registre. AndroidUn mécanisme similaire est également fourni, appelé services de propriété.

initLorsque le processus démarre, le service d'attributs sera lancé et de la mémoire lui sera allouée pour stocker ces attributs. Si vous avez besoin de lire ces attributs directement, le code lié au service d'attributs dans la fonction aura les deux lignes suivantes init.cpp:main

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

Ces deux lignes de code sont utilisées pour initialiser la configuration du service de propriété et démarrer le service de propriété.

1 Initialisation et démarrage du service immobilier

property_initL'implémentation spécifique de la fonction est la suivante :

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

1La fonction en commentaire __system_property_area_init()permet d'initialiser la zone mémoire des attributs. Regardez ensuite StartPropertyServicele code de la fonction :

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

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

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

    listen(property_set_fd, 8); // 2

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

1Créer non bloquant au commentaire Socket. 2Appelez listenla fonction au niveau du commentaire property_set_fdà écouter , afin que le créé Socketdevienne server, c'est-à-dire le service d'attribut ; listenle deuxième paramètre de la fonction est défini sur 8, ce qui signifie que le service d'attribut peut 8fournir des services à la plupart des utilisateurs qui tentent de définir attributs en même temps. 3Le code au niveau du commentaire property_set_fdplace dans epollto epollmonitor property_set_fd: property_set_fdlorsque les données arrivent dans , initle processus demandera RegisterHandlerun traitement.

Dans Linuxle nouveau noyau, il est epollutilisé pour remplacer le noyau select, qui a été amélioré pour gérer un grand nombre de descripteurs de fichiers . Il s'agit d'une version améliorée de l' interface de multiplexage , qui peut améliorer considérablement les performances des programmes avec seulement un petit nombre de descripteurs. connexions actives dans un grand nombre de connexions simultanées. L'utilisation du système sous la condition de . Le type de données interne utilisé pour gagner du temps est un arbre rouge-noir, qui a une vitesse de recherche rapide. Le tableau utilisé pour stocker les informations a une vitesse de recherche très lente. Ce n'est qu'en attendant un petit nombre de descripteurs de fichiers que l'efficacité et sera similaire.epollLinuxpollLinuxI/Oselect/pollCPUepollselectepollselect

2 Le service traite les demandes des clients

D'après ce qui précède, nous pouvons savoir que lorsque le service d'attribut reçoit la requête du client, il appellera handle_property_set_fdla fonction pour la traiter :

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

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

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

            break;
        }
        ...
    }

La fonction est en outre encapsulée 1dans le commentaire , comme indiqué ci-dessous :handle_property_set_fd

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

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

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

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

Il existe deux types d'attributs système, l'un est les attributs communs et l'autre les attributs de contrôle, qui sont utilisés pour exécuter certaines commandes, telles que l'animation de démarrage. Par conséquent, il HandlePropertySetest divisé en deux points de traitement, une partie traite des attributs de contrôle et l'autre partie est utilisée pour traiter les attributs communs.Seuls les attributs communs sont analysés ici. Si 1le nom de l'attribut dans le commentaire ctl.commence par , cela signifie qu'il s'agit d'un attribut de contrôle. Si l'autorisation du client est satisfaite, HandleControlMessagela fonction sera appelée pour modifier l'attribut de contrôle. S'il s'agit d'un attribut ordinaire, sous la condition de l'autorité cliente Mazu, le commentaire au niveau du commentaire sera appelé 3pour PropertySetmodifier l'attribut ordinaire, comme indiqué ci-dessous :

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

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

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

PropertySetLa fonction modifie principalement les attributs communs. Tout d'abord, il est nécessaire de juger si l'attribut est légal. S'il est légal, 1recherchez l'attribut dans l'espace de stockage des attributs au niveau de l'annotation. Si l'attribut existe, mettez à jour la valeur de l'attribut ; sinon, ajoutez l'attribut. De plus, ro. persist.les propriétés dont le nom commence par sont traitées en conséquence.

La troisième étape : analyser init.rcle fichier de configuration

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

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

init.rcest un fichier de configuration très important, c'est un script écrit par Androidle langage d'initialisation ( ), ce langage contient principalement trois types d'instructions : , , , et .Android Init Language5Action(行为)Command(命令)Service(服务)Option(选项)Import(引入)

Configuration de init.rc

init.rcLe fichier est grossièrement divisé en deux parties, une partie est la "liste d'actions" ( ) oncommençant par le mot-clé , et l'autre partie est la "liste de services" ( ) commençant par le mot-clé .action listserviceservice list

La liste d'actions est utilisée pour créer le répertoire requis et spécifier des autorisations pour certains fichiers spécifiques, et la liste de services est utilisée pour enregistrer initcertains sous-processus que le processus doit démarrer. Comme suit:

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

on initet on bootest Actionune instruction de type, son format est le suivant :

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

Pour savoir comment créer Zygoet, regardez principalement Servicela déclaration de type , son format est le suivant :

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

ServiceFormat de l'instruction de type :

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

A noter que le fichier est découpé en , et chaque service correspond à un Android 8.0fichier . Les scripts de démarrage sont définis dans , prenez par exemple :init.rc.rcZygoteinit.zygoteXX.rcinit.zygote64.rc

// /system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

Analysez la signification du code ci-dessus en fonction Servicedu format de l'instruction de type. ServiceIl est utilisé pour notifier initau processus de créer Zygoteun processus nommé , le chemin d'exécution de ce processus est /system/bin/app_process64, et le code suivant est app_process64le paramètre à passer à . class mainFait référence Zygoteau classnamepour main.

Serviceinstruction de type d'analyse

init.rcL' Actioninstruction de type et Servicel'instruction de type dans ont des classes correspondantes pour l'analyse, Actionl'instruction de type utilise ActionParserpour l'analyse et Servicel'instruction de type utilise ServiceParserpour l'analyse. Parce que l'analyse principale est ici Zygote, elle n'est qu'introduite ServiceParser.

ServiceParserLe code d'implémentation est system/core/init/service.cppen . Ensuite, vérifiez ServiceParsercomment analyser le Servicetype d'instruction mentionné ci-dessus, deux fonctions seront utilisées : la première consiste à ParseSectionanalyser le fichier Servicedu .rc, par exemple, comme mentionné ci-dessus init.zygote64.rc, ParseSectionla fonction est principalement utilisée pour construire Servicel'étagère du ; l'autre est ParseLineSection, résout les sous-clés pour toujours. Le code ressemble à ceci :

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

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

    filename_ = filename;

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

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

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

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

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

Au commentaire 1, un objet est construit selon les paramètres Service. La fonction est appelée après analyse des données EndSection:

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

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

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

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

    return Success();
}

EndSectionLa fonction rappelle ServiceManager.AddServicela fonction, voici AddServicela fonction :

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

1Le code du commentaire Serviceajoute l'objet à Servicela liste liée. ServiceDe manière générale, le processus d'analyse consiste à créer Servicel'objet en fonction des paramètres, puis à remplir Servicel'objet en fonction du contenu du champ d'option, et enfin Serviceà ajouter l'objet à la liste liée vectorde type Service.

Phase 4 : lancement Zygotedu processus

Après avoir compris Servicele processus d'analyse, l'étape suivante consiste inità démarrer le processus Service. Ici, nous expliquons principalement comment démarrer Zygotele processus Service. Dans Zygotele script de démarrage de , vous pouvez connaître le comportement Zygotede . In a le code de configuration suivant :classnamemaininit.rc

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

Parmi eux, class_startest un COMMAND, et la fonction correspondante est do_class_start. Les commentaires 1qui commencent ceux pour sont donc , classnamesont mainutilisés pour Servicelancer la fonction définie dans comme suit Zygote:classnamemainclass_start mainZygotedo_class_startbuiltins.cpp

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

Au commentaire 1, il parcourra Servicela liste chaînée, trouvera classnamecelle qui mainest Zygote, et exécutera StartIfNotDisabledla fonction, comme indiqué ci-dessous :

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

Au niveau du commentaire 1, si l'option définie dans Servicele fichier correspondant n'est pas trouvée , la fonction sera appelée pour le démarrer , et l'option n'est pas définie dans le fichier correspondant , alors vérifiez la fonction :.rcdisableStart()ServiceZygoteinit.zygote64.rcdisabledStart()

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

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

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

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

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

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

Jugez d'abord Services'il a été en cours d'exécution, s'il est en cours d'exécution, il ne redémarrera pas, si le programme passe au commentaire 1, cela signifie que le sous-processus n'a pas été démarré, puis appelez fork()la fonction pour créer un sous-processus, et renvoie pidla valeur, 2si dans le commentaire pid == 0, elle indique la logique du code en cours d'exécution dans les sous-threads. 3Appelez la fonction au commentaire execv, Servicele processus enfant sera démarré et entrez la fonction Servicedu main.

Si le Serviceest Zygote, Zygotele chemin d'exécution est system/bin/app_process64, et le fichier correspondant est app_main.cpp, il entrera donc dans la fonction app_main.cppde main, c'est-à-dire que dans la fonction Zygotede main, le code est le suivant :

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

1Le code au commentaire est d'appeler runtime.startla fonction pour démarrer Zygote, jusqu'à présent, Zygoteil a démarré.

référence

init.rc Analyse détaillée
du service SystemServer et du service ServiceManager
[Démarrage du système Android 10] Series - init (processus Tianzi n ° 1)

Je suppose que tu aimes

Origine blog.csdn.net/xingyu19911016/article/details/127451545
conseillé
Classement