65.9K
CodeProject 正在变化。 阅读更多。
Home

Brainless Android - 简介

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (9投票s)

2014 年 7 月 29 日

CPOL

13分钟阅读

viewsIcon

23024

对安卓的初步介绍。

简介

Android 是一个面向移动平台的软件栈。这意味着 Android 为移动设备提供了一整套完整的软件:操作系统、中间件和关键的移动应用程序。近几年来,它获得了巨大的普及。Android 从零开始构建,旨在让开发者能够创建跨平台的移动应用程序,充分利用手机的各项功能,而不必过多地担心硬件。由于其开放的性质,开发者可以按需使用,即开发者可以只专注于构建高级应用程序或游戏等。如果他们愿意,也可以使用原生级别以获得更多功能。Android 构建在 Linux 内核之上,这使得操作系统能够获得 Linux 的所有优点,以及大量已经熟悉 Linux 的用户/开发者基础。它还支持使用 Java 语言进行开发,这非常棒。作为开源项目,它能够被移植到新平台,获取新硬件的驱动程序。它提供对 Web 的访问,因此开发者现在可以利用 Web 的强大功能,甚至可以利用手机的数据来构建自定义应用程序。它还提供了一个良好且开放的开发工具链。它由 开放手机联盟管理,该联盟拥有许多主要参与者作为成员。由于其开放的性质,现在有许多其他设备正在采用 Android。

历史

注意:我已尽力根据我的知识使历史记录尽可能真实。如果发现任何错误,请告诉我。

我将仅为文章的完整性而介绍历史。有一篇不错的 文章,它很好地描绘了该操作系统的崛起。它最初是由 Andy Rubin 在离开他之前的公司 Danger Inc. 后创办的,并使用了他之前拥有的域名 Android.com 作为他新公司 Android Inc. 的域名。后来,他与 Rich Miner、Nick Sears 和 Chris White 一起开发了一个以用户为中心操作系统。他们的目标是开发一个高度可定制、轻量级、响应迅速且免费的开源操作系统。尽管该公司在同一年就耗尽了资金,但它得到了好友 Steve Perlman 赠予的 10000 美元,以继续他的梦想。Android 的真正转折点出现在 2005 年 Google 收购了整个公司。有了这样一家公司的推动,Android 势不可挡。从那时起,定制 Linux 内核的真正工作开始了。2007 年 11 月 7 日,开放手机联盟成立,其中包括了移动电信领域的其他世界领先者,旨在为 Android 生态系统定义开放标准和免费软件。五天后的 2007 年 11 月 12 日,第一版 Android SDK 发布。这就是 Android 设备新时代的开始。

Android 构建块

Android 架构图清晰地展示了构成构建块的不同元素。

Android Architecture Diagram

来源 Android.com

现在让我们简要讨论 Android 中的不同层。

  1. 内核 - Android 应该可以在某些设备上运行。最可移植且真正可以扩展到任何设备的操作系统是 Linux 操作系统。通过将 Linux 作为基础,我们可以获得大量有经验的人员来处理 Linux 操作系统。它易于移植到新硬件,并且非常健壮。此外,我们还可以获得编写新设备驱动程序的人员。所以这是一个正确的决定。这是 Android 生态系统的核心。它充当硬件抽象层 (HAL)。
  2. - 在 HAL 之上是 C/C++ 库,它们利用 HAL 的服务并提供更通用的以用户为中心的服务。这些是用户可以用来构建应用程序的库。它包含了 OpenGL、Sqlite 数据库、FreeType 字体渲染引擎、SSL、libc 等。
  3. Android Runtime - 这包括 Dalvik 虚拟机和 Java 核心库。Dalvik 是一个虚拟机,它接收 Java 类文件并将其转换为 Dex 文件,Dex 文件比 JVM 更紧凑且资源消耗更少。由于 Android 运行在内存和电池资源有限的移动设备上,因此需要 Dalvik VM。那么为什么要转换类文件而不是直接从源代码生成 dex 文件呢?嗯,Java 编译器已经出色地完成了生成类文件的任务,所以通过使用它们,我们无需重写整个编译器框架。其次,不仅是 Java,其他语言也生成类文件,例如 JPython、Scala。因此,通过仅使用类文件,我们无需为当前或未来的其他语言编写或修改编译器。Android Runtime 将 Android 库的功能导出给用户。所以用户可以用 Java 语言编写一切。
  4. 应用程序框架 - 该框架是为开发者提供的高级框架。它围绕 Android Runtime 和库构建,为用户提供了一个功能丰富的 Android 开发环境。主要的 Android API 包括 UI(用户界面)、电话、资源、位置、Content Providers(数据)和包管理器。框架中最重要的组件是 Activity Manager,它管理应用程序的生命周期以及用户导航的通用“后堆栈”。这个层使 Android 充满活力。这一层帮助许多开发者利用操作系统的强大功能并提供丰富的用户体验。
  5. 应用程序 - 最后,最顶层是应用程序层。您的大部分代码将驻留在此处,以及内置应用程序,如电话和网络浏览器。

