Android
Le démarrage de l'appareil doit passer par trois phases : Boot Loader
, Linux Kernel
et Android
services système. Strictement parlant, Android
le système est en fait une série de "processus de service" s'exécutant sur Linux
le noyau, et "l'ancêtre" de ces processus de service est init
le processus.
Boot Loader
Il 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.
init
Le processus est Android
le premier processus dans l'espace utilisateur du système, PID
(numéro de processus) est 1
, c'est Android
une é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.rc
construit en analysant les fichiers L'état de fonctionnement initial du système, c'est-à-dire que Android
la plupart des services système init.rc
sont décrits dans le fichier de script et démarrés selon certaines conditions.
init
Le 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.rc
le fichier de configuration et démarrezZygote
le processus ;
init
Un 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 ( ) Linux
dans les fichiers système et lancera le processus. init.rc
/system/core/rootdir/init.rc
init
La 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 10
Le 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.cpp
main
system/core/init/main.cpp
main
// /system/core/init/main.cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv); // 1
}
La première étape : créer et monter le répertoire de fichiers requis pour le démarrage
1
Le code du commentaire est init
la première étape du processus, qui est implémenté first_state_init.cpp
dans 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, init
la 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 proc
des 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é.sysfs
selinuxfs
5
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, sitmpfs
le système de fichiers est démonté, tout le contenu qu'il contient n'existera plus.tmpfs
Les deux peuvent être utilisésRAM
, ou une partition de swap peut être utilisée, et la taille sera modifiée en fonction des besoins réels.tmpfs
La vitesse de est incroyable, après tout, il réside dansRAM
, même avec la partition de swap, les performances sont toujours très bonnes. Étant donné quetmpfs
réside dansRAM
, son contenu n'est pas persistant. Après la mise hors tension,tmpfs
le contenu du contenu disparaît, ce qui est égalementtmpfs
la 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 quepty
le périphérique composé principal de est ouvert, un nouveau fichier de périphérique/dev/ptmx
sera créé dynamiquement/dev/pts
sous ;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
:proc
Semblable 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é/sys
sous le répertoire.sysfs
Le système de fichiers estLinux 2.6
introduit 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);
...
}
1
Appelez la fonction au niveau du commentaire property_init()
pour initialiser la propriété et 3
appelez StartPropertyService
la fonction au niveau du commentaire pour démarrer le service de propriété.
2
L'appel de la fonction au niveau du commentaire InstallSignalFdHandler
est utilisé pour définir la fonction de traitement du signal du sous-processus, principalement pour empêcher init
le 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 InstallSignalFdHandler
la fonction est utilisée pour recevoir SIGCHLD
le signal (elle ne traite que SIGCHLD
le signal de fin de processus en interne).
processus zombie
Processus zombie et dommage : dans UNIX/Linux
, le processus parent utilise fork
la 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
Window
Il 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. Android
Un mécanisme similaire est également fourni, appelé services de propriété.
init
Lorsque 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_init
L'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";
}
}
1
La fonction en commentaire __system_property_area_init()
permet d'initialiser la zone mémoire des attributs. Regardez ensuite StartPropertyService
le 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();
}
}
1
Créer non bloquant au commentaire Socket
. 2
Appelez listen
la fonction au niveau du commentaire property_set_fd
à écouter , afin que le créé Socket
devienne server
, c'est-à-dire le service d'attribut ; listen
le deuxième paramètre de la fonction est défini sur 8
, ce qui signifie que le service d'attribut peut 8
fournir des services à la plupart des utilisateurs qui tentent de définir attributs en même temps. 3
Le code au niveau du commentaire property_set_fd
place dans epoll
to epoll
monitor property_set_fd
: property_set_fd
lorsque les données arrivent dans , init
le processus demandera RegisterHandler
un traitement.
Dans Linux
le nouveau noyau, il est epoll
utilisé 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.epoll
Linux
poll
Linux
I/O
select/poll
CPU
epoll
select
epoll
select
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_fd
la 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 1
dans 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 HandlePropertySet
est 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 1
le 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, HandleControlMessage
la 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é 3
pour PropertySet
modifier 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;
}
PropertySet
La 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, 1
recherchez 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.rc
le 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.rc
est un fichier de configuration très important, c'est un script écrit par Android
le langage d'initialisation ( ), ce langage contient principalement trois types d'instructions : , , , et .Android Init Language
5
Action(行为)
Command(命令)
Service(服务)
Option(选项)
Import(引入)
init.rc
Le fichier est grossièrement divisé en deux parties, une partie est la "liste d'actions" ( ) on
commençant par le mot-clé , et l'autre partie est la "liste de services" ( ) commençant par le mot-clé .action list
service
service 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 init
certains 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 init
et on boot
est Action
une instruction de type, son format est le suivant :
on <triggger> [&& <trigger>]* // 设置触发器
<command>
<command> // 动作触发之后要执行的命令
Pour savoir comment créer Zygoet
, regardez principalement Service
la déclaration de type , son format est le suivant :
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
Service
Format 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.0
fichier . Les scripts de démarrage sont définis dans , prenez par exemple :init.rc
.rc
Zygote
init.zygoteXX.rc
init.zygote64.rc
// /system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
Analysez la signification du code ci-dessus en fonction Service
du format de l'instruction de type. Service
Il est utilisé pour notifier init
au processus de créer Zygote
un processus nommé , le chemin d'exécution de ce processus est /system/bin/app_process64
, et le code suivant est app_process64
le paramètre à passer à . class main
Fait référence Zygote
au classname
pour main
.
Service
instruction de type d'analyse
init.rc
L' Action
instruction de type et Service
l'instruction de type dans ont des classes correspondantes pour l'analyse, Action
l'instruction de type utilise ActionParser
pour l'analyse et Service
l'instruction de type utilise ServiceParser
pour l'analyse. Parce que l'analyse principale est ici Zygote
, elle n'est qu'introduite ServiceParser
.
ServiceParser
Le code d'implémentation est system/core/init/service.cpp
en . Ensuite, vérifiez ServiceParser
comment analyser le Service
type d'instruction mentionné ci-dessus, deux fonctions seront utilisées : la première consiste à ParseSection
analyser le fichier Service
du .rc
, par exemple, comme mentionné ci-dessus init.zygote64.rc
, ParseSection
la fonction est principalement utilisée pour construire Service
l'é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();
}
EndSection
La fonction rappelle ServiceManager.AddService
la fonction, voici AddService
la fonction :
// system/core/init/service.cpp
void ServiceList::AddService(std::unique_ptr<Service> service) {
services_.emplace_back(std::move(service)); // 1
}
1
Le code du commentaire Service
ajoute l'objet à Service
la liste liée. Service
De manière générale, le processus d'analyse consiste à créer Service
l'objet en fonction des paramètres, puis à remplir Service
l'objet en fonction du contenu du champ d'option, et enfin Service
à ajouter l'objet à la liste liée vector
de type Service
.
Phase 4 : lancement Zygote
du processus
Après avoir compris Service
le processus d'analyse, l'étape suivante consiste init
à démarrer le processus Service
. Ici, nous expliquons principalement comment démarrer Zygote
le processus Service
. Dans Zygote
le script de démarrage de , vous pouvez connaître le comportement Zygote
de . In a le code de configuration suivant :classname
main
init.rc
// /system/core/rootdir/init.rc
on nonencrypted
class_start main // 1
class_start late_start
Parmi eux, class_start
est un COMMAND
, et la fonction correspondante est do_class_start
. Les commentaires 1
qui commencent ceux pour sont donc , classname
sont main
utilisés pour Service
lancer la fonction définie dans comme suit Zygote
:classname
main
class_start main
Zygote
do_class_start
builtins.cpp
// /system/core/init/builtins.cpp
static Result<Success> do_class_start(const BuiltinArguments& args) {
// Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
return Success();
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
for (const auto& service : ServiceList::GetInstance()) {
// 1
if (service->classnames().count(args[1])) {
if (auto result = service->StartIfNotDisabled(); !result) {
LOG(ERROR) << "Could not start service '" << service->name()
<< "' as part of class '" << args[1] << "': " << result.error();
}
}
}
return Success();
}
Au commentaire 1
, il parcourra Service
la liste chaînée, trouvera classname
celle qui main
est Zygote
, et exécutera StartIfNotDisabled
la 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 Service
le 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 :.rc
disable
Start()
Service
Zygote
init.zygote64.rc
disabled
Start()
// /system/core/init/service.cpp
Result<Success> Service::Start() {
...
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
// 如果Service已经运行,则不启动
if (flags_ & SVC_RUNNING) {
if ((flags_ & SVC_ONESHOT) && disabled) {
flags_ |= SVC_RESTART;
}
// It is not an error to try to start a service that is already running.
return Success();
}
...
// 判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
flags_ |= SVC_DISABLED;
return ErrnoError() << "Cannot find '" << args_[0] << "'";
}
...
// 如果子进程没有启动,则调用fork函数创建子进程
pid_t pid = -1; // 1
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork();
}
// 当前代码逻辑在子进程中运行
if (pid == 0) {
// 2
umask(077);
...
if (!ExpandArgsAndExecv(args_, sigstop_)) {
PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
}
_exit(127);
}
}
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
std::vector<std::string> expanded_args;
std::vector<char*> c_strings;
expanded_args.resize(args.size());
c_strings.push_back(const_cast<char*>(args[0].data()));
for (std::size_t i = 1; i < args.size(); ++i) {
if (!expand_props(args[i], &expanded_args[i])) {
LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
}
c_strings.push_back(expanded_args[i].data());
}
c_strings.push_back(nullptr);
if (sigstop) {
kill(getpid(), SIGSTOP);
}
// 调用execv函数,启动service子进程
return execv(c_strings[0], c_strings.data()) == 0; // 3
}
Jugez d'abord Service
s'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 pid
la valeur, 2
si dans le commentaire pid == 0
, elle indique la logique du code en cours d'exécution dans les sous-threads. 3
Appelez la fonction au commentaire execv
, Service
le processus enfant sera démarré et entrez la fonction Service
du main
.
Si le Service
est Zygote
, Zygote
le 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.cpp
de main
, c'est-à-dire que dans la fonction Zygote
de 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.");
}
}
1
Le code au commentaire est d'appeler runtime.start
la fonction pour démarrer Zygote
, jusqu'à présent, Zygote
il 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)