Android系统中的init进程与init语言

ShiDrakefor 5年前
   <p>今天是农历正月初一,一切又是全新的开始。在这个有着特殊意义的日子里,让我们来一起学习一下Android系统的启动是如何开始的。</p>    <h2>init进程与init.rc</h2>    <p>init进程是一切的开始,在Android系统中,所有进程的进程号都是不确定的,唯独init进程的进程号一定是1。因为这个进程一定是系统起来的第一个进程。并且,init进程掌控了整个系统的启动逻辑。</p>    <p>我们知道,Android可能运行在各种不同的平台,不同的设备上。因此,启动的逻辑是不尽相同的。 为了适应各种平台和设备的需求,init进程的初始化工作通过 init.rc 配置文件来管理。init.rc以Android Init Language作为配置语法,下文我们简称Android Init Language为init语言。配置文件的主入口文件是/init.rc,这个文件会通过 import 关键字引入其他的配置文件。在这里,我们统称这些文件为 init.rc 。</p>    <p>/init.rc可能 import 以下路径中的.rc文件:</p>    <ul>     <li>/init.${ro.hardware}.rc 硬件厂商提供的主配置文件</li>     <li>/system/etc/init/ 核心系统模块的配置文件</li>     <li>/vendor/etc/init/ SoC厂商提供的配置文件</li>     <li>/odm/etc/init/ 设备厂商提供的配置文件</li>    </ul>    <h2>init语法说明</h2>    <p>init语言以换行为语句分隔,以空格来为符号分隔。配置文件中支持五种类型的表达式:</p>    <ul>     <li>Action: 包含了一系列的Command</li>     <li>Command: init语言中的命令</li>     <li>Service: init进程启动的服务</li>     <li>Option: 对于服务的配置选项</li>     <li>Import: 引入其他配置文件</li>    </ul>    <p>这其中,Action和Service需要保证名称唯一。</p>    <h2>Action与Command</h2>    <p>Action表达式的语法如下:</p>    <pre>  <code class="language-java">on <trigger> [&& <trigger>]*     <command>     <command>     <command></code></pre>    <p>这里的Trigger是Action执行的触发器,当触发器条件满足时,command会被执行。触发器有两类:</p>    <ul>     <li>事件触发器 事件可以由”trigger”命令发出,也可以是init进程通过 QueueEventTrigger() 函数发出</li>     <li>属性触发器 当指定的属性满足时触发</li>    </ul>    <p>一个Action可以有多个属性触发器,但是只能包含一个事件触发器。下面是一些实例:</p>    <ul>     <li>on boot && property:a=b 在”boot”事件发生时,并且属性a的值是b时触发</li>     <li>on property:a=b && property:c=d 在属性a的值是b并且属性c的值是d时触发</li>    </ul>    <p>Action中支持的命令如下表所示:</p>    <table>     <thead>      <tr>       <th>Command</th>       <th>参数格式</th>       <th>说明</th>      </tr>     </thead>     <tbody>      <tr>       <td>bootchart_init</td>       <td>-</td>       <td>启动bootchart</td>      </tr>      <tr>       <td>chmod</td>       <td><em>octal-mode path</em></td>       <td>改变文件的访问权限</td>      </tr>      <tr>       <td>chown</td>       <td><em>owner group path</em></td>       <td>改变文件的拥有者和组</td>      </tr>      <tr>       <td>class_start</td>       <td><em>serviceclass</em></td>       <td>启动指定类别的服务</td>      </tr>      <tr>       <td>class_stop</td>       <td><em>serviceclass</em></td>       <td>停止并disable指定类别的服务</td>      </tr>      <tr>       <td>class_reset</td>       <td><em>serviceclass</em></td>       <td>停止指定类别的服务,但是不disable它们</td>      </tr>      <tr>       <td>copy</td>       <td><em>src dst</em></td>       <td>拷贝文件</td>      </tr>      <tr>       <td>domainname</td>       <td><em>name</em></td>       <td>设置域名</td>      </tr>      <tr>       <td>enable</td>       <td><em>servicename</em></td>       <td>enable一个被disable的服务</td>      </tr>      <tr>       <td>exec</td>       <td>[ <em>seclabel</em> [ <em>user</em> [ <em>group</em> ]]] -- <em>command</em> [ <em>argument</em> ]*</td>       <td>fork一个子进程来执行指定的命令</td>      </tr>      <tr>       <td>export</td>       <td><em>name value</em></td>       <td>导出环境变量</td>      </tr>      <tr>       <td>hostname</td>       <td><em>name</em></td>       <td>设置host名称</td>      </tr>      <tr>       <td>ifup</td>       <td><em>iterface</em></td>       <td>使网卡在线</td>      </tr>      <tr>       <td>insmod</td>       <td><em>path</em></td>       <td>安装指定路径的模块</td>      </tr>      <tr>       <td>load_all_props</td>       <td>-</td>       <td>从/system, /vendor等路径载入属性</td>      </tr>      <tr>       <td>load_persist_props</td>       <td>-</td>       <td>载入持久化的属性</td>      </tr>      <tr>       <td>loglevel</td>       <td><em>level</em></td>       <td>设置内核的日志级别</td>      </tr>      <tr>       <td>mkdir</td>       <td><em>path</em> [ <em>mode</em> ] [ <em>owner</em> ] [ <em>group</em> ]</td>       <td>创建目录</td>      </tr>      <tr>       <td>mount_all</td>       <td><em>fstab</em> [ <em>path</em> ]* [-- <em>option</em> ]</td>       <td>挂载文件系统并且导入指定的.rc文件</td>      </tr>      <tr>       <td>mount</td>       <td><em>type</em> <em>device</em> <em>dir</em> [ <em>flag</em> ]* [ <em>options</em> ]</td>       <td>挂载一个文件系统</td>      </tr>      <tr>       <td>powerctl</td>       <td>-</td>       <td>内部实现使用</td>      </tr>      <tr>       <td>restart</td>       <td><em>service</em></td>       <td>重启服务</td>      </tr>      <tr>       <td>restorecon</td>       <td><em>path</em> [ <em>path</em> ]*</td>       <td>设定文件的安全上下文</td>      </tr>      <tr>       <td>restorecon_recursive</td>       <td><em>path</em> [ <em>path</em> ]*</td>       <td>restorecon的递归版本</td>      </tr>      <tr>       <td>rm</td>       <td><em>path</em></td>       <td>对于指定路径调用unlink(2)</td>      </tr>      <tr>       <td>rmdir</td>       <td><em>path</em></td>       <td>删除文件夹</td>      </tr>      <tr>       <td>setprop</td>       <td><em>name</em> <em>value</em></td>       <td>设置属性值</td>      </tr>      <tr>       <td>setrlimit</td>       <td><em>resource</em> <em>cur</em> <em>max</em></td>       <td>指定资源的rlimit</td>      </tr>      <tr>       <td>start</td>       <td><em>service</em></td>       <td>启动服务</td>      </tr>      <tr>       <td>stop</td>       <td><em>service</em></td>       <td>停止服务</td>      </tr>      <tr>       <td>swapon_all</td>       <td><em>fstab</em></td>       <td>在指定文件上调用fs_mgr_swapon_all</td>      </tr>      <tr>       <td>symlink</td>       <td><em>target</em> <em>path</em></td>       <td>创建符号链接</td>      </tr>      <tr>       <td>sysclktz</td>       <td><em>mins_west_of_gmt</em></td>       <td>指定系统时钟基准</td>      </tr>      <tr>       <td>trigger</td>       <td><em>event</em></td>       <td>触发一个事件</td>      </tr>      <tr>       <td>umount</td>       <td><em>path</em></td>       <td>unmount指定的文件系统</td>      </tr>      <tr>       <td>verity_load_state</td>       <td>-</td>       <td>内部实现使用</td>      </tr>      <tr>       <td>verity_update_state</td>       <td><em>mount_point</em></td>       <td>内部实现使用</td>      </tr>      <tr>       <td>wait</td>       <td><em>path</em> [ <em>timeout</em> ]</td>       <td>等待某个文件存在直到超时</td>      </tr>      <tr>       <td>write</td>       <td><em>path</em> <em>content</em></td>       <td>写入内容到指定文件</td>      </tr>     </tbody>    </table>    <h2>Service与Option</h2>    <p>Service是init进程启动的可执行程序。服务可以选择在自己退出之后,由init将其重启。</p>    <p>Service表达式的语法格式如下:</p>    <pre>  <code class="language-java">service <name> <pathname> [ <argument> ]*     <option>     <option>     ...</code></pre>    <p>Option是对服务的修饰,它们影响着init进程如何以及何时启动服务。所有支持的option如下表所示:</p>    <table>     <thead>      <tr>       <th>option</th>       <th>参数格式</th>       <th>说明</th>      </tr>     </thead>     <tbody>      <tr>       <td>critical</td>       <td>-</td>       <td>标识为系统关键服务,该服务若退出多次将导致系统重启到recovery模式</td>      </tr>      <tr>       <td>disabled</td>       <td>-</td>       <td>不会随着类别自动启动,必须明确start</td>      </tr>      <tr>       <td>setenv</td>       <td><em>name</em> <em>value</em></td>       <td>为启动的进程设置环境变量</td>      </tr>      <tr>       <td>socket</td>       <td><em>name</em> <em>type</em> <em>perm</em> [ <em>user</em> [ <em>group</em> [ <em>seclabel</em> ]]]</td>       <td>创建Unix Domain Socket</td>      </tr>      <tr>       <td>user</td>       <td><em>username</em></td>       <td>在执行服务之前切换用户</td>      </tr>      <tr>       <td>group</td>       <td><em>groupname</em> [ <em>groupname</em> ]*</td>       <td>在执行执行之前切换组</td>      </tr>      <tr>       <td>seclabel</td>       <td><em>seclabel</em></td>       <td>在执行服务之前切换seclabel</td>      </tr>      <tr>       <td>oneshot</td>       <td>-</td>       <td>一次性服务,死亡之后不用重启</td>      </tr>      <tr>       <td>class</td>       <td><em>name</em></td>       <td>指定服务的类别</td>      </tr>      <tr>       <td>onrestart</td>       <td>-</td>       <td>当服务启动时执行命令</td>      </tr>      <tr>       <td>writepid</td>       <td><em>file…</em></td>       <td>写入子进程的pid到指定文件</td>      </tr>     </tbody>    </table>    <p>这其中有两个特别重要的进程就是: <strong>zygote</strong> 和 <strong>system_server</strong> 进程。</p>    <ul>     <li><strong>zygote</strong> 的中文意思是“受精卵“。这是一个很有寓意的名称:所有的应用进程都是由 zygote fork出来的子进程,因此zygote进程是所有应用进程的父进程。</li>     <li><strong>system_server</strong> 这个进程正如其名称一样,这是一个系统服务器。Framework层的几乎所有服务都位于这个进程中。这其中就包括管理四大组件的 ActivityManagerService 。</li>    </ul>    <h2>Import</h2>    <p>import是一个关键字,并不是一个命令。可以在.rc文件中通过这个关键字来加载其他的.rc文件。它的语法很简单:</p>    <pre>  <code class="language-java">import path</code></pre>    <p>path可以是另外一个.rc文件,也可以是一个文件夹。如果是文件夹,那么这个文件夹下面的所有文件都会被导入,但是不会循环加载子目录。</p>    <h2>init.rc代码实例</h2>    <p>AOSP中包含了Android系统需要的最基本的.rc文件,它们位于这个路径:/system/core/rootdir/ 。</p>    <p>我们选取其中了一两个代码片段来了解一下:</p>    <pre>  <code class="language-java"># /system/core/rootdir/init.rc    import /init.environ.rc  import /init.usb.rc  import /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        # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.      mkdir /mnt 0775 root system        # Set the security context of /postinstall if present.      restorecon /postinstall        start ueventd    on init      sysclktz 0        # Mix device-specific information into the entropy pool      copy /proc/cmdline /dev/urandom      copy /default.prop /dev/urandom        # Backward compatibility.      symlink /system/etc /etc      symlink /sys/kernel/debug /d        # Link /vendor to /system/vendor for devices without a vendor partition.      symlink /system/vendor /vendor  ...</code></pre>    <p>这是根目录/init.rc文件中一开始的代码片段。有了前面的讲解之后,这段代码应当还是比较好理解的。在这段代码中:</p>    <ul>     <li>一开始通过import关键字引入了其他几个.rc文件</li>     <li>设定了一个事件为early-init的Action</li>     <li>设定了一个事件init的Action</li>    </ul>    <p>“eraly-init”和”init”事件都是由init进程发出的。</p>    <p>下面,我们再来看另外一个代码片段:</p>    <pre>  <code class="language-java"># /system/core/rootdir/init.zygote32.rc    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 audioserver      onrestart restart cameraserver      onrestart restart media      onrestart restart netd      writepid /dev/cpuset/foreground/tasks</code></pre>    <p>这段代码定义了Andorid系统中一个非常重要的服务: zygote 。这个服务是通过可执行命令/system/bin/app_process启动的,启动的时候传递了参数: -Xzygote /system/bin --zygote --start-system-server 。</p>    <p>关于Zygote我们已经在另外一篇文章中讲解过了,参见这里Zygote进程。</p>    <h2>结束语</h2>    <p> </p>    <p>来自:http://qiangbo.space/2017-01-27/AndroidAnatomy_Init/</p>    <p> </p>