Android 独特而强大的品质之一是所有应用程序都具有相同的资源访问权限。我的意思是,Google 编写的应用程序必须通过您使用的相同公共 API。Google 能做到的,您也能做到。如果您愿意,甚至可以告诉 Android 让您的应用程序替换标准应用程序。

接下来,我们将深入探讨操作系统中各个重要组件。

Android 操作系统

本质上,Android 是 Linux 的一个变种。这意味着它可以做任何常规的 Linux 操作系统可以做的事情。我们只需要处理移动设备有限的资源约束。因此,与任何常规操作系统一样,它也有一个启动序列。让我们看看启动过程。

通电

当我们打开系统电源时,CPU 将处于未初始化的状态。CPU 将调用复位向量开始初始化。内部时钟未设置,唯一的可用内存是内部 RAM。当电源稳定后,执行将从 Boot ROM 代码开始。这是一小段代码,硬编码在 CPU ASIC 中。CPU 将从 ROM 中的已知位置读取代码并开始执行。

引导加载程序

ROM 代码将在非常基础的级别执行一些时钟、内存和外围设备配置。然后它将加载并执行引导加载程序。从技术上讲,引导加载程序不是 Android 生态系统的一部分。它只是用于启动 Android 的外部系统。通常,这是设备制造商的责任,因为他们对设备的内部结构更为了解。他们可以使用任何流行的引导加载程序,例如 redboot、uboot、qi bootloader。他们也可以选择自己编写引导加载程序。只要能正常工作,就没有什么好担心的。

OEM 和运营商会在引导加载程序中设置锁定和限制。如果您想进一步了解,可以查看 此位置 的引导加载程序。这里有两个重要的文件:

  • init.S - 初始化堆栈,清零 BSS 段,调用 main.c 中的 _main()
  • main.c - 初始化硬件(时钟、主板、键盘、控制台),创建 Linux 标签。

然后,它将在“启动设备”上搜索操作系统的启动映像。引导加载程序设置启动参数并将控制权传递给内核映像。然后内核被解压缩并加载到闪存中。然后它将启动 USB、SD 卡、音频等外围设备。

引导加载程序还可以检测某些按键,这些按键可用于加载“恢复”内核,或将手机置于开发人员可以执行开发任务的模式,例如重写闪存映像,以及直接下载和执行备用内核映像等。

内核

Linux 内核在 Android 上的启动方式与其他系统类似。它将设置运行系统所需的一切。以下是完成的操作:

  • 核心内核初始化
    • 内存和 I/O 区域已初始化
    • 中断已启动,进程表已初始化
  • 驱动程序初始化
  • 内核守护进程(线程)已启动
  • 根文件系统已挂载
  • 第一个用户空间进程已启动
    • 通常是 /init(请注意,其他 Linux 系统通常启动 /sbin/init)

Init

Android 启动序列的一个关键组件是名为“init”的程序,它是一个专门用于初始化 Android 系统元素的程序。与其他 Linux 桌面或嵌入式发行版不同,Android 使用自己的一组初始化程序。Android 的“init”程序处理两个文件,分别称为“init.rc”和“init.<machine_name>.rc”。在这里,“machine_name”会附加到文件名中。大多数情况下,这将是一个代号。rc 文件将以一种称为“Init Language”的语言保存指令。

  • init.rc - 包含适用于任何 Android 系统和硬件的通用初始化。
  • init.<machine_name>.rc - 包含特定于该硬件的指令。

因此,如果有人想拥有自己的服务,以便在启动时启动,则需要将其放入这些文件之一。通常,这对普通用户来说不是必需的。但如果您想知道如何操作,可以这样做。假设我们有以下代码希望在启动时执行。它将每 3 秒唤醒一次并将经过的时间记录到系统日志中。

// BrainlessService.c
#include < utils/log.h >
#include < unistd.h >

int main(int argc, char **argv)
{
    LOGI("Brainless Service started");
    int elapsed = 0;
    while(1)
    {
         sleep(3);
         elapsed += 3;
         LOGI("Service elapsed time is %d", elapsed);
    }
    return 1;
}

现在假设我们设法将其编译成可执行文件 '/system/bin/brainlessservice'。完成后,我们需要在 init.rc 文件中添加以下内容:

service brainlessservice /system/bin/brainlessservice
user brainlessservice
group brainlessservice
oneshot

现在,这将在“init”阶段启动“brainlessservice”服务。

以下是 Init 执行的活动列表:

  • Init 进程启动 rild(无线电接口链路守护进程)和 vold(用于媒体卷的卷守护进程,如文件系统 - 与音频音量无关)等守护进程。
  • Dalvik 守护进程(Zygote)已启动
  • “system_server”启动,并初始化多个核心服务。System Server 是 Android 系统的核心,在 Dalvik 初始化并运行后立即启动。其他系统服务将在 System Server 进程的上下文中运行。首先,服务器将加载一个名为 android_servers 的本地库,该库提供到本地功能的接口。初始化分两步完成:
    • 加载库以初始化到本地服务的接口,
    • 在 SystemServer.java 中的 ServerThread::run() 中初始化基于 Java 的核心服务
  • “Activity Manager”启动核心应用程序(这些应用程序本身也是 Dalvik 应用程序)[例如 com.android.phone - 电话应用程序或 android.process.acore - 主屏幕(桌面)]
  • “init”还启动了 adb、mediaserver、dbus-daemon 等其他进程。

Zygote(Dalvik VM 启动器)

Zygote 是一个守护进程,其唯一目的是启动应用程序。它由 app_process 启动。以下是启动这种特殊进程的序列:

让我们看看 Zygote 启动的 init.rc 文件:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd

当 app_process 启动 Zygote 时,它会创建第一个“Dalvik VM”并调用 Zygote 的 main() 方法。Zygote 启动后,它会预加载所有必要的 Java 类和资源,启动 System Server(如上一节所述),并打开一个套接字 '/dev/socket/zygote' 来监听启动应用程序的请求。System Server 是一个与其父进程完全分离的进程。

新应用程序如何启动?

Zygote 通过 '/dev/socket/zygote' 接收启动应用程序的请求。然后 Zygote 调用 fork(),在另一个内存空间中创建一个它的克隆。现在,在主 Zygote 中,所有初始化(加载 Dalvik 和加载 Java 类)都已完成,因此这个克隆是高效且快速的。这使得创建 VM 和加载资源的过程非常高效。实际上,在 fork 或 clone 调用期间,Linux 实现了写时复制(Copy on Write)原则。所以实际上没有不同的内存被复制到另一个内存空间。内存是共享的,并标记为写时复制。只有当有写入内存的请求时,才会复制内存。所以这个过程很快。您现在可以看到使用 Linux 内核的好处了吗?我们默认获得了所有这些实现。

 

Activity Manager

Zygote 将在请求时启动一个应用程序。例如,当我们点击应用程序时,它会在 onClick() 事件中启动它。此时,应用程序启动器通过一个名为“Binder”的远程过程调用 (RPC) 机制联系 Activity Manager,并调用 startActivity()。收到 startActivity() 请求后,Activity Manager 请求 Zygote fork 它自己并启动新的 VM,就像我们在上一节中讨论的那样。这是通过 startViaZygote() 方法完成的,该方法将打开与 Zygote 套接字 (/dev/socket/zygote) 的连接。

除了这些,Activity Manager 还负责其他任务,如广播意图、显示“应用程序无响应”消息框。

要了解更多关于 Activity Manager 的信息,您可以参考 代码

现在我们简要了解了 Android 的内部机制,我们将讨论 Android 的一个重要方面。那就是 Dalvik VM。

Dalvik VM

Dalvik 是 Android 的 Java 虚拟机。它允许 Android 运行从基于 Java 的应用程序生成的字节码,并将 Android 组件和 JNI 钩子导出到本地库和其余的本地用户空间。Dalvik 是为嵌入式系统设计的。这些系统具有少量 RAM、慢速 CPU,并运行没有交换空间且电池有限的操作系统。如前所述,Dalvik 将“class”文件转换为“dex”文件并执行。这些“dex”文件的大小可以达到相应 jar 文件的一半。

Dalvik VM 的内部结构与 JVM 也有所不同。JVM 是一个基于堆栈的 VM,而 Dalvik 是一个基于寄存器的 VM。Dalvik 通过一些技术(如最小重复、隐式类型和隐式标签)节省了内存占用。Dalvik VM 依赖 Linux 内核提供底层功能,如线程和低级内存管理。从 Android 2.2 开始,Dalvik 还拥有即时 (Just in Time) 编译器。

Dalvik VM 的内部结构是一个更复杂的话题。我们将在以后的文章中讨论。

结论

在本文中,我们深入探讨了 Android 内部流程的许多细节。我将在我认为可以增强文章内容时尽力改进这篇文章。如果您有任何建议,请告诉我。

© . All rights reserved.