第一個用戶進程 – Android 的 Init 進程

本文嘗試對着 《深入理解 Android 5.0 系統》來對 android 9.0 的啟動代碼進行分析,但是分析過程中發現自己缺乏操作系統方面的知識,以致於只能做一些簡單分析。最近也買了一本操作系統的書 《操作系統:精髓與設計原理》(第9版) ,等後續基礎提升後,會繼續進行分析。

雖然 Init 進程是 Linux 內核啟動後創建的第一個用戶進程,地位非常重要。Init 進程在初始化過程中會啟動很多重要的守護進程,因此,了解 Init 進程的啟動過程將有助於我們更好地理解 Android系統。Init 除了完成系統的初始化之外,本身也是一個守護進程,擔負著系統部分很重要的職責。本文將詳細介紹 Init 進程的初始化以及它作為守護進程的功能。

先簡單介紹 Android 的啟動過程。從系統角度看,Android 的啟動過程可分為 bootloader 引導、裝載和啟動Linux 內核、啟動 Android 系統 3 個大的階段。其中 Android系統的啟動還可以細分為啟動 Init 進程、啟動 Zygote、啟動 SystemService、啟動 SystemServer、啟動 Home 等多個階段。下圖揭示了整個 Android 的啟動過程。

下面簡單介紹設備的啟動過程。

(1)Bootloader 引導。

當我們按下手機的電源鍵時,最先運行的就是 bootloader。bootloader 主要的作用是初始化基本的硬件設備(如 CPU、內存、Flash 等)並且通過建立內存空間映射,為裝載 Linux 內核準備好合適的運行環境。一旦 Linux 內核裝載完畢,bootloader 將會從內存中清除掉。

如果用戶在 Bootloader 運行期間,按下預定義的組合鍵,可以進入系統的更新模塊。Android的下載更新可以選擇進入 Fastboot 模式或者 Recover 模式。

  • Fastboot 是 Android設計的一套通過 USB 來更新手機分區映像的協議,方便開發人員能快速更新指定的手機分區。但是一般的零售機上往往去掉了 Fastboot,Google 銷售的開發機則帶有 Fastboot 模塊。
  • Recovery 模式是 Android 特有的升級系統。利用 Recovery 模式,手機可以進行恢復出廠設置,或者執行 OTA、補丁和固件升級。進入 Recovery 模式實際上是啟動了一個文本模式的 Linux。

(2)裝載和啟動 Linux 內核。

Android 的 boot.img 存放的就是 Linux 內核和一個根文件系統。Bootloader 會把 boot.img 映像裝載進內存。然後 Linux 內核會執行整個系統的初始化,完成後裝載根文件系統,最後啟動 Init
進程。

(3)啟動Init 進程。

Linux 內核加載完畢後,會首先啟動 Init 進程,Init 進程是系統的第一個進程。在 Init 進程的啟動過程中,會解析 Linux 的配置腳本 init.rc 文件。根據 init.rc 文件的內容,Init 進程會裝載 Android的文件系統、創建系統目錄、初始化屬性系統、啟動 Android 系統重要的守護進程,這些進程包括 USB 守護進程、adb 守護進程、vold 守護進程、rild 守護進程等。

最後 Init 進程也會作為守護進程來執行修改屬性請求,重啟崩潰的進程等操作。

(4)啟動 ServiceManager。

ServiceManager 由 Init 進程啟動。它主要的作用是管理 Binder 服務,負責 Binder服務的註冊與查找。

(5)啟動 Zygote進程。

Init 進程初始化結束時,會啟動 Zygote 進程。Zygote 進程負責 fork 出應用進程,是所有應用進程的父進程。Zygote 進程初始化時會創建 Dalivik 虛擬機、預裝載系統的資源文件和 Java 類。所有從Zygote進程 fork 出的用戶進程將繼承和共享這些預加載的資源,不用浪費時間重新加載,加快了應用程序的啟動過程。啟動結束後,Zygote 進程也將變為守護進程,負責響應啟動 APK應用程序的請求。

(6)啟動 SystemServer。

SystemServer 是 Zygote 進程 fork 出的第一個進程,也是整個 Android 系統的核心進程。在 SystemServer 中運行着 Android 系統大部分的 Binder 服務。SystemServer 首先啟動本地服務 Sensor Service;接着啟動包括 ActivityManagerService、WindowsMangerService、PackageManagerService在內的所有 Java 服務。

(7)啟動 MediaServer。

MediaServer 由 Init 進程啟動。它包含了一些多媒體相關的本地 Binder 服務,包括∶ CameraService、AudioFlingerService、MediaPlayerService 和 AudioPolicyService。

(8)啟動 Launcher。

SystemServer 加載完所有 Java 服務後,最後會調用 ActivityManagerService 的 SystemReady)方法。在這個方法的執行中,會發出 Intent”android.intent.category.HOME”。凡是響應這個 Intent的 apk 應用都會運行起來,Launcher 應用是 Android 系統默認的桌面應用,一般只有它會響應這個 Intent,因此,系統開機後,第一個運行的應用就是 Launcher。

Init 進程的初始化過程

Init 進程的源碼位於目錄 system/core/init下。程序的入口函數 main() 位於文件 init.cpp 中。 pie 9.0 main 函數的流程 main()函數比較長,整個 Init 進程的啟動流程都在這個函數中。下面我們把 main()函數分成小段,一段段地介紹其功能和作用。 具體可以看下面的代碼:

  1 // /system/core/init/init.cpp
  2 int main(int argc, char** argv) {
  3     if (!strcmp(basename(argv[0]), "ueventd")) {
  4         return ueventd_main(argc, argv);
  5     }
  6 
  7     if (!strcmp(basename(argv[0]), "watchdogd")) {
  8         return watchdogd_main(argc, argv);
  9     }
 10 
 11     if (argc > 1 && !strcmp(argv[1], "subcontext")) {
 12         InitKernelLogging(argv);
 13         const BuiltinFunctionMap function_map;
 14         return SubcontextMain(argc, argv, &function_map);
 15     }
 16 
 17     if (REBOOT_BOOTLOADER_ON_PANIC) {
 18         InstallRebootSignalHandlers();
 19     }
 20 
 21     bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
 22 
 23     if (is_first_stage) {
 24         boot_clock::time_point start_time = boot_clock::now();
 25 
 26         // Clear the umask.
 27         umask(0);
 28 
 29         clearenv();
 30         setenv("PATH", _PATH_DEFPATH, 1);
 31         // Get the basic filesystem setup we need put together in the initramdisk
 32         // on / and then we'll let the rc file figure out the rest.
 33         mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
 34         mkdir("/dev/pts", 0755);
 35         mkdir("/dev/socket", 0755);
 36         mount("devpts", "/dev/pts", "devpts", 0, NULL);
 37         #define MAKE_STR(x) __STRING(x)
 38         mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
 39         // Don't expose the raw commandline to unprivileged processes.
 40         chmod("/proc/cmdline", 0440);
 41         gid_t groups[] = { AID_READPROC };
 42         setgroups(arraysize(groups), groups);
 43         mount("sysfs", "/sys", "sysfs", 0, NULL);
 44         mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
 45 
 46         mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
 47 
 48         if constexpr (WORLD_WRITABLE_KMSG) {
 49             mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
 50         }
 51 
 52         mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
 53         mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
 54 
 55         // Mount staging areas for devices managed by vold
 56         // See storage config details at //source.android.com/devices/storage/
 57         mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
 58               "mode=0755,uid=0,gid=1000");
 59         // /mnt/vendor is used to mount vendor-specific partitions that can not be
 60         // part of the vendor partition, e.g. because they are mounted read-write.
 61         mkdir("/mnt/vendor", 0755);
 62 
 63         // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
 64         // talk to the outside world...
 65         InitKernelLogging(argv);
 66 
 67         LOG(INFO) << "init first stage started!";
 68 
 69         if (!DoFirstStageMount()) {
 70             LOG(FATAL) << "Failed to mount required partitions early ...";
 71         }
 72 
 73         SetInitAvbVersionInRecovery();
 74 
 75         // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
 76         global_seccomp();
 77 
 78         // Set up SELinux, loading the SELinux policy.
 79         SelinuxSetupKernelLogging();
 80         SelinuxInitialize();
 81 
 82         // We're in the kernel domain, so re-exec init to transition to the init domain now
 83         // that the SELinux policy has been loaded.
 84         if (selinux_android_restorecon("/init", 0) == -1) {
 85             PLOG(FATAL) << "restorecon failed of /init failed";
 86         }
 87 
 88         setenv("INIT_SECOND_STAGE", "true", 1);
 89 
 90         static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
 91         uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
 92         setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
 93 
 94         char* path = argv[0];
 95         char* args[] = { path, nullptr };
 96         execv(path, args);
 97 
 98         // execv() only returns if an error happened, in which case we
 99         // panic and never fall through this conditional.
100         PLOG(FATAL) << "execv(\"" << path << "\") failed";
101     }
102 
103     // At this point we're in the second stage of init.
104     InitKernelLogging(argv);
105     LOG(INFO) << "init second stage started!";
106 
107     // Set up a session keyring that all processes will have access to. It
108     // will hold things like FBE encryption keys. No process should override
109     // its session keyring.
110     keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
111 
112     // Indicate that booting is in progress to background fw loaders, etc.
113     close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
114 
115     property_init();
116 
117     // If arguments are passed both on the command line and in DT,
118     // properties set in DT always have priority over the command-line ones.
119     process_kernel_dt();
120     process_kernel_cmdline();
121 
122     // Propagate the kernel variables to internal variables
123     // used by init as well as the current required properties.
124     export_kernel_boot_props();
125 
126     // Make the time that init started available for bootstat to log.
127     property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
128     property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
129 
130     // Set libavb version for Framework-only OTA match in Treble build.
131     const char* avb_version = getenv("INIT_AVB_VERSION");
132     if (avb_version) property_set("ro.boot.avb_version", avb_version);
133 
134     // Clean up our environment.
135     unsetenv("INIT_SECOND_STAGE");
136     unsetenv("INIT_STARTED_AT");
137     unsetenv("INIT_SELINUX_TOOK");
138     unsetenv("INIT_AVB_VERSION");
139 
140     // Now set up SELinux for second stage.
141     SelinuxSetupKernelLogging();
142     SelabelInitialize();
143     SelinuxRestoreContext();
144 
145     epoll_fd = epoll_create1(EPOLL_CLOEXEC);
146     if (epoll_fd == -1) {
147         PLOG(FATAL) << "epoll_create1 failed";
148     }
149 
150     sigchld_handler_init();
151 
152     if (!IsRebootCapable()) {
153         // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
154         // In that case, receiving SIGTERM will cause the system to shut down.
155         InstallSigtermHandler();
156     }
157 
158     property_load_boot_defaults();
159     export_oem_lock_status();
160     start_property_service();
161     set_usb_controller();
162 
163     const BuiltinFunctionMap function_map;
164     Action::set_function_map(&function_map);
165 
166     subcontexts = InitializeSubcontexts();
167 
168     ActionManager& am = ActionManager::GetInstance();
169     ServiceList& sm = ServiceList::GetInstance();
170 
171     LoadBootScripts(am, sm);
172 
173     // Turning this on and letting the INFO logging be discarded adds 0.2s to
174     // Nexus 9 boot time, so it's disabled by default.
175     if (false) DumpState();
176 
177     am.QueueEventTrigger("early-init");
178 
179     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
180     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
181     // ... so that we can start queuing up actions that require stuff from /dev.
182     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
183     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
184     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
185     am.QueueBuiltinAction(keychord_init_action, "keychord_init");
186     am.QueueBuiltinAction(console_init_action, "console_init");
187 
188     // Trigger all the boot actions to get us started.
189     am.QueueEventTrigger("init");
190 
191     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
192     // wasn't ready immediately after wait_for_coldboot_done
193     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
194 
195     // Don't mount filesystems or start core system services in charger mode.
196     std::string bootmode = GetProperty("ro.bootmode", "");
197     if (bootmode == "charger") {
198         am.QueueEventTrigger("charger");
199     } else {
200         am.QueueEventTrigger("late-init");
201     }
202 
203     // Run all property triggers based on current state of the properties.
204     am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
205 
206     while (true) {
207         // By default, sleep until something happens.
208         int epoll_timeout_ms = -1;
209 
210         if (do_shutdown && !shutting_down) {
211             do_shutdown = false;
212             if (HandlePowerctlMessage(shutdown_command)) {
213                 shutting_down = true;
214             }
215         }
216 
217         if (!(waiting_for_prop || Service::is_exec_service_running())) {
218             am.ExecuteOneCommand();
219         }
220         if (!(waiting_for_prop || Service::is_exec_service_running())) {
221             if (!shutting_down) {
222                 auto next_process_restart_time = RestartProcesses();
223 
224                 // If there's a process that needs restarting, wake up in time for that.
225                 if (next_process_restart_time) {
226                     epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
227                                            *next_process_restart_time - boot_clock::now())
228                                            .count();
229                     if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
230                 }
231             }
232 
233             // If there's more work to do, wake up again immediately.
234             if (am.HasMoreCommands()) epoll_timeout_ms = 0;
235         }
236 
237         epoll_event ev;
238         int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
239         if (nr == -1) {
240             PLOG(ERROR) << "epoll_wait failed";
241         } else if (nr == 1) {
242             ((void (*)()) ev.data.ptr)();
243         }
244     }
245 
246     return 0;
247 }

View Code

(1)進入 main()函數後,首先檢查啟動程序的文件名。如果文件名是”ueventd”,執行守護進程 ueventd 的主函數 ueventd main(O,如果文件名是”watchdogd”,執行看門狗守護進程的主函數 watchdogd_main()。都不是則繼續執行。

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

    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }
   // 參數個數大於1,就會獲取多個
    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
        InitKernelLogging(argv);
        const BuiltinFunctionMap function_map;
        return SubcontextMain(argc, argv, &function_map);
    }

從這裡可以看出 init 進程的代碼里也包含了另外兩個守護進程的代碼,因為這幾個守護進程的代碼重合度高,所以,開發人員乾脆把它們都放在一起了。但是在編譯時,Android 生成了兩個指向 init 文件的符號連接ueventd 和 watchdogd,這樣啟動時如果執行的是這兩個符號連接,main函數就能判斷出到底要啟動哪個守護進程。

(2)創建一些基本的目錄,包括/dev、/porc、/sys等;同時把一些文件系統,如 tmpfs、devpt、 proc、sysfs 等 mount 到相應的目錄。 

     mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        // Don't expose the raw commandline to unprivileged processes.
        chmod("/proc/cmdline", 0440);
        gid_t groups[] = { AID_READPROC };
        setgroups(arraysize(groups), groups);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);

        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));

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

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

tmpfs 是一種基於內存的文件系統,mount 後就可以使用。tmpfs 文件系統下的文件都存放在內存中,訪問速度快,但是關機後所有內容都會丟失,因此 tmpfs 文件系統比較適合存放一些臨時性的文件。tmpfs 文件系統的大小是動態變化的,剛開始佔用空間很小,隨着文件的增多會隨之變大,很節省空間。Android 將 tmpfs 文件系統 mount 到/dev目錄,/dev 目錄用來存放系統創造的設備節點,正好符合 tmpfs 文件系統的特點。

  • devpts 是虛擬終端文件系統,它通常 mount 在目錄/dev/pts 下。

  • proc 也是一種基於內存的虛擬文件系統,它可以看作是內核內部數據結構的接口,通過它可以獲得系統的信息,同時能夠在運行時修改特定的內核參數。

  • sysfs 文件系統和 proc 文件系統類似,它是 Linux 2.6 內核引入的,作用是把系統的設備和總線按層次組織起來,使得它們可以在用戶空間存取,用來向用戶空間導出內核的數據結構及它們的屬性。

(3)在/dev 目錄下創建一個空文件”.booting”表示初始化正在進行。

close(open("/dev/.booting",o_WRONLY I o_CREAT,0000));

 

is_bootingO函數會依靠空文件”.booting”來判斷是否進程處於初始化中。初始化結束後這個文件將被刪除。

(4)調用 property init()函數來初始化 Android 的屬性系統。 property_init();

property_init()函數主要作用是創建一個共享區域來存儲屬性值。下節分析屬性系統時會詳細介紹。

(5)調用 property load boot defaults()函數。

is_charger = !strcmp (bootmode,"charger"); property_load_boot_defaults ();

property_load_boot_defaults()函數將解析設備根目錄下的 default.prop 文件,把文件中定義的屬性值讀出來設置到屬性系統中。

所謂充電模式是指插着充電器開機時設備會進入的狀態。這時 kernel 和 init 進程會啟動,但是大部分的服務都不會啟動。

(6)main() 函數最後會進入一個無限 while 循環,每次循環開始都會調用 ExecuteOneCommand()函數來執行命令列表中的一條命令,同時調用 RestartProcesses()函數來啟動服務進程∶

while (true) {
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1;

        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }

        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_restart_time = RestartProcesses();

                // If there's a process that needs restarting, wake up in time for that.
                if (next_process_restart_time) {
                    epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
                                           *next_process_restart_time - boot_clock::now())
                                           .count();
                    if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
                }
            }

            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
        }

        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
        if (nr == -1) {
            PLOG(ERROR) << "epoll_wait failed";
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
} 

(7)Init 進程初始化系統後,會化身為守護進程來處理子進程的死亡信號,修改屬性的請求和組合鍵盤鍵事件。

 啟動 Service 進程

在 main()函數的 for 循環中,會調用 RestartProcesses()函數來啟動服務列表中的服務進程。函數 RestartProcesses()的代碼如下所示∶

