Android系统学习(四)------关于init进程及开机自启动
你好!这里是风筝的博客,
欢迎和我一起交流。
初入Android,本篇文章不过是拾人牙慧,见笑了~
init进程,它是内核启动的第一个用户级进程,进程号为1。它通过解析init.rc脚本来构建出系统的初始形态,它的生命周期贯穿整个linux 内核运行的始终。
核心代码在system/core/init/init.cpp
程序解读不再累述,可以看这篇文章:Android Init进程源码分析
大概会做这些事情:
启动守护进程ueventd
设置环境变量
创建一些基本目录,并挂载("/dev", “/dev/pts”,"/dev/pts",,,,)
把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
启动kernel log(创建节点/dev/kmsg)
初始化Android的属性系统
解析DT和命令行中的kernel启动参数(dt优先级大于命令行)
设置系统属性
调用selinux_initialize函数启动SELinux
创建epoll,初始化signal(子进程终止信号处理)
加载默认属性,启动属性服务(build.prop等文件)
解析init.rc文件
把rc文件中action加入执行队列中,稍后会启动trigger为"early-init"等的action
最后init进程会进入一个死循环,在这个无限循环中,init进程会做以下事:
1.执行action_queue列表的action
2.检查系统中是否有进程需要重启
3.处理系统属性变化事件
4.处理子进程的终止(signal方式,会产生SIGCHLD信号发送给init进程),回收僵尸进程。
其中,在 Android中使用启动脚本init.rc,可以在系统的初始化过程中进行一些简单的初始化操作。这个脚本被直接安装到目标系统的根文件系统中,被 init可执行程序解析。 init.rc是在init启动后被执行的启动脚本
init.rc的使用方法,可以参考说明文件system/core/init/readme.txt。
init.rc语法基本由四个部分组成:
Actions、Commands、Services、Options
#这里on 就是一个Actions#write 、restorecon 、start 都是它的Commandson early-init # Set init and its forked children's oom_adj. write /proc/1/oom_score_adj -1000 # Set the security context of /adb_keys if present. restorecon /adb_keys start ueventd#这里service [ ]*就是一个Services#class、critical、seclabel 都是它的Optionsservice healthd /sbin/healthd class core critical seclabel u:r:healthd:s0
每个Actions 和 Services都表示一个新的段落section的开始,所有的commands和options 都是归属于上方最近的一个段落。
而且每个rc文件里又可以import 导入其他的init.xx.rc文件。
这里试着尝试通过配置init.rc文件实现开机自启动脚本:
脚本实现每次开机后台抓取dmesg写入文本。
修改system/core/rootdir/init.rc:
diff --git a/rootdir/init.rc b/rootdir/init.rc++service kmsg_log /system/bin/kmsg.sh+ class late_start+ seclabel u:r:kmsg_log:s0+ #实测,seclabel 这句很重要,必加
修改device/rockchip/rk3399/device.mk:
拷贝脚本到系统目录中:PRODUCT_COPY_FILES 就是将kmsg.sh文件从源码树中拷贝到镜像文件系统的system/bin路径下
diff --git a/device.mk b/device.mk++PRODUCT_COPY_FILES += \+ device/rockchip/rk3399/kmsg.sh:system/bin/kmsg.sh
编写device/rockchip/rk3399/kmsg.sh:
#!/system/bin/shcount=3for i in `seq $(($count+1))`do #echo "i is $i" if [ ! -f "/data/logs/kmsg_$i.log" ]; then break fidoneif [ $i -eq $(($count+1)) ]; then rm /data/logs/kmsg_1.log i=$count for j in `seq $(($count-1))` do #echo "/data/log/kmsg_$(($j+1)).log /data/log/kmsg_$j.log" mv /data/logs/kmsg_$(($j+1)).log /data/logs/kmsg_$j.log donefi#save date, otherwise the date of file will be flushed.date > /data/logs/kmsg_$i.logdmesg >> /data/logs/kmsg_$i.logcat /proc/kmsg >> /data/logs/kmsg_$i.log
脚本参考:[RK3399][Android7.1] 调试笔记 — 开机后台抓取kmsg log
不过很遗憾,实践时会发现不会成功,有错误:
init: Service kmsg_log does not have a SELinux domain defined.
这里就涉及一个很重要的东西,就是SELinux!
这是一种系统安全机制,如果没有在SELinux里给予service对应的权限规则,那么系统是不会让service正常启动的!
所以我们需要定义一个SELinux domain,添加对应的权限。
添加device/rockchip/common/sepolicy/kmsg-log.te文件:
前面几句是SELinux 域的初始模板,可以根据该可执行文件执行的具体操作为该模板添加规则
type kmsg_log, domain;type kmsg_log_exec, exec_type, file_type;type kmsg_log-cnf, file_type;init_daemon_domain(kmsg_log)allow kmsg_log kmsg_log-cnf:file read;allow kmsg_log rootfs:lnk_file getattr;allow kmsg_log kernel:system syslog_read;allow kmsg_log kmsg_device:chr_file { read open };allow kmsg_log kmsg_log-cnf:file { getattr open };
修改device/rockchip/common/sepolicy/file_contexts文件:
diff --git a/sepolicy/file_contexts b//sepolicy/file_contexts++/system/bin/kmsg_log.sh u:object_r:kmsg_log_exec:s0
这里就不用修改Android.mk添加te文件了,因为在
system/sepolicysystem/sepolicy/Android.mk文件里条注释:
# Builds paths for all policy files found in BOARD_SEPOLICY_DIRS and the LOCAL_PATH.
就是说会自动去编译BOARD_SEPOLICY_DIRS路径下的te文件,我们搜索可知,BOARD_SEPOLICY_DIRS就是device/rockchip/common/sepolicy/
关于SELinux domain的编写,
参考:Android init.rc启动服务
深入理解SELinux SEAndroid(第一部分)
SEAndroid安全机制简要介绍和学习计划
最后把boot.img和system.img烧写进系统,就可以看到脚本开机自启了。
init.c 、init.rc init.xx.rc 等最终会编译到ramdisk.img(根文件系统)中,和kernel一起打包成boot.img。android启动后每次都会从boot.img中解压出init.c等文件到内存,所以要修改必须修改替换boot.img
另外:通过getprop | grep init.svc 可查看所有的service运行状态。状态总共分为:running, stopped, restarting
如果文件没有执行权限的话,可以在system/coce/libcutils/fs_config.c文件的android_files[]数组里添加权限
最后给出一些参数
Triggers说明--------------------------------------------------------------------------------early-init在初始化早期阶段触发late-init在初始化晚期阶段触发init在初始化阶段触发late-init在初始化晚期阶段触发boot/charger当系统启动/充电时触发property:=当属性值满足条件时触发 如:on property:ro.debuggable=1fs挂载mtd分区时触发boot基本网络的初始化,内存管理等时触发post-fs改变系统目录的访问权限时触发device-added-设备节点添加时触发device-removed-设备节点删除时触发service-exited- 在特定服务(service)退出时触发Command说明--------------------------------------------------------------------------------import 导入init.XX.rc、xxx.conf等文件chmod 更改文件访问权限chown 更改文件所有者和组chdir 变更工作目录chroot 改变进程根目录insmod 加载XX.ko驱动模块start 如果服务尚未运行,则启动服务stop 如果当前在运行则停止服务运行class_start 启动指定类的所有服务class_stop 停止指定类的所有服务class_reset 重启指定类的所有服务setprop 设置系统属性< Name >至export 设置全局环境变量,这个变量值可以被所有进程访问(全局的,一直存在)mkdir [mode] [owner] [group]mkdir [mode] [owner] [group]创建目录,后面项缺省值为 mode,owner,group: 0755 root roottrigger 触发一个事件。用于将action从另一个action 队列exec [ ]* 执行指定的Program,并可以带有执行参数,会阻塞initifup 启动某个网络接口,使其为up状态,通过netcfg可以查看,ifup eth0 等价于 netcfg eth0 up 功能一样hostname 设置设备的主机名domainname 设置网络域名localdomainmount [ ]*把device挂接到dir目录下面,文件系统类型为typewrite [ ]*打开一个文件,利用write命令写入一个或多个字符串loglevel 设置log级别symlink 创建连接到的符号链接Option说明--------------------------------------------------------------------------------class 服务属于class_name这个类。缺省值service属于 “default” 类。同一个class下面的服务可以一起启动或停止disabled 服务不会随class自动启动,要用start server_name 或 property_set("ctl.start", server_name);才能启动oneshot当服务退出后,不会再重新启动,如果没有加这个option,则服务默认退出后又会重新重启user 声明服务的用户名,缺省值应该为root用户group [ ]*声明服务所属组名,可以一次声明属于多个组onrestart + command服务重启的时,会执行onrestart后面的commandsetenv 在当前服务进程中设置环境变量name的值为value,环境变量仅在本进程内生效critical声明为设备的循环服务。如果服务在四分钟内退出了四次,则设备会进入recovery模式socket [ [ ] ] 创建名为/dev/socket/的unix domain socket ,并把它的句柄fd传给本服务进程seclabel 执行服务之前改变安全级别
参考:
android系统启动流程之init.rc详细分析笔记
init.rc内容解析可以看看这个:Android系统init进程启动及init.rc全解析
更多相关文章
- NPM 和webpack 的基础使用
- 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程
- 读取android手机流量信息
- 浅析android通过jni控制service服务程序的简易流程
- android 使用html5作布局文件: webview跟javascript交互
- Android(安卓)Wifi模块分析(三)
- Android(安卓)多媒体扫描过程(Android(安卓)Media Scanner Proces
- android“设置”里的版本号
- Android开发环境搭建