-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Android系统启动流程之init进程(二) #9
Labels
Comments
文章受益良多,催更.rc文件解析,哈哈哈哈 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
前言
上一篇中讲了init进程的第一阶段,我们接着讲第二阶段,主要有以下内容
本文涉及到的文件
一、创建进程会话密钥并初始化属性系统
第二阶段一开始会有一个is_first_stage的判断,由于之前第一阶段最后有设置INIT_SECOND_STAGE,
因此直接跳过一大段代码。从keyctl开始才是重点内容,我们一一展开来看
1.1 keyctl
定义在platform/system/core/init/keyutils.h
keyctl将主要的工作交给__NR_keyctl这个系统调用,keyctl是Linux系统操纵内核的通讯密钥管理工具
我们分析下 keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 1)
参考linux手册
这里并没有拿返回值,估计就是为了新建会话密钥环了,从注释Set up a session keyring也可看出
1.2 property_init
定义在 platform/system/core/init/property_service.cpp
直接交给 __system_property_area_init 处理
__system_property_area_init 定义在/bionic/libc/bionic/system_properties.cpp
看名字大概知道是用来初始化属性系统区域的,应该是分门别类更准确些,首先清除缓存,这里主要是清除几个链表以及在内存中的映射,新建property_filename目录,这个目录的值为 /dev/_properties_
然后就是调用initialize_properties加载一些系统属性的类别信息,最后将加载的链表写入文件并映射到内存
1.3 initialize_properties
定义在/bionic/libc/bionic/system_properties.cpp
交给 initialize_properties_from_file 处理,指定了一些文件路径
1.4 initialize_properties_from_file
定义在/bionic/libc/bionic/system_properties.cpp
这个函数主要工作是解析属性类别文件,对属性做一下分类,具体就是一行行解析,过滤 # 开头的、只读到key的、从ctl.开头的,然后将解析出来的键值对放到两个链表中
prefixes链表存放key(其实是一些key的前缀),contexts链表存放value(其实是对应key应当属于那些类别的信息),这样的好处是将庞杂的属性根据前缀分类,存储到不同的context中,
查找和修改是非常高效的,类似map的做法
1.5 链表结构
定义在/bionic/libc/bionic/system_properties.cpp
之前我们看到有两个重要的链表prefixs和contexts,frefixs存key(其实是一些key的前缀),contexts存value(其实是对应key应当属于那些类别的信息),接下来我们看下这两个链表的结构
context_node中有三个比较重要的属性context_、_pa和next,context_用来存类别信息,_pa是存具体key-value节点的,next是链表下一个节点
prefix_node中有三个重要属性prefix,context和next,prefix用来存key,context用来存关联的context_node,next是链表下一个节点
prop_area 这个在context_node里引用,属性data是具体key-value的数据库,里面是用 hybrid trie/binary tree(字典树)这种结构存储的,也就是一对多,我给张图就明白了
prop_info 就是具体的key-value了,这个是从prop_area解析出来的
之前有个list_add函数,这个函数是一个模板函数,与Java中的泛型类似,List 和 Args相当于T和T1,这个函数主要作用就是调用T的构造函数,
把list,可变参数args作为参数传进去
1.6 process_kernel_dt
定义在platform/system/core/init/init.cpp
读取DT(设备树)的属性信息,然后通过 property_set 设置系统属性
1.7 property_set
定义在/bionic/libc/bionic/system_properties.cpp
property_set用的地方特别多,作用是设置系统属性,具体就是通过遍历之前的prefixs链表找到对应的context_node,然后通过context_node的_pa属性找到对应key-value节点prop_info,能找到就更新value,找不到就设置新值,
另外就是调用property_changed方法触发trigger,trigger后续讲.rc解析时再详细讲,trigger可以触发一系列活动
1.8 其他属性设置
后续的一些函数或代码都是直接或间接调用 property_set 设置系统属性
二、进行SELinux第二阶段并恢复一些文件安全上下文
2.1 selinux_initialize
定义在platform/system/core/init/init.cpp
第二阶段只是执行 selinux_init_all_handles
2.2 selinux_init_all_handles
定义在platform/system/core/init/init.cpp
这里是创建SELinux的处理函数,selinux_android_file_context_handle和selinux_android_prop_context_handle内部实现差不多,其实就是传递不同的文件路径给selabel_open
2.2 selabel_open
定义在platform/external/selinux/libselinux/src/label.c
首先创建一个selabel_handle结构体,然后根据backend的类型将处理函数映射给initfuncs数组中的值,将参数opts传递过去
这个opts只是包含一个简单的路径,比如 /system/etc/selinux/plat_file_contexts ,而initfuncs负责去解析它
2.3 initfuncs
定义在platform/external/selinux/libselinux/src/label.c
这些数组对应backend的6种可能的值
initfuncs数组中每一项都对应一个init函数,init函数主要作用是解析传进来的文件,这些传进来的文件定义了哪些进程可以访问哪些文件,执行哪些操作
SELinux的内容比较多,由于篇幅就暂时不深入了
可以参考老罗的SEAndroid安全机制框架分析
2.3 selinux_restore_context
定义在 platform/system/core/init/init.cpp
主要就是恢复这些文件的安全上下文,因为这些文件是在SELinux安全机制初始化前创建,所以需要重新恢复下安全性
三、新建epoll并初始化子进程终止信号处理函数
3.1 epoll_create1
EPOLL类似于POLL,是Linux中用来做事件触发的,跟EventBus功能差不多
linux很长的时间都在使用select来做事件触发,它是通过轮询来处理的,轮询的fd数目越多,自然耗时越多,对于大量的描述符处理,EPOLL更有优势
epoll_create1是epoll_create的升级版,可以动态调整epoll实例中文件描述符的个数
EPOLL_CLOEXEC这个参数是为文件描述符添加O_CLOEXEC属性,参考http://blog.csdn.net/gqtcgq/article/details/48767691
3.2 signal_handler_init
定义在platform/system/core/init/signal_handler.cpp
这个函数主要的作用是注册SIGCHLD信号的处理函数
init是一个守护进程,为了防止init的子进程成为僵尸进程(zombie process),
需要init在子进程在结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,
防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)
在linux当中,父进程是通过捕捉SIGCHLD信号来得知子进程运行结束的情况,SIGCHLD信号会在子进程终止的时候发出,了解这些背景后,我们来看看init进程如何处理这个信号
首先,调用socketpair,这个方法会返回一对文件描述符,这样当一端写入时,另一端就能被通知到,
socketpair两端既可以写也可以读,这里只是单向的让s[0]写,s[1]读
然后,新建一个sigaction结构体,sa_handler是信号处理函数,指向SIGCHLD_handler,
SIGCHLD_handler做的事情就是往s[0]里写个"1",这样s1就会收到通知,SA_NOCLDSTOP表示只在子进程终止时处理,
因为子进程在暂停时也会发出SIGCHLD信号
sigaction(SIGCHLD, &act, 0) 这个是建立信号绑定关系,也就是说当监听到SIGCHLD信号时,由act这个sigaction结构体处理
ReapAnyOutstandingChildren 这个后文讲
最后,register_epoll_handler的作用就是signal_read_fd(之前的s[1])收到信号,触发handle_signal
终上所述,signal_handler_init函数的作用就是,接收到SIGCHLD信号时触发handle_signal
3.3 handle_signal
定义在platform/system/core/init/signal_handler.cpp
首先清空signal_read_fd中的数据,然后调用ReapAnyOutstandingChildren,之前在signal_handler_init中调用过一次,
它其实是调用ReapOneProcess
3.4 ReapOneProcess
定义在platform/system/core/init/service.cpp
这是最终的处理函数了,这个函数先用waitpid找出挂掉进程的pid,然后根据pid找到对应Service,最后调用Service的Reap方法清除资源,根据进程对应的类型,决定是否重启机器或重启进程
四、设置其他系统属性并开启系统属性服务
4.1 设置其他系统属性
property_load_boot_defaults,export_oem_lock_status,set_usb_controller这三个函数都是调用property_set设置一些系统属性
4.2 start_property_service
定义在platform/system/core/init/property_service.cpp
之前我们看到通过property_set可以轻松设置系统属性,那干嘛这里还要启动一个属性服务呢?这里其实涉及到一些权限的问题,不是所有进程都可以随意修改任何的系统属性,
Android将属性的设置统一交由init进程管理,其他进程不能直接修改属性,而只能通知init进程来修改,而在这过程中,init进程可以进行权限控制,我们来看看这些是如何实现的
首先创建一个socket并返回文件描述符,然后设置最大并发数为8,其他进程可以通过这个socket通知init进程修改系统属性,
最后注册epoll事件,也就是当监听到property_set_fd改变时调用handle_property_set_fd
4.3 handle_property_set_fd
定义在platform/system/core/init/property_service.cpp
这个函数主要作用是建立socket连接,然后从socket中读取操作信息,根据不同的操作类型,调用handle_property_set做具体的操作
4.4 handle_property_set
定义在platform/system/core/init/property_service.cpp
这就是最终的处理函数,以"ctl."开头的key就做一些Service的Start,Stop,Restart操作,其他的就是调用property_set进行属性设置,
不管是前者还是后者,都要进行SELinux安全性检查,只有该进程有操作权限才能执行相应操作
小结
init进程第二阶段主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux做准备,那么第二阶段就是真正去把这些落实的,下一篇我们将讲解.rc文件的解析
The text was updated successfully, but these errors were encountered: