==本文为读书和博客学习笔记,记录将知识总结自己理解的方式。可能存在错误。文末会给出相关参考链接==
1. Android系统概括
1.1 Android系统架构整体认识
上图可以看出,Android系统大致可以分为5层(这个分层是主观的,并不准确),自顶向下依次介绍。
- 应用层(System APP)
这一层主要就是系统APP和非系统APP(也就是开发者开发的APP,如抖音,微信等)主要是Kotlin + Java
- 框架层(Java API Framework)
供应用层直接调用的Java API,系统APP和普通开发者直接调用的API。如Activity Manager,Package Manager等,具体如下:
应用框架层提供的组件
名称 | 功能描述 |
---|---|
Activity Manager (活动管理器) | 管理各个应用程序生命周期,以及常用的导航回退功能 |
Package Manager (包管理器) | 管理所有安装在 Android 系统中的应用程序 |
Notification Manager (通知管理器) | 使得应用程序可以在状态栏中显示自定义的提示信息 |
Window Manager (窗口管理器) | 管理所有开启的窗口程序 |
Resource Manager (资源管理器) | 提供应用程序使用的各种非代码资源,如本地化字符串、图片、布局文件、颜色文件等 |
更多参考《Android进阶解密》 Page.2 表1-1
- 系统运行库层(Native)
系统运行库分成两部分: 是C/C++程序库和Android运行时库。
C/C++程序库能被Android系统中的不同组件所使用,井通过应用程序框架为开发者提供服务。
名称 | 功能描述 |
---|---|
SQ Lite | 轻型关系型数据库引擎 |
OpenGL ES | 3D绘图函数库 |
更多参考《Android进阶解密》 Page.3 表1-2
主要是由C/C++语言编写,然后Java框架层封装供应用层调用
运行时库又分为核心库和 ART (Android 5.0 系统之后, Dalvik
拟机被 ART 取代)。核心库提供了Java语言核心库的大多数功能,这样开发者可以使用 Java语言来编写Android应用。DVM是JVM再Android中虚拟机,每个DVM运行在一个独立的进程中,避免一个DVM出现错误而影响其他进程的应用层序。那么也就是说一个程序对应至少一个进程,和至少一个DVM虚拟机。
ART和DVM的区别
ART是为了替代DVM而诞生的,DVM采用通过即时编译器(Just In Time ,JIT )转换为机器码,也就是每次应用程序运行都将经过JIT,这会使应用程序的运行效率降低。而在ART中,系统在安装应用时会进行一次预编译(Ahead Of Time,AOT),将字节码预先编译成机器码并存储在本地,这样应用每次运行时就不需要执行编译了,运行效率也大大提高。
- 硬件抽象层 (HAL)
目的保护硬件知识产权,屏蔽硬件实现细节,提供向上层(Native 层)接口。将控制硬件动作放在硬件抽象层中
- Linux 内核层 Linux Kernel)
核心系统服务在这层中,系统的安全性、内存管理、进程管理、网络协议技和驱动模型等都依赖于该内核。
1.2 从开机到init进程启动
整体进程启动概述:
Android系统启动由上到下分别经过:Loader -> Kernel -> Native -> Framework -> App
Loader层: 激活Kernel。
- 启动电源以及系统启动: 当电源按下时引导芯片代码从预定义的地方(固化在 ROM)开始执行。加载引导程序BootLoader 到RAM中,然后执行。
- 引导程序 Bootloader: 引导程序 BootLoader 是在 Android 操作系统开始运行前的 个小程序,它的主要作用是把系统 OS 拉起来并运行。
- Linux 内核启动: 当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设置后,它首先在系统文件中寻找 init.rc 文件,井启动 init 进程。
Kernel层: Android内核空间,到这里才刚刚开始进入Android系统。
Bootloader启动Kernel的swapper进程(pid=0),它是内核首个进程,用于初始化进程管理、内存管理、加载各种驱动。更重要的是启动如下两个重要进程:
- init进程(pid=1):用户进程的鼻祖
- threadd进程(pid=2):内核进程的鼻祖
这层真正大主管是threadd进程,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。
Native层: 进入用户空间。 什么是用户空间与内核空间1 什么是用户空间与内核空间2
这层init进程(pid=1)是大主管。它负责孵化各种系统级服务、守护进程等。最重要的是孵化出Zygote进程:Java进程的鼻祖。
- Media Server进程:负责启动和管理整个C++ framework,包含AudioFlinger,Camera Service等服务。 什么是AudioFlinger?
Framework层: 在Native之上,也是用户空间,主要给app层提供api以及系统服务。
这层大主管是 Zygote进程。它负责注册ZygoteSocket服务端套接字,加载虚拟机,preloadClasses和preloadResouces。
- System Server进程:负责启动和管理整个Java framework,包含AMS、WMS、PMS等服务
App层: 应用程序。
所有的App进程都是由Zygote进程fork生成的。
2. Android系统启动
2.1 init进程
上面提到,在kernel层中通过swapper进程(也称Idle进程 pid=0)创建init进程,同时进程空间由内核态转到用户态,init的入口函数在init.cpp的main函数中,在init进程启动后会先执行init入口函数的main方法。
init.cpp
int main(int argc, char** argv) {
...省略
965 if (is_first_stage) {//1
966 boot_clock::time_point start_time = boot_clock::now();
967
968 // Clear the umask.
969 umask(0);
970
971 // Get the basic filesystem setup we need put together in the initramdisk
972 // on / and then we'll let the rc file figure out the rest.
973 mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
974 mkdir("/dev/pts", 0755);
975 mkdir("/dev/socket", 0755);
976 mount("devpts", "/dev/pts", "devpts", 0, NULL);
977 #define MAKE_STR(x) __STRING(x)
978 mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
979 // Don't expose the raw commandline to unprivileged processes.
980 chmod("/proc/cmdline", 0440);
981 gid_t groups[] = { AID_READPROC };
982 setgroups(arraysize(groups), groups);
983 mount("sysfs", "/sys", "sysfs", 0, NULL);
984 mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
985 mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
986 mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
987 mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
988
...省略
1026 }
//初始化属性服务
1040 property_init();//2
...省略
1068 //创建epoll句柄
1069 epoll_fd = epoll_create1(EPOLL_CLOEXEC);//3
1070 if (epoll_fd == -1) {
1071 PLOG(ERROR) << "epoll_create1 failed";
1072 exit(1);
1073 }
1074 //用于设置子进程信号处理函数
1075 signal_handler_init();//4
1076
//启动属性服务
1079 start_property_service();//5
1080 set_usb_controller();
1081
1082 const BuiltinFunctionMap function_map;
1083 Action::set_function_map(&function_map);
1084
1085 Parser& parser = Parser::GetInstance();
1086 parser.AddSectionParser("service",std::make_unique());
1087 parser.AddSectionParser("on", std::make_unique());
1088 parser.AddSectionParser("import", std::make_unique());
1089 std::string bootscript = GetProperty("ro.boot.init_rc", "");
1090 if (bootscript.empty()) {
//解析init.rc 文件
1091 parser.ParseConfig("/init.rc");//6
1092 parser.set_is_system_etc_init_loaded(
1093 parser.ParseConfig("/system/etc/init"));
1094 parser.set_is_vendor_etc_init_loaded(
1095 parser.ParseConfig("/vendor/etc/init"));
1096 parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
1097 } else {
1098 parser.ParseConfig(bootscript);
1099 parser.set_is_system_etc_init_loaded(true);
1100 parser.set_is_vendor_etc_init_loaded(true);
1101 parser.set_is_odm_etc_init_loaded(true);
1102 }
1103
...省略
1138
1139 while (true) {
1140 // By default, sleep until something happens.
1141 int epoll_timeout_ms = -1;
1142
1143 if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
1144 am.ExecuteOneCommand();
1145 }
1146 if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
1147 restart_processes();//重启死去的进程
1148
1149 // If there's a process that needs restarting, wake up in time for that.
1150 if (process_needs_restart_at != 0) {
1151 epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
1152 if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
1153 }
1154
1155 // If there's more work to do, wake up again immediately.
1156 if (am.HasMoreCommands()) epoll_timeout_ms = 0;
1157 }
1158
1159 epoll_event ev;
1160 int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));//7
1161 if (nr == -1) {
1162 PLOG(ERROR) << "epoll_wait failed";
1163 } else if (nr == 1) {
1164 ((void (*)()) ev.data.ptr)();
1165 }
1166 }
1167
1168 return 0;
1169}
Part.1 创建和挂在启动所需的文件目录
这部分主要有两个函数mount()和mkdir()。
- mkdir()主要是创建基本目录,如mkdir("/dev/pts", 0755);第一个是文件目录,第二个0755是文件属性。
- mount() 主要是将文件系统挂载(安装)到指定目录
- 创建的文件目录有/dev、/porc、/sysfc等。
- 文件系统有mpfs、devpt、proc、sysfs等。
这一步的目的就是创建目录,然后将文件系统挂载到指定的目录
关于详细见Android系统启动——2init进程-第4部分
Part.2 初始化属性服务
首先了解什么是属性服务?
属性服务: 类似Windows中的注册表,它的作用是采用键=值对的方式来记录用户,软件,系统的属性信息,这样在系统或是软件重启后,依然能够根据之前的注册表中的记录,进行一些相应的初始化工作。
先来了解下属性服务有那些特点:
- 在init进程启动中启动属性服务,分配内存空间存储这些属性。
- 属性服务是全局可见的,系统中的所有进程都可以访问和修改,但是在各个进程在修改时,通过访问权限控制,系统中的所有进程想要修改必须要想init进程提出请求,init在接到请求后通过访问权限级别决定是否修改
- 属性服务的属性类别有多种,包括:"ro." , "NET. " "ersist." , "ctl." ctl.为控制属性 其他为普通属性
关于属性服务更多查看:
- Android属性系统简介及使用主要是介绍属性服务的概念、优缺点、类别和实际形式
了解属性服务是什么和有什么用,下面来分析下代码
property_init() 初始化属性服务
property_service.cpp
68void property_init() {
69 if (__system_property_area_init()) { //1
70 LOG(ERROR) << "Failed to initialize property area";
71 exit(1);
72 }
73}
__system_property_area_init()调用此方法初始化属性服务内存区域,将属性服务信息存到这片内存中
根据Android系统启动-Init篇第五部分将,其中用到mmap()方法
理解mmap()之前先了解内存映射文件,如下图:
Linux系统分为用户空间和内核空间。文件一般存在物理内存上,进程需要访问文件同时做到进程共享,那么就需要用户空间进程->访问内核进程->内核进程访问物理内存磁盘文件。但是有个问题就是用户空间进程不能直接使用内核空间进程读取到的文件资源,需要将其copy到自己进程的用户内存空间然后访问,所以虚拟地址空间(虚拟文件系统)诞生,这样不管是用户空间的进程还是内核空间的进程,他们在自己的进程开辟一块进程地址空间,里面的地址映射的到虚拟地址空间。这样无论是内核空间还是用户用户空间就都可以直接访问,减少一次copy到自己进程空间在读取的操作。用户空间和内核空间都有mmap()方法,在用户空间mmap()就是创建进程地址空间的作用,也就是__system_property_area_init()方法中其中一项工作
参考:概述及使用:
Linux内存映射(mmap)
Linux中的mmap映射 [一]
Linux驱动mmap内存映射
Part.3 创建epoll句柄
要理解epoll,首先要简单了解下Linux中 I/O模式分为那些?具体参考这边文章的一些概念介绍LinuxIO模式及select、poll、epoll详解
这里做下自己的概括理解总结:
1. 用户空间与内核空间
现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。
2. 文件描述符fd
Linux中多次出现fd这个概念,文件描述符本质上是一个索引内核中资源地址的一个下标描述,可能指的的Socket,UDP,TCP数据包,文件流等可读写操作的数据。
3. I/O模式
Linux中的I/O模式可能与APP应用层不同,APP应用层发生I/O操作,往往指的是同进程下的线程。而Linux中好像貌似没有线程这个概念,同时发生发生数据读写(也就是要操作fd)时,往往是用户空间和内核空间进程之间的操作,上面也提到,内核进程权限更高,可以访问更高的级别,那么用户空间发起I/O操作最后是交给内核去处理的,不管你的读取硬件设备(如磁盘)和是Socket,都是用户空间进程发起请求给内核空间,然后内核空间去进行真正的read/write,然后返回给用户空间。这样的过程只能给就发生2中情况
- 用户空间发起后是否阻塞 block/non-blocking
- 内核空间是否立马返回数据 synchronous/asynchronous
相互组合形成4中I/O模式
4. I/O 多路复用之select、poll、epoll详解
I/O 多路复用也是I/O模式的一种,但是不同的是,常规的一般是一个读写操作,而I/O多路复用可以实现一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
select
- 固定的监听数量(1024个)
- 轮训方式,每次调用一次select来查看准备就绪的事件 时间复杂度O(n)
- 兼容好,比较底层,各个平台系统都支持
poll
select的升级,不限制最大数量,用链表维护文件事件fd
epoll
linux独有,维护一个队列(好像是双向队列)通过epoll_create(epoll_create1)初始化队列大小,epoll_ctl()处理事件,epoll_wait()返回可处理事件fd,属性服务最后处理的fd就是在这里注册的fd处理的
具体参考:
相关知识点全面讲解:
Linux IO模式及 select、poll、epoll详解
三个方法的优缺点:细说select、poll和epoll之间的区别与优缺点
简单通俗的三个知识点的讲解:select、poll、epoll、同步、异步、阻塞、非阻塞总结
Part.4 信号处理
Linux进程通过相互发送接收消息来实现进程间通信,这些消息被称为"信号"。每个进程在处理它进程发送的信号时,都要注册处理者,处理者被称为信号处理器。
每个进程在处理其他进程发送的signal信号时都需要先注册,当进程的运行状态改变或终止时会产生某种signal信号,init进程是所有用户空间进程的父进程,当其子进程终止时产生SIGCHLD信号,init进程调用信号安装函数sigaction(),传递参数给sigaction结构体,便完成信号处理的过程。
//定义两个文件描述符
34static int signal_write_fd = -1;
35static int signal_read_fd = -1;
36
37static void handle_signal() {
38 // Clear outstanding requests.
39 char buf[32];
40 read(signal_read_fd, buf, sizeof(buf));
41
42 ServiceManager::GetInstance().ReapAnyOutstandingChildren();
43}
44
45static void SIGCHLD_handler(int) {
46 if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
47 PLOG(ERROR) << "write(signal_write_fd) failed";
48 }
49}
50
51void signal_handler_init() {
52 // Create a signalling mechanism for SIGCHLD.
53 int s[2];
//创建读写socket对
54 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
55 PLOG(ERROR) << "socketpair failed";
56 exit(1);
57 }
58
//通过 读写 socket对赋值fd
59 signal_write_fd = s[0];
60 signal_read_fd = s[1];
61
62 // Write to signal_write_fd if we catch SIGCHLD.
//sigaction 处理信号的结构体
63 struct sigaction act;
64 memset(&act, 0, sizeof(act));
65 act.sa_handler = SIGCHLD_handler;
66 act.sa_flags = SA_NOCLDSTOP;
// 调用信号安装函数sigaction(),传递参数给sigaction结构体,便完成信号处理的过程。
67 sigaction(SIGCHLD, &act, 0);
68
69 ServiceManager::GetInstance().ReapAnyOutstandingChildren();
70 //添加到epoll
71 register_epoll_handler(signal_read_fd, handle_signal);
72}
当init进程调用signal_handler_init后,一旦受到子进程终止带来的SIGCHLD消息后,将利用信号处理者SIGCHLD_handler向signal_write_fd写入信息;epoll句柄监听到signal_read_fd收到消息后,将调用handle_signal进行处理。如下图
Part.5 启动属性服务
666void start_property_service() {
667 property_set("ro.property_service.version", "2");
668 //SOCK_NONBLOCK 创建非阻塞Sokcet 返回fd文件描述符
669 property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
670 0666, 0, 0, NULL);
671 if (property_set_fd == -1) {
672 PLOG(ERROR) << "start_property_service socket creation failed";
673 exit(1);
674 }
675 //对property_set_fd监听 8表示最多同时处理8个服务
676 listen(property_set_fd, 8);
677 //handle_property_set_fd 方法处理属性服务
678 register_epoll_handler(property_set_fd, handle_property_set_fd);
679}
总结:
- 创建非阻塞Socket,返回fd,这个Socket作为服务端,一直等待客户端对属性服务操作的事件
- 对fd监听,设置最大同时能处理8客户端
- 放入epoll中, 然后调用handle_property_set_fd方法处理。
Part.6 解析init.rc
init.rc是重要得配置文件,是由Android初始化语言AIL(Android Init Language)编写得脚本,具体格式和含义参考:
主要参考,内容简洁介绍什么是AIL语言:Init篇
详细介绍:init.rc解析
总结: AIL有5种类型语句: Action(动作),Command(命令),Service(服务
),Option(xuan),Improt(导入)
Action:
动作标识了进程执行得各个生命周期和对应做得做得事情。
Service: 是一个程序,对应各一个进程或服务得启动。由fork()创建。
Commond: 执行的命令
Option: 启动Services预设参数
Import: 再init.rc 导入其他rc文件
default: 意味着disabled=false,oneshot=false,critical=false。
各个类型得命令可以相互组合使用常常Action与Commond组合使用,Service和Option组合使用:如下:
init.rc是内容比较多。也不用专门细研究这个文件,知道他是通过解析这个文件启动服务和相应的进程(也包括守护进程),其中 ServiceManager(管理IBinder的服务),Zygote(Java进程的鼻祖),Surfaceflinger(图像展示服务)。
其中Action和Service都有专门得类进项相应的解析:ActionParser和ServiceParser
1) service解析
其中Service->ServiceParser(service.cpp)。 在会调用如:
877void ServiceManager::AddService(std::unique_ptr service) {
878 Service* old_service = FindServiceByName(service->name());
879 if (old_service) {
880 LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
881 return;
882 }
883 services_.emplace_back(std::move(service));//加入链表
884}
通过解析Service语句,最后将解析的数据封装到一个service种然后加入链表。
2) service启动
上面提到init.rc中会启动多个Service。这里以启动Zygote为例子。
init.rc
在init.rc中
622on nonencrypted
623 class_start main
624 class_start late_start
class_start 是一个COMMAND
表示:class_start
: 启动属于同一个class的所有服务;
main表示的Service就是Zygote。对应函数为do_class_start:
builtins.cpp:
129static int do_class_start(const std::vector& args) {
130 /* Starting a class does not start services
131 * which are explicitly disabled. They must
132 * be started individually.
133 */
134 ServiceManager::GetInstance().
135 ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
136 return 0;
137}
ForEachServiceInClass是遍历上一步的Service链表。取出量表中的每个Service
函数中调用service.cpp
762bool Service::StartIfNotDisabled() {
763 if (!(flags_ & SVC_DISABLED)) {
764 return Start();
765 } else {
766 flags_ |= SVC_DISABLED_START;
767 }
768 return true;
769}
主要是判断这个Servcie是否是disabled(表示不随class自动启动,只有根据service名才启动)
接着调用start方法:
service.cpp :
bool Service::Start() {
648
649 pid_t pid = -1;//定义pid
650 if (namespace_flags_) {是否没有启动
651 pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
652 } else {
653 pid = fork();//创建子进程
654 }
655
656 if (pid == 0) {
657 umask(077);
658
//通过execve函数,启动Service子进程
721 if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
722 PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
723 }
724
725 _exit(127);
726 }
757
758 NotifyStateChange("running");
759 return true;
760}
761
关于fork() 函数返回值的含义 linux fork()返回值
通过init.rc中
12行:
12 import /init.${ro.zygote}.rc
其中得知会根据系统加载不同得zygote.rc:
- zygote32
- zygote32_64
- zygote64
- zygote64_32
以zygote64对应文件为init.zygote64.rc为例子:
init.zygote64.rc
1 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
2 class main
3 priority -20
4 user root
关于Zygote进程解析得是init.zygote64.rc这个文件,/system/bin/app_process64这个路径下对用得是app_main.cpp,这样我们得知当service.cpp#start()中调用execve()函数启动子进程,此时就进入app_main.cpp中的mian函数。
187int main(int argc, char* const argv[])
188{
342
343 if (zygote) {//如果是zygote进程,则启动zygote
344 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
345 } else if (className) {
346 runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
347 } else {
348 fprintf(stderr, "Error: no class name or --zygote supplied.\n");
349 app_usage();
350 LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
351 }
352}
至此Zygote进程启动。
Zygote启动流程
init进程启动总结:
( 1 ) 创建和挂载启动所需的文件目录。
( 2 )初始化和启动属性服务。
( 3 )解析 init.rc 配置文件并启动 Zygote 进程。
参考:
《Android进阶解密》
什么是用户空间与内核空间1
什么是用户空间与内核空间2
Android系统启动——2init进程-第4部分
android的surfaceflinger原理讲解
Linux内存映射(mmap)
Linux中的mmap映射 [一]
Linux驱动mmap内存映射
LinuxIO模式及select、poll、epoll详解
Linux IO模式及 select、poll、epoll详解
细说select、poll和epoll之间的区别与优缺点
select、poll、epoll、同步、异步、阻塞、非阻塞总结
linux fork()返回值