static std::optional<boot_clock::time_point> RestartProcesses() {
    std::optional<boot_clock::time_point> next_process_restart_time;
    for (const auto& s : ServiceList::GetInstance()) {
        if (!(s->flags() & SVC_RESTARTING)) continue;

        auto restart_time = s->time_started() + 5s;
        if (boot_clock::now() > restart_time) {
            if (auto result = s->Start(); !result) {
                LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
            }
        } else {
            if (!next_process_restart_time || restart_time < *next_process_restart_time) {
                next_process_restart_time = restart_time;
            }
        }
    }
    return next_process_restart_time;
}

會檢查 ServiceList 單例列表中的每個服務,凡是帶有 SVC RESTARTING 標誌的,進行啟動,在啟動前會判斷當前時間是否已經到達啟動時間。如果時間達到了,就會調用對應 service 的方法來進行啟動:

// /system/core/init/service.cpp

Result<Success> Service::Start() { bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET)); // Starting a service removes it from the disabled or reset state and // immediately takes it out of the restarting state if it was in there. flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); // Running processes require no additional work --- if they're in the // process of exiting, we've ensured that they will immediately restart // on exit, unless they are ONESHOT. For ONESHOT service, if it's in // stopping status, we just set SVC_RESTART flag so it will get restarted // in Reap(). 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(); } ......

SVC_DISABLED、SVC_RESTARTING、SVC_RESET、SVC RESTART、SVC_DISABLED_START 這 5 個標誌都是和啟動進程相關,需要先清除掉。如果服務帶有 SVC RUNNING 標誌,說明服務進程已經運行,這裡就不重複啟動了。

fork 子進程

 pid_t pid = -1;
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork();
    }

fork 調用的一個奇妙之處就是它僅僅被調用一次,卻能夠返回兩次,它可能有三種不同的返回值:

  • (1)在父進程中,fork返回新創建子進程的進程ID;

  • (2)在子進程中,fork返回0;

  • (3)如果出現錯誤,fork返回一個負值。

在 fork 函數執行完畢後,如果創建新進程成功,則出現兩個進程,一個是子進程,一個是父進程。在子進程中,fork函數返回0,在父進程中,fork返回新創建子進程的進程ID。我們可以通過fork返回的值來判斷當前進程是子進程還是父進程。

解析啟動腳本 init.rc

Init 進程啟動時最重要的工作是解析並執行啟動文件 init.rc。本節將介紹 init.rc 文件的格式,以及Init 進程解析腳本文件的流程。

 init.rc 文件格式介紹

init.rc 文件是以塊(section)為單位組織的,一個”塊”可以包含多行。”塊”分成兩大類;一類稱為”行為(action)”;另一類稱為”服務(service)”。”行為”塊以關鍵字”on”開始,表示一堆命令的集合,”服務”塊以關鍵字”service”開始,表示啟動某個進程的方式和參數。”塊”以關鍵字”on”或”service”開始,直到下一個”on”或”service”結束,中間所有行都屬於這個”塊”(空行或注釋行沒有分割作用)。注釋以’#’號開始(如下所示是一份格式樣本)。

# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
#

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000

    # Disable sysrq from keyboard
    write /proc/sys/kernel/sysrq 0

    # Set the security context of /adb_keys if present.
    restorecon /adb_keys

    # Set the security context of /postinstall if present.
    restorecon /postinstall

    # Mount cgroup mount point for cpu accounting
    mount cgroup none /acct nodev noexec nosuid cpuacct
    mkdir /acct/uid

    # root memory control cgroup, used by lmkd
    mkdir /dev/memcg 0700 root system
    mount cgroup none /dev/memcg nodev noexec nosuid memory
    # app mem cgroups, used by activity manager, lmkd and zygote
    mkdir /dev/memcg/apps/ 0755 system system
    # cgroup for system_server and surfaceflinger
    mkdir /dev/memcg/system 0550 system system

    start ueventd

on init
    sysclktz 0

    # Mix device-specific information into the entropy pool
    copy /proc/cmdline /dev/urandom
    copy /default.prop /dev/urandom

    symlink /system/bin /bin
    symlink /system/etc /etc

    # Backward compatibility.
    symlink /sys/kernel/debug /d

無論是”行為”塊還是”服務”塊,並不是按照文件中的編排順序逐一執行的。它們只是一份放在這裡的定義,至於執行與否以及何時執行要由 init 進程在運行時決定。

“行為(action)”的關鍵字”on”後面跟的字串稱為”觸發器(trigger)”,例如,實例中的”boot”和”nonencrypted”。”觸發器”後面是命令列表。命令列表中的每一行都是一條命令,命令的種類非常多。

 

Tags: