Android系統啟動-Init篇


copy from : http://gityuan.com/2016/02/05/android-init/

 

基於Android 6.0的源碼剖析, 分析Android啟動過程進程號為1的init進程的工作內容

system/core/init/ - init.cpp - init_parser.cpp - signal_handler.cpp 

一、概述

init進程是Linux系統中用戶空間的第一個進程,進程號固定為1。Kernel啟動后,在用戶空間啟動init進程,並調用init中的main()方法執行init進程的職責。對於init進程的功能分為4部分:

  • 解析並運行所有的init.rc相關文件
  • 根據rc文件,生成相應的設備驅動節點
  • 處理子進程的終止(signal方式)
  • 提供屬性服務的功能

接下來從main()方法說起。

1.1 main

[-> init.cpp]

static int epoll_fd = -1; int main(int argc, char** argv) { ... //設置文件屬性0777 umask(0); //初始化內核log,位於節點/dev/kmsg【見小節1.2】 klog_init(); //設置輸出的log級別 klog_set_level(KLOG_NOTICE_LEVEL); //創建一塊共享的內存空間,用於屬性服務【見小節5.1】 property_init(); //初始化epoll功能 epoll_fd = epoll_create1(EPOLL_CLOEXEC); //初始化子進程退出的信號處理函數,並調用epoll_ctl設置signal fd可讀的回調函數【見小節2.1】 signal_handler_init(); //加載default.prop文件 property_load_boot_defaults(); //啟動屬性服務器,此處會調用epoll_ctl設置property fd可讀的回調函數【見小節5.2】 start_property_service(); //解析init.rc文件 init_parse_config_file("/init.rc"); //執行rc文件中觸發器為on early-init的語句 action_for_each_trigger("early-init", action_add_queue_tail); //等冷插拔設備初始化完成 queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); //設備組合鍵的初始化操作,此處會調用epoll_ctl設置keychord fd可讀的回調函數 queue_builtin_action(keychord_init_action, "keychord_init"); // 屏幕上顯示Android靜態Logo 【見小節1.3】 queue_builtin_action(console_init_action, "console_init"); //執行rc文件中觸發器為on init的語句 action_for_each_trigger("init", action_add_queue_tail); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); char bootmode[PROP_VALUE_MAX]; //當處於充電模式,則charger加入執行隊列;否則late-init加入隊列。 if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("late-init", action_add_queue_tail); } //觸發器為屬性是否設置 queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); while (true) { if (!waiting_for_exec) { execute_one_command(); //根據需要重啟服務【見小節1.4】 restart_processes(); } int timeout = -1; if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } if (!action_queue_empty() || cur_action) { timeout = 0; } epoll_event ev; //循環等待事件發生 int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout)); if (nr == -1) { ERROR("epoll_wait failed: %s\n", strerror(errno)); } else if (nr == 1) { ((void (*)()) ev.data.ptr)(); } } return 0; } 

init進程執行完成后進入循環等待epoll_wait的狀態。

1.2 log系統

此時android的log系統還沒有啟動,采用kernel的log系統,打開的設備節點/dev/kmsg, 那么可通過cat /dev/kmsg來獲取內核log。

接下來,設置log的輸出級別為KLOG_NOTICE_LEVEL(5),當log級別小於5時則會輸出到kernel log, 默認值為3.

#define KLOG_ERROR_LEVEL 3 #define KLOG_WARNING_LEVEL 4 #define KLOG_NOTICE_LEVEL 5 #define KLOG_INFO_LEVEL 6 #define KLOG_DEBUG_LEVEL 7 #define KLOG_DEFAULT_LEVEL 3 //默認為3 

1.3 console_init_action

[-> init.cpp]

static int console_init_action(int nargs, char **args) { char console[PROP_VALUE_MAX]; if (property_get("ro.boot.console", console) > 0) { snprintf(console_name, sizeof(console_name), "/dev/%s", console); } int fd = open(console_name, O_RDWR | O_CLOEXEC); if (fd >= 0) have_console = 1; close(fd); fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC); if (fd >= 0) { const char *msg; msg = "\n" "\n" "\n" "\n" "\n" "\n" "\n" // console is 40 cols x 30 lines "\n" "\n" "\n" "\n" "\n" "\n" "\n" " A N D R O I D "; write(fd, msg, strlen(msg)); close(fd); } return 0; } 

這便是開機顯示的底部帶ANDROID字樣的畫面。

1.4 restart_processes

[-> init.cpp]

static void restart_processes() { process_needs_restart = 0; service_for_each_flags(SVC_RESTARTING, restart_service_if_needed); } 

檢查service_list中的所有服務,對於帶有SVC_RESTARTING標志的服務,則都會調用其相應的restart_service_if_needed。

static void restart_service_if_needed(struct service *svc) { time_t next_start_time = svc->time_started + 5; if (next_start_time <= gettime()) { svc->flags &= (~SVC_RESTARTING); service_start(svc, NULL); return; } if ((next_start_time < process_needs_restart) || (process_needs_restart == 0)) { process_needs_restart = next_start_time; } } 

之后再調用service_start來啟動服務。

接下來,解讀init的main方法中的4大塊核心知識點:信號處理、rc文件語法、啟動服務以及屬性服務。

二、信號處理

在小節[1.1]的init.cpp的main()方法中通過signal_handler_init()來初始化信號處理過程。

主要工作:

  • 初始化signal句柄;
  • 循環處理子進程;
  • 注冊epoll句柄;
  • 處理子進程的終止;

2.1 signal_handler_init

[-> signal_handler.cpp]

void signal_handler_init() { int s[2]; // 創建socket pair if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) { exit(1); } signal_write_fd = s[0]; signal_read_fd = s[1]; //當捕獲信號SIGCHLD,則寫入signal_write_fd struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = SIGCHLD_handler; //SA_NOCLDSTOP使init進程只有在其子進程終止時才會受到SIGCHLD信號 act.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &act, 0); //進入waitpid來處理子進程是否退出的情況【見小節2.2】 reap_any_outstanding_children(); //調用epoll_ctl方法來注冊epoll的回調函數【見小節2.3】 register_epoll_handler(signal_read_fd, handle_signal); } 

每個進程在處理其他進程發送的signal信號時都需要先注冊,當進程的運行狀態改變或終止時會產生某種signal信號,init進程是所有用戶空間進程的父進程,當其子進程終止時產生SIGCHLD信號,init進程調用信號安裝函數sigaction(),傳遞參數給sigaction結構體,便完成信號處理的過程。

這里有兩個重要的函數:SIGCHLD_handler和handle_signal,如下:

//寫入數據 static void SIGCHLD_handler(int) { //向signal_write_fd寫入1,直到成功為止 if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) { ERROR("write(signal_write_fd) failed: %s\n", strerror(errno)); } } //讀取數據 static void handle_signal() { char buf[32]; //讀取signal_read_fd中的數據,並放入buf read(signal_read_fd, buf, sizeof(buf)); reap_any_outstanding_children(); 【見小節2.2】 } 

2.2 reap_any_outstanding_children

[-> signal_handler.cpp]

static void reap_any_outstanding_children() { while (wait_for_one_process()) { } } static bool wait_for_one_process() { int status; //等待任意子進程,如果子進程沒有退出則返回0,否則則返回該子進程pid。 pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG)); if (pid == 0) { return false; } else if (pid == -1) { return false; } //根據pid查找到相應的service service* svc = service_find_by_pid(pid); std::string name; if (!svc) { return true; } //當flags為RESTART,且不是ONESHOT時,先kill進程組內所有的子進程或子線程 if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) { kill(-pid, SIGKILL); } //移除當前服務svc中的所有創建過的socket for (socketinfo* si = svc->sockets; si; si = si->next) { char tmp[128]; snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); unlink(tmp); } //當flags為EXEC時,釋放相應的服務 if (svc->flags & SVC_EXEC) { waiting_for_exec = false; list_remove(&svc->slist); free(svc->name); free(svc); return true; } svc->pid = 0; svc->flags &= (~SVC_RUNNING); //對於ONESHOT服務,使其進入disabled狀態 if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) { svc->flags |= SVC_DISABLED; } //禁用和重置的服務,都不再自動重啟 if (svc->flags & (SVC_DISABLED | SVC_RESET)) { svc->NotifyStateChange("stopped"); //設置相應的service狀態為stopped return true; } //服務在4分鍾內重啟次數超過4次,則重啟手機進入recovery模式 time_t now = gettime(); if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) { if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); return true; } } else { svc->time_crashed = now; svc->nr_crashed = 1; } } svc->flags &= (~SVC_RESTART); svc->flags |= SVC_RESTARTING; //執行當前service中所有onrestart命令 struct listnode* node; list_for_each(node, &svc->onrestart.commands) { command* cmd = node_to_item(node, struct command, clist); cmd->func(cmd->nargs, cmd->args); } //設置相應的service狀態為restarting svc->NotifyStateChange("restarting"); return true; } 

另外:通過getprop | grep init.svc 可查看所有的service運行狀態。狀態總共分為:running, stopped, restarting

2.3 register_epoll_handler

[-> signal_handler.cpp]

void register_epoll_handler(int fd, void (*fn)()) { epoll_event ev; ev.events = EPOLLIN; ev.data.ptr = reinterpret_cast<void*>(fn); //將fd的可讀事件加入到epoll_fd的監聽隊列中 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { ERROR("epoll_ctl failed: %s\n", strerror(errno)); } } 

當fd可讀,則會觸發調用(*fn)函數。

三、rc文件語法

rc文件語法是以行尾單位,以空格間隔的語法,以#開始代表注釋行。rc文件主要包含Action、Service、Command、Options,其中對於Action和Service的名稱都是唯一的,對於重復的命名視為無效。

3.1 Action

Action: 通過觸發器trigger,即以on開頭的語句來決定執行相應的service的時機,具體有如下時機:

  • on early-init; 在初始化早期階段觸發;
  • on init; 在初始化階段觸發;
  • on late-init; 在初始化晚期階段觸發;
  • on boot/charger: 當系統啟動/充電時觸發,還包含其他情況,此處不一一列舉;
  • on property:<key>=<value>: 當屬性值滿足條件時觸發;

3.2 Service

服務Service,以 service開頭,由init進程啟動,一般運行在init的一個子進程,所以啟動service前需要判斷對應的可執行文件是否存在。init生成的子進程,定義在rc文件,其中每一個service在啟動時會通過fork方式生成子進程。

例如: service servicemanager /system/bin/servicemanager代表的是服務名為servicemanager,服務執行的路徑為/system/bin/servicemanager。

3.3 Command

下面列舉常用的命令

  • class_start <service_class_name>: 啟動屬於同一個class的所有服務;
  • start <service_name>: 啟動指定的服務,若已啟動則跳過;
  • stop <service_name>: 停止正在運行的服務
  • setprop <name> <value>:設置屬性值
  • mkdir <path>:創建指定目錄
  • symlink <target> <sym_link>: 創建連接到<target>的<sym_link>符號鏈接;
  • write <path> <string>: 向文件path中寫入字符串;
  • exec: fork並執行,會阻塞init進程直到程序完畢;
  • exprot <name> <name>:設定環境變量;
  • loglevel <level>:設置log級別

3.4 Options

Options是Service的可選項,與service配合使用

  • disabled: 不隨class自動啟動,只有根據service名才啟動;
  • oneshot: service退出后不再重啟;
  • user/group: 設置執行服務的用戶/用戶組,默認都是root;
  • class:設置所屬的類名,當所屬類啟動/退出時,服務也啟動/停止,默認為default;
  • onrestart:當服務重啟時執行相應命令;
  • socket: 創建名為/dev/socket/<name>的socket
  • critical: 在規定時間內該service不斷重啟,則系統會重啟並進入恢復模式

default: 意味着disabled=false,oneshot=false,critical=false。

四、啟動服務

4.1 啟動順序

on early-init
on init
on late-init
    trigger post-fs      
    trigger load_system_props_action
    trigger post-fs-data  
    trigger load_persist_props_action
    trigger firmware_mounts_complete
    trigger boot   

on post-fs      //掛載文件系統
    start logd mount rootfs rootfs / ro remount mount rootfs rootfs / shared rec mount none /mnt/runtime/default /storage slave bind rec ... on post-fs-data //掛載data start logd start vold //啟動vold ... on boot //啟動核心服務 ... class_start core //啟動core class 

觸發器的執行順序為on early-init -> init -> late-init,從上面的代碼可知,在late-init觸發器中會觸發文件系統掛載以及on boot。再on boot過程會觸發啟動core class。至於main class的啟動是由vold.decrypt的以下4個值的設置所決定的, 該過程位於system/vold/cryptfs.c文件。

on nonencrypted class_start main class_start late_start on property:vold.decrypt=trigger_restart_min_framework class_start main on property:vold.decrypt=trigger_restart_framework class_start main class_start late_start on property:vold.decrypt=trigger_reset_main class_reset main on property:vold.decrypt=trigger_shutdown_framework class_reset late_start class_reset main 

4.2 服務啟動(Zygote)

在init.zygote.rc文件中,zygote服務定義如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd 

通過init_parser.cpp完成整個service解析工作,此處就不詳細展開講解析過程,該過程主要工作是:

  • 創建一個名叫”zygote”的service結構體;
  • 創建一個用於socket通信的socketinfo結構體;
  • 創建一個包含4個onrestart的action結構體。

Zygote服務會隨着main class的啟動而啟動,退出后會由init重啟zygote,即使多次重啟也不會進入recovery模式。zygote所對應的可執行文件是/system/bin/app_process,通過調用pid =fork()創建子進程,通過execve(svc->args[0], (char**)svc->args, (char**) ENV),進入App_main.cpp的main()函數。故zygote是通過fork和execv共同創建的。

流程如下:

zygote_init

而關於Zygote重啟在前面的信號處理過程中講過,是處理SIGCHLD信號,init進程重啟zygote進程,更多關於Zygote內容見Zygote篇

4.3 服務重啟

init_oneshot

當init子進程退出時,會產生SIGCHLD信號,並發送給init進程,通過socket套接字傳遞數據,調用到wait_for_one_process()方法,根據是否是oneshot,來決定是重啟子進程,還是放棄啟動。

所有的Service里面只有servicemanager ,zygote ,surfaceflinger這3個服務有onrestart關鍵字來觸發其他service啟動過程。

//zygote可觸發media、netd重啟
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd //servicemanager可觸發healthd、zygote、media、surfaceflinger、drm重啟 service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart healthd onrestart restart zygote onrestart restart media onrestart restart surfaceflinger onrestart restart drm //surfaceflinger可觸發zygote重啟 service surfaceflinger /system/bin/surfaceflinger class core user system group graphics drmrpc onrestart restart zygote 

由上可知:

  • zygote:觸發media、netd以及子進程(包括system_server進程)重啟;
  • system_server: 觸發zygote重啟;
  • surfaceflinger:觸發zygote重啟;
  • servicemanager: 觸發zygote、healthd、media、surfaceflinger、drm重啟

所以,surfaceflinger,servicemanager,zygote自身以及system_server進程被殺都會觸發Zygote重啟。

五、屬性服務

當某個進程A,通過property_set()修改屬性值后,init進程會檢查訪問權限,當權限滿足要求后,則更改相應的屬性值,屬性值一旦改變則會觸發相應的觸發器(即rc文件中的on開頭的語句),在Android Shared Memmory(共享內存區域)中有一個_system_property_area_區域,里面記錄着所有的屬性值。對於進程A通過property_get()方法,獲取的也是該共享內存區域的屬性值。

5.1 property_init

[-> property_service.cpp]

void property_init() { //用於保證只初始化_system_property_area_區域一次 if (property_area_initialized) { return; } property_area_initialized = true; //創建共享內存 if (__system_property_area_init()) { return; } pa_workspace.size = 0; pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); } 

該方法核心功能在執行__system_property_area_init()方法,創建用於跨進程的共享內存。主要工作如下:

  • 執行open(),打開名為”/dev/properties“的共享內存文件,並設置大小為128KB;
  • 執行mmap(),將該內存映射到init進程;
  • 將該內存的首地址保存在全局變量__system_property_area__,后續的增加或者修改屬性都基於該變量來計算位置。

關於加載的prop文件

通過load_all_load_all_propsprops()方法,加載以下:

  1. /system/build.prop;
  2. /vendor/build.prop;
  3. /factory/factory.prop;
  4. /data/local.prop;
  5. /data/property路徑下的persist屬性

5.2 start_property_service

[-> property_service.cpp]

void start_property_service() { 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, sehandle); listen(property_set_fd, 8); //設置property文件描述符可讀的回調函數【見小節2.3】 register_epoll_handler(property_set_fd, handle_property_set_fd); } 

創建並監聽名叫“property_service”的socket,再利用epoll_ctl設置property文件描述符觸發可讀時的回調函數為handle_property_set_fd,接下來看看該函數的實現。

5.3 handle_property_set_fd

static void handle_property_set_fd() { static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */ int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC); struct ucred cr; socklen_t cr_size = sizeof(cr); getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0); SocketConnection socket(s, cr); uint32_t timeout_ms = kDefaultSocketTimeout; //設置2秒超時 uint32_t cmd = 0; if (!socket.RecvUint32(&cmd, &timeout_ms)) { socket.SendUint32(PROP_ERROR_READ_CMD); return; } 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)) { return; } prop_name[PROP_NAME_MAX-1] = 0; prop_value[PROP_VALUE_MAX-1] = 0; //設置property【見小節5.4】 handle_property_set(socket, prop_value, prop_value, true); break; } case PROP_MSG_SETPROP2: { std::string name; std::string value; if (!socket.RecvString(&name, &timeout_ms) || !socket.RecvString(&value, &timeout_ms)) { socket.SendUint32(PROP_ERROR_READ_DATA); return; } //設置property【見小節5.4】 handle_property_set(socket, name, value, false); break; } default: socket.SendUint32(PROP_ERROR_INVALID_CMD); break; } } 

這里針對socket接收事件設置2秒超時,也就是說property的設置過程有可能耗時。

5.4 handle_property_set

static void handle_property_set(SocketConnection& socket, const std::string& name, const std::string& value, bool legacy_protocol) { const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2"; if (!is_legal_property_name(name)) { //檢查屬性名是否合規 socket.SendUint32(PROP_ERROR_INVALID_NAME); return; } struct ucred cr = socket.cred(); char* source_ctx = nullptr; getpeercon(socket.socket(), &source_ctx); if (android::base::StartsWith(name, "ctl.")) { if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) { //處理以ctl.開頭的屬性 handle_control_message(name.c_str() + 4, value.c_str()); } ... } else { if (check_perms(name, source_ctx, &cr)) { //設置屬性名和屬性值 uint32_t result = property_set(name, value); } ... } freecon(source_ctx); } 

這里會檢測屬性名是否合規,具體檢查規范如下:

bool is_legal_property_name(const std::string& name) { size_t namelen = name.size(); if (namelen < 1) return false; if (name[0] == '.') return false; if (name[namelen - 1] == '.') return false; /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */ /* Don't allow ".." to appear in a property name */ for (size_t i = 0; i < namelen; i++) { if (name[i] == '.') { // i=0 is guaranteed to never have a dot. See above. if (name[i-1] == '.') return false; continue; } if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue; if (name[i] >= 'a' && name[i] <= 'z') continue; if (name[i] >= 'A' && name[i] <= 'Z') continue; if (name[i] >= '0' && name[i] <= '9') continue; return false; } return true; } 
uint32_t property_set(const std::string& name, const std::string& value) { return PropertySetImpl(name, value); } static uint32_t PropertySetImpl(const std::string& name, const std::string& value) { size_t valuelen = value.size(); if (!is_legal_property_name(name)) { return PROP_ERROR_INVALID_NAME; } if (valuelen >= PROP_VALUE_MAX) { //屬性名不可過長 return PROP_ERROR_INVALID_VALUE; } prop_info* pi = (prop_info*) __system_property_find(name.c_str()); if (pi != nullptr) { // 以ro.開頭的屬性不可更改 if (android::base::StartsWith(name, "ro.") && strcmp(name.c_str(),"ro.build.software.version")) { 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); } //以persist.開頭的屬性需要持久化 if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) { write_persistent_property(name.c_str(), value.c_str()); } //屬性值改變的通知過程 property_changed(name, value); return PROP_SUCCESS; } 

不同屬性執行邏輯有所不同,主要區分如下:

  • 屬性名以ctl.開頭,則表示是控制消息,控制消息用來執行一些命令。例如:
    • setprop ctl.start bootanim 查看開機動畫;
    • setprop ctl.stop bootanim 關閉開機動畫;
    • setprop ctl.start pre-recovey 進入recovery模式;
  • 屬性名以ro.開頭,則表示是只讀的,不能設置,所以直接返回;
  • 屬性名以persist.開頭,則需要把這些值寫到對應文件;需要注意的是,persist用於持久化保存某些屬性值,當同時也帶來了額外的IO操作。

六、總結

init進程(pid=1)是Linux系統中用戶空間的第一個進程,主要工作如下:

  • 創建一塊共享的內存空間,用於屬性服務器;
  • 解析各個rc文件,並啟動相應屬性服務進程;
  • 初始化epoll,依次設置signal、property、keychord這3個fd可讀時相對應的回調函數;
  • 進入無限循環狀態,執行如下流程:
    • 檢查action_queue列表是否為空,若不為空則執行相應的action;
    • 檢查是否需要重啟的進程,若有則將其重新啟動;
    • 進入epoll_wait等待狀態,直到系統屬性變化事件(property_set改變屬性值),或者收到子進程的信號SIGCHLD,再或者keychord 鍵盤輸入事件,則會退出等待狀態,執行相應的回調函數。

可見init進程在開機之后的核心工作就是響應property變化事件和回收僵屍進程。當某個進程調用property_set來改變一個系統屬性值時,系統會通過socket向init進程發送一個property變化的事件通知,那么property fd會變成可讀,init進程采用epoll機制監聽該fd則會 觸發回調handle_property_set_fd()方法。回收僵屍進程,在Linux內核中,如父進程不等待子進程的結束直接退出,會導致子進程在結束后變成僵屍進程,占用系統資源。為此,init進程專門安裝了SIGCHLD信號接收器,當某些子進程退出時發現其父進程已經退出,則會向init進程發送SIGCHLD信號,init進程調用回調方法handle_signal()來回收僵屍子進程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM