您的位置:首页 > 移动开发 > Android开发

Android 启动过程init分析

2014-10-09 14:20 267 查看
1、Init为了防止僵尸程序出现,在启动过程中Init下的子进程需要给Init发送SIGCHLD信号来得知子进程结束的情况。Linux中僵尸程序是在进程结束后未及时处理造成内从空间被占用的情况。又由于系统默认情况下进程的暂停也会向父进程发送SIGCHLD信号,因此Init第一部代码如下:

struct sigaction act;
………………
act.sa_handler = sigchld_handler;
act.sa_flags = SA_NOCLDSTOP;
act.sa_mask = 0;
act.sa_restorer = NULL;
sigaction(SIGCHLD, &act, 0);



init需要忽略子进程在暂停时发出的SIGCHLD信号,因此将act.sa_flags 置为SA_NOCLDSTOP,该标志位的含义是就是要求系统在子进程暂停时不发送SIGCHLD信号。

2、创建文件系统目录并挂载相关的文件系统

umask(0);//清除屏蔽字,保证新建的目录的访问权限不受屏蔽字影响
root的默认权限 umask 为 022,一般身份用户为 002
mkdir("/dev", 0755);
mkdir("/proc", 0755);
mkdir("/sys", 0755);

mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);</span>


3、相关权限

umask值为022 默认目录权限为0777- 022 =0755 默认目录权限为0666- 022 =0655

第一个字符代表这个文件的类型(如目录、文件或链接文件等等):

当为[ d ]则是目录;

当为[ - ]则是文件;

若是[ l ]则表示为连结档(link file);

若是[ b ]则表示为装置文件里面的可供储存的接口设备(可随机存取装置);

若是[ c ]则表示为装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)

接下来的字符中,以三个为一组,且均为『rwx』 的三个参数的组合

< [ r ]代表可读(read)、[ w ]代表可写(write)、[ x ]代表可执行(execute) 要注意的是,这三个权限的位置不会改变,如果没有权限,就会出现减号[ - ]而已>

第一组为『文件拥有者的权限』;

第二组为『同群组的权限』;

第三组为『其他非本群组的权限』.

4、在init初始化过程中,Android分别挂载了tmpfs,devpts,proc,sysfs
4类文件系统。

4.1、tmpfs文件系统

tmpfs是一种虚拟内存文件系统,因此它会将所有的文件存储在虚拟内存中,并且tmpfs下的所有内容均为临时性的内容,如果你将tmpfs文件系统卸载后,那么其下的所有的内容将不复存在。

tmpfs有些像虚拟磁盘(ramdisk),但不是一回事。说其像虚拟磁盘,是因为它

可以使用你的RAM,但它也可以使用你的交换分区。传统的虚拟磁盘是一个块设

备,而且需要一个mkfs之类的命令格式化它才能使用。tmpfs是一个独立的文件系

统,不是块设备,只要挂接,立即就可以使用。

tmpfs的大下是不确定的,它最初只有很小的空间,但随着文件的复制和创建,

它的大小就会不断变化,换句话说,它会根据你的实际需要而改变大小;tmpfs的速

度非常惊人,毕竟它是驻留在RAM中的,即使用了交换分区,性能仍然非常卓越;

由于tmpfs是驻留在RAM的,因此它的内容是不持久的,断电后,tmpfs的内容就消失

了,这也是被称作tmpfs的根本原因。

关于tmpfs文件系统请参考linux内核文档:

kernel/Documentation/filesystems/tmpfs.txt

4.2、devpts文件系统

devpts文件系统为伪终端提供了一个标准接口,它的标准挂接点是/dev/pts。只要

pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文

件。

4.3、proc文件系统

proc文件系统是一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。

在proc文件系统中,你可以修改内核的参数,是不是很强大?怎么修改呢?你只需要echo一个新的值到对应的文件中即可,但是如果在修改过程中发生错误的话,那么你将别无选择,只能重启设备。

关于proc文件系统请参考linux内核文档:

kernel/Documentation/filesystems/proc.txt

4.4、 sysfs文件系统

与proc文件系统类似,sysfs文件系统也是一个不占有任何磁盘空间的虚拟文件系

统。它通常被挂接在/sys目录下。sysfs文件系统是Linux2.6内核引入的,它把连接在系

统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。

5、屏蔽标准的输入输出,即标准的输入输出定向到NULL设备。

通过调用函数open_devnull_stdio实现

void open_devnull_stdio(void)
{
int fd;
static const char *name = "/dev/__null__";
//创建一个字符专用文件(character special  file) /dev/__null__
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
//获取/dev/__null__的文件描述符,并输出该文件
fd = open(name, O_RDWR);
unlink(name);
//将与进程相关的标准输入(0),标准输出(1),标准错误输出(2),均定向到NULL设备
if (fd >= 0) {
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2) {
close(fd);
}
return;
}
}

exit(1);
}


这里解释一下

dup2(fd, 0);

dup2(fd, 1);

dup2(fd, 2);

过程:

首先说明以下dup2的作用,这个函数主要是复制一个函数的描述符,一般用于重定向进程的stdin,stdout,stderr。它的原型如下:

int dup2(int oldfd, int newfd);

dup2(fd, 0);

dup2(fd, 1);

dup2(fd, 2);

这三次调用一次将依次代表stdin,stdout,stderr的描述符0,1,2,重定向到dev/null,通过这种方式达到屏蔽标准输入输出的作用。

6、初始化内核log系统

这个过程对应的源码为:log_init();

这个函数详细实现为

void log_init(void)
{
static const char *name = "/dev/__kmsg__";
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
log_fd = open(name, O_WRONLY);
//当进程在进行exec系统调用时,要确保log_fd是关闭的(通过FD_CLOEXEC标志位来设置).
fcntl(log_fd, F_SETFD, FD_CLOEXEC);
unlink(name);
}
}
unlink是删除名为name的文档,但有其他进程打开了此文件,则在所有关于此文件的文件描述词皆关闭后才会删除。

S_IFCHR 0020000 字符装置

由上述实现看出内核的log输出是通过文件描述符log_fd写入的,那到底写入到什么设备呢?/dev/kmsg,这个设备则会把它收到的任何写入都作为printk的输出。printk函数是内核中运行的向控制台输出显示的函数。

7.解析init.rc

init.rc是一个可配置的初始化文件,通常定制厂商可以配置额外的初始化配置,init.%PRODUCT%.rc

init.rc是在$系统版本英文名/system/core/init/init.c中读取的,它基于“行”,包含一些用空格隔开的关键字(它属于特殊字符)

如果关键字中有空格,处理方法类似于C语言,使用/表示转义,使用“”防止关键字被断开,另外注意/在末尾表示换行

#开头的表示注释

init.rc包含4种状态类别:Actions/Commands/Services/Options

当声明一个service或者action的时候,它将隐式声明一个section,它之后跟随的command或者option都将属于这个section

action和service不能重名,否则忽略为error

actions就是在某种条件下触发一系列的命令,通常有一个trigger,形式如:

on <trigger>

<command>

<command>

service结构如下:

service <name> <pathname> [ <argument> ]*

<option>

<option>

option是service的修饰词,主要包括:

critical

表示如果服务在4分钟内存在多于4次,则系统重启到recovery mode

disabled

表示服务不会自动启动,需要手动调用名字启动

setEnv <name> <value>

设置启动环境变量

socket <name> <type> <permission> [<user> [<group>]]

开启一个unix域的socket,名字为/dev/socket/<name> , <type>只能是dgram或者stream,<user>和<group>默认为0

user <username>

表示将用户切换为<username>,用户名已经定义好了,只能是system/root

group <groupname>

表示将组切换为<groupname>

oneshot

表示这个service只启动一次

class <name>

指定一个要启动的类,这个类中如果有多个service,将会被同时启动。默认的class将会是“default”

onrestart

在重启时执行一条命令

trigger主要包括:

boot

当/init.conf加载完毕时

<name>=<value>

当<name>被设置为<value>时

device-added-<path>

设备<path>被添加时

device-removed-<path>

设备<path>被移除时

service-exited-<name>

服务<name>退出时

exec <path> [ <argument> ]*

执行一个<path>指定的程序

export <name> <value>

设置一个全局变量

ifup <interface>

使网络接口<interface>连接

import <filename>

引入其他的配置文件

hostname <name>

设置主机名

chdir <directory>

切换工作目录

chmod <octal-mode> <path>

设置访问权限

chown <owner> <group> <path>

设置用户和组

chroot <directory>

设置根目录

class_start <serviceclass>

启动类中的service

class_stop <serviceclass>

停止类中的service

domainname <name>

设置域名

insmod <path>

安装模块

mkdir <path> [mode] [owner] [group]

创建一个目录,并可以指定权限,用户和组

mount <type> <device> <dir> [ <mountoption> ]*

加载指定设备到目录下

<mountoption> 包括"ro", "rw", "remount", "noatime"

setprop <name> <value>

设置系统属性

setrlimit <resource> <cur> <max>

设置资源访问权限

start <service>

开启服务

stop <service>

停止服务

symlink <target> <path>

创建一个动态链接

sysclktz <mins_west_of_gmt>

设置系统时钟

trigger <event>

触发事件

write <path> <string> [ <string> ]*

向<path>路径的文件写入多个<string>
Options和Commands的取值参考system/core/rootdir/init.rc

init.rc的解析过程:

init文件有两个init.rc和init.hardware.rc。

init_parse_config_file("/init.rc");//解析init.rc

/* pull the kernel commandline and ramdisk properties file in */

import_kernel_cmdline(0);//从/proc/cmdline读取内核启动参数,并保存到相应的变量中

get_hardware_name(hardware, &revision);//从/proc/cpuinfo中获取硬件信息

snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);

init_parse_config_file(tmp);//解析硬件相关的init信息

介绍一下init_parse_config_file过程,这个函数负责init文件的解析。

1.首先判断关键字,只能有两种可能on或者service,通过关键字来判定section范围;

2.根据Actions和Services的格式对section进行逐行解析;

3.将解析出的内容存放到双向循环链表中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: