Android 启动过程init分析
2014-10-09 14:20
267 查看
1、Init为了防止僵尸程序出现,在启动过程中Init下的子进程需要给Init发送SIGCHLD信号来得知子进程结束的情况。Linux中僵尸程序是在进程结束后未及时处理造成内从空间被占用的情况。又由于系统默认情况下进程的暂停也会向父进程发送SIGCHLD信号,因此Init第一部代码如下:
init需要忽略子进程在暂停时发出的SIGCHLD信号,因此将act.sa_flags 置为SA_NOCLDSTOP,该标志位的含义是就是要求系统在子进程暂停时不发送SIGCHLD信号。
2、创建文件系统目录并挂载相关的文件系统
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实现
这里解释一下
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();
这个函数详细实现为
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.将解析出的内容存放到双向循环链表中。
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.将解析出的内容存放到双向循环链表中。
相关文章推荐
- 分析Android 根文件系统启动过程(init守护进程分析)
- Android init 启动过程分析
- Android init 启动过程分析1
- 分析Android 根文件系统启动过程(init守护进程分析)
- Android init 启动过程分析
- 分析Android 根文件系统启动过程(init守护进程分析)
- Android init 启动过程分析2
- 分析Android 根文件系统启动过程(init守护进程分析)
- Android init 启动过程分析(2)
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析)
- Android init 启动过程分析
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析)
- Android init 启动过程分析
- 分析Android 根文件系统启动过程(init守护进程分析)