1.操作系统简介
操作系统(Operating System, 简称OS)是管理和控制计算机硬件与软件资源的计算机程序。
它是一种由引导程序(bootloader)启动并管理计算机中所有程序生命周期的系统程序。
任何其他软件都必须在操作系统的支持下才能运行,操作系统能有效组织和管理系统中的各种软、硬件资源,合理组织计算机系统的工作流程并控制程序的执行,为用户提供一个良好的操作环境。
目前比较为人所知的操作系统有Microsoft的Windows系统、Apple的Mac及以Linux为内核的各种Linux发行版(Centos/Ubuntu等)。
现代计算机系统由一个或多个处理器、主存、打印机、键盘、鼠标、显示器、网络接口及各种输入\输出设备构成。
操作系统这个术语听上去稀松平常,并不给人任何兴奋的感觉,甚至有点俗气。
原因在于中文的“操作”这个词:提到操作员,通常让人想起操作车床、磨床和起重机的穿着油腻工作服的工人,这自然让人兴奋不起来。
将英文的Operating翻译为中文的“操作”,是因为翻译的人没有理解英文Operating Systems(OS,操作系统)这个名字所蕴涵的精髓。
那么英文的Operating Systems意味着什么呢?各位见过手术过程吗?在手术室里,主刀大夫称为Operating Surgeon。在整个手术过程中,主刀大夫具有至高无上的权威:他说要打麻药,麻醉师便要赶紧打麻药;他说需要手术钳,助理大夫就赶忙递给他手术钳;他说需要止血,护士就要忙不迭地拿止血药棉来止血。整个手术最关键的部分,切开皮肤、拿掉器官、安装移植器官等均由主刀大夫完成。当然,主刀大夫有时候也会将某些任务,如缝合创口,交给助理大夫或护士来做,但整个手术的过程皆由其主控。
一句话,Operating Surgeon就是掌控整个手术过程、具有精湛技术和敏锐判断力的医师。
引申至非医学领域,Operating Person意思是操刀手,就是掌控事情的人。再将Person这个词换成System,则Operating Systems指的就是掌控局势的一种系统。也就是说计算机里面的一切事情均由Operating Systems来掌控。
那么,我们现在面临两个问题:
第一个问题是,操作系统到底是什么东西?
第二个问题是,操作系统到底操控什么事情?
我们先回答第一个问题。既然操作系统是掌控计算机局势的一个系统,自然很重要。但这个说法并不能帮助读者理解操作系统,也无法形成有形的概念。
如果我们换个说法:操作系统是介于计算机和应用软件之间的一个软件系统,则概念就具体多了。
从这个定义出发,我们知道操作系统的上面和下面都有别的对象存在:下面是硬件平台,上面是应用软件,现在回答第二个问题。我们知道操作系统代表的是掌控事情的系统,掌控什么事情呢?当然是计算机上或计算机里发生的一切事情。最原始的计算机并没有操作系统,而是直接由人来掌控事情,即所谓的单一控制终端、单一操作员模式。但是随着计算机复杂性的增长,人已经不能胜任直接掌控计算机了。于是我们编写出操作系统这个“软件”来掌控计算机,将人类从日益复杂的掌控任务中解脱出来。
这个掌控有着多层深远的意义。首先,由于计算机的功能和复杂性不断发生变化(趋向更加复杂),操作系统所掌控的事情也就越来越多,越来越复杂。同时,操作系统本身能够使用的资源也不断增多(如内存容量)。
这是早期驱动操作系统不断改善的根本原因。其次,既然操作系统是专门掌控计算机的,那么计算机上发生的所有事情自然需要操作系统的知晓和许可,未经操作系统同意的任何事情均视为非法的,也就是病毒和入侵攻击所试图运作的事情。作为操作系统的设计人员,我们当然要确保计算机不发生任何我们不知情或不同意的事情。但是人的能力是有限的,人的思维也是有缺陷的,我们设计出的系统自然不会十全十美,也会有缺陷,这就给了攻击者可乘之机。
操作系统设计人员和攻击者之间的博弈是当前驱动操作系统改善的一个重要动力。再次,掌控事情的水平有高低之分,有效率不同之分。就像手术大夫之间也有水平高低之分。为了更好地掌控事情,同时也为了更好地满足人类永不知足的各种越来越苛刻的要求,操作系统自然需要不断改善。这种改善在过去、现在和将来都会继续下去的。
最后,我们可以给操作系统下定义了:
操作系统是一个软件系统;
操作系统使计算机变得好用(将人类从繁琐、复杂的对机器掌控的任务中解脱);
操作系统使计算机运作变得有序(操作系统掌控计算机上所有的事情)。
总结起来就是:操作系统是掌控计算机上所有事情的软件系统。
作用:它可以帮我们管理计算机的各种资源,协助我们完成各种复杂繁琐的任务。
操作系统三要素:
虚拟化(virtualization)
操作系统将物理(physical)资源(如处理器、内存或磁盘)转换为更通用、更强大且更易于使用的虚拟形式。因此,我们有时将操作系统称为虚拟机(virtual machine)。当然,为了让用户可以告诉操作系统做什么,从而利用虚拟机的功能(如运行程序、分配内存或访问文件),操作系统还提供了一些接口(API),供你调用。实际上,典型的操作系统会提供几百个系统调用(system call),让应用程序调用。由于操作系统提供这些调用来运行程序、访问内存和设备,并进行其他相关操作,我们有时也会说操作系统为应用程序提供了一个标准库(standard library)。
最后,因为虚拟化让许多程序运行(从而共享CPU),让许多程序可以同时访问自己的指令和数据(从而共享内存),让许多程序访问设备(从而共享磁盘等),所以操作系统有时被称为资源管理器(resource manager)。每个CPU、内存和磁盘都是系统的资源(resource),因此操作系统扮演的主要角色就是管理(manage)这些资源,以做到高效或公平,或者实际上考虑其他许多可能的目标。
- 进程,它是虚拟化的CPU;
- 地址空间,它是虚拟化的内存。
并发(concurrency)
- 持久性(persistence)
操作系统实际上做了什么:它取得CPU、内存或磁盘等物理资源(resources),并对它们进行虚拟化(virtualize)。它处理与并发(concurrency)有关的麻烦且棘手的问题。它持久地(persistently)存储文件,从而使它们长期安全
操作系统也是一种软件,但是操作系统是一种非常复杂的软件。操作系统提供了几种抽象模型:
- 文件:对 I/O 设备的抽象
- 虚拟内存:对程序存储器的抽象
- 进程:对一个正在运行程序的抽象
- 虚拟机:对整个操作系统的抽象
以现代标准而言,一个标准PC的操作系统应该提供以下功能
用户界面(User interface)
普通用户操作电脑是需要用户界面的,没有用户界面的电脑对于普通用户来说就是灾难。你能想象家里的老人或者上了年纪的人用电脑却没有鼠标的场景吗?你能想象使用黑框框来做 PPT 吗?你能想象使用黑框框来浏览网页吗?
专业的 IT 工作者有时候会使用黑框框纯粹是工作需要,在有些场景下,黑框框比用户界面更有效率一些。而类似于服务器场景的开发工作基本上是没有用户界面的,当然一直强调的是专业场景,这个世界上能流畅使用黑框框的人占总人口的比例太少太少。
用户界面这项伟大的发明诞生于施乐公司,经乔布斯和比尔盖茨商业化运作后得以让世人发现它的伟大之处。用户界面的发明就相当于简体汉字的出现一般,简体汉字的推行让中国的文盲率大幅降低,而用户界面的发明则大幅降低了计算机的使用难度,所以你很难想象现代的操作系统没有用户界面。
进程管理(Processing management)
先来解释一下什么是进程。进程是计算机进行资源管理以及调度的基本单位,是程序的执行实体。这种说法比较正统,或者你可以将一个进程看成是计算机正在进行的一项任务。计算机里面有很多各式各样功能的进程,那么多进程如何比较好的运行是个深奥的学问。
你可以想象一下:你打开了微信、word 文档、音乐软件,你想一边跟同事进行交流文档的内容应该如何写,一边在 word 文档敲下你的构思,然后在这个过程中你还听着音乐。在这个过程中,计算机需要将微信的网络保持连接,持续收发微信的信息,随时保存你的 word 文档到磁盘上,解码音乐流并播放出来。这一系列程序运转如何能让你产生一个假象:你以为它们是在同时运行的,但其实它们在同一时刻只有一个在运行。这就是进程管理和调度。
内存管理(Memory management)
内存是计算机很重要的一个资源,因为程序只有被加载到内存中才可以运行,此外 CPU 所需要的指令与数据也都是来自内存的。内存的并不是无限制的,它受限于硬件和寻址位数。
但是现代操作系统会让每一个进程都觉得自己在独占整个内存,这就是虚拟内存技术。值得注意的是,这里的虚拟内存与 swap 这种虚拟内存是不一样的,虽然两个都是称为虚拟内存,但是完全是两个不同方向的技术。
进程的运行需要分配内存,内存分配的快慢都与内存管理方式有着巨大的影响。两个不同进程对应的内存区域是不能相互访问的,操作系统必须得提供这样的保证,否则很容易出问题,比如:运行着的 dota 游戏如果可以被另外一个进程访问它的内存区域的话,那就可以直接将内存区域中的某个数值进行修改,比如将游戏中的玩家生命值变为无限,这样对手怎么打都打不死自己的英雄。例如前段时间火热的吃鸡游戏,外挂软件可以让角色在决赛圈外进行锁血,这个就是游戏内存被修改的最好示例。当然这是通过比较专业的手段来绕过操作系统的限制,这也从另外一个方面来说明,其实现在的操作系统安全性也是有很大提升空间的。
进程退出销毁时,内存的回收也是很重要的,否则很容易就会产生内存溢出,占着茅坑不拉屎,导致其他的进程都憋死。
文件系统(File system)
文件系统与用户的距离很近,每个人平常在使用计算机的时候或多或少都会留下一些数据,而这些数据通常会保留在磁盘里面。磁盘如果不进行格式化的话,普通人是没法使用的。磁盘里面其实就是一些布满磁性物质的盘片,在计算机的世界里数据是 0 和 1 组成的,那对应的在磁盘里面就是磁性的正负极,也就是说计算机的一个文本数据要保存到磁盘中,那就需要将文本数据的电气化信号 0 和 1 通过磁盘翻译为磁性正负极并保存起来。
磁盘格式化的过程就是将文件系统架设到磁盘上,这样可以更好的管理磁盘的数据。你可以将磁盘未格式化之前的数据看做是一堆杂乱无章散落在地上的书,而文件系统就是一个有编排顺序的书架,格式化的过程就是将这堆书一本本按编排顺序放到书架上。这个比喻不太恰当,因为格化式操作通常来说会清掉数据,就相当于将书里面的字都清掉了,放到书架上的书里面都是空白页,所以格式化的时候请谨慎。
网络通信(Networking)
我们常见的网络通信场景有:微信聊天、浏览网页、玩网络游戏等,可以说现在的操作系统如果不能上网感觉就没了灵魂。网络通信是个很复杂的过程,连很多专业的程序猿都说不清楚当他上网时网页是如何显示出来的,更别说整个网络的拓扑结构了。
整个网络通信其实是一套约定好的通信协议,很多人第一次听说协议时觉得很高级,其实没什么高级的,简单的说,类似于我们军训时当教官喊立正我们必须得做出相应动作一样,一个指令对应一个动作。由于网络太复杂了,某位哲人说过如果某样东西太复杂可以通过分层来解决,于是网络分了 5 层。有人也许会说是 7 层,那是 OSI 标准,在实际应用中一般都是 5 层。
设备管理
计算机上有很多设备,比如CPU、内存、网卡、声卡、显卡、硬盘等。那什么是设备管理?
在计算机中除了 CPU 和内存,对于其他一切输入输出设备的管理统称为设备管理。
计算机中的设备分为输入和输出设备。以 CPU 为中心,凡是向 CPU 输送数据的设备统称为输入设备,例如鼠标、键盘、摄像头等;同样以 CPU 为中心,凡是从 CPU 获取数据的设备统称为输出设备,如显示器等。有些设备既是输入设备也是输入设备,比如网卡等。
一个比较常见的场景是当我们将 U盘插进电脑的 USB 插孔时,电脑能实时识别出 U 盘设备,那计算机为啥能实时识别出这些设备呢?之后会有章节讨论一下这个话题。
操作系统的基本特征
并发和共享是操作系统的两个最基本的特征,它们又互为存在条件。一方面,资源共享是以程序(进程)的并发执行为条件的,若系统部允许程序并发执行,自然不存在资源共享问题。另一方面,若系统部能对资源共享实施有效管理,也必将影响程序的并发执行,甚至根本无法并发执行。
计算机的组成
计算机硬件
计算机的硬件由大量的IC (Integrated Circuit,集成电路)组成。每块IC上都带有许多引脚。这些引脚有的用于输入,有的用于输出。
从概念上讲,计算机的结构非常简单:
首先布置一根总线,然后将各种硬件设备挂在总线上。所有的这些设备都有一个控制设备,外部设备都由这些控制器与CPU通信。
而所有设备之间的通信均需通过总线,如果对总线做一个简单的概括,可以认为总线就是数字信号的集合,而这些信号被提供给计算机上的每块电路板:
计算机由处理器、存储器和输入/输出部件组成,每类部件都有一个或多个模块。这些部件以某种方式互连,以实现计算机执行程序的主要功能。因此,计算机有4个主要的结构化部件:
- 处理器(Processor):控制计算机的操作,执行数据处理功能。只有一个处理器时,它通常指中央处理器(CPU).
- 内存(Main memory):存储数据和程序。此类存储器通常是易失性的,即当计算机关机时,存储器的内容会丢失。相对于此的是磁盘存储器,当计算机关机时,它的内容不会丢失。内存通常也称为实存储器(real memory)或主存储器(primary memory)。
- 输入/输出模块(I/O modules):在计算机和外部环境之间移动数据。外部环境由各种外部设备组成,包括辅助存储器设备(如硬盘)、通信设备和终端。
- 系统总线(System bus):在处理器、内存和输入/输出模块间提供通信的设施。
处理器的一种功能是与存储器交换数据。为此,它通常使用两个内部寄存器:
- 存储器地址寄存器(Memory Address Register, MAR),用于确定下一次读/写的存储器地址。
- 存储器缓冲寄存器(Memory Buffer Register,MBR),存放要写入存储器的数据或从存储器中读取的数据。
同理,输入/输出地址存储器(I/O Address Register,简称I/O AR或I/O地址寄存器)用于确定一个特定的输入/输出设备,输入/输出缓冲寄存器(I/O Buffer Register,简称I/O BR或I/O缓冲寄存器)用于在输入/输出模块和处理器间交换数据。
内存模块由一组单元组成,这些单元由顺序编号的地址定义。每个单元包含一个二进制数,它可解释为一个指令或数据。输入/输出模块在外部设备与处理器和存储器之间传送数据。输入/输出模块包含内存缓冲区,用于临时保存数据,直到它们被发送出去。
计算机打开电源后的执行
在每台计算机上有一块双亲板(在政治因素影响到计算机产业之前,它们曾称为“母版”)。
在双亲板上有一个称为基本输入输出系统(Basic Input Output System, BIOS)的程序。在BIOS内有底层I/O软件,包括读键盘、写屏幕、进行磁盘I/O以及其他过程。
在计算机启动时,BIOS开始运行。它首先检查所安装的RAM数量、键盘和其他基本设备是否已安装并正常响应。
接着,它开始扫描PCIe和PCI总线并找出连在上面的所有设备。即插即用设备也被记录下来。如果现有的设备和系统上一次启动时的设备不同,则新的设备将被配置。然后BIOS通过尝试存储在CMOS存储器中的设备清单决定启动设备。用户可以在系统刚启动之后进入一个BIOS配置程序,对设备清单进行修改。
典型的,如果存在CD-ROM(有时是USB),则系统试图从中启动(之前重装系统就是这么操作的)。如果失败,系统将从硬盘启动。启动设备上的第一个扇区被读入内存并执行。这个扇区中包含一个对保存在启动扇区末尾的分区表检查的程序,以确定哪个分区是活动的。然后,从该分区读入第二个启动装载模块。来自活动分区的这个装载模块被读入操作系统,并启动之。
然后,操作系统询问BIOS,以获得配置信息。对于每种设备,系统检查对应的设备驱动程序是否存在。如果没有,系统要求用户插入含有该驱动程序的CD-ROM(由设备供应商提供)或者从网络上下载驱动程序。一旦有了全部的设备驱动程序,操作系统就将它们调入内核。然后初始化有关表格,创建需要的任何背景进程,并在每个终端上启动登陆程序或GUI。
不同的处理器和硬解系统可能会采用不同的策略,但是通用系统的启动分为三个步骤:
开机并执行bootloader程序
开机就是给系统开始供电,此时硬件电路会产生一个确定的复位时序,保证CPU是最后一个被复位的器件。为什么CPU要最后被复位呢? 因为,如果CPU是第一个被复位,则当CPU复位后开始运行时,其他硬件内部的寄存器状态可能还没有准备好,比如磁盘或者内存,那这样就可能出现外围硬件初始化的错误。当正确完成复位后,CPU开始执行第一条指令,该指令所在的内存地址是固定的,这由CPU的制造者指定。不同的CPU可能会从不同的地址获取指令,但这个地址必须是固定的,这个固定的地址所保存的程序往往被称为“引导程序“(Bootloader),因为其作用是装载真正的用户程序。
至于如何装载,则是一个策略问题,不同的CPU会提供不同的装载方式,比如有的是通过普通的并口存储器,有的则是通过SD卡,但是无论硬件上使用何种接口装载,装载过程必须提供以下信息,具体包括:
- 从哪里读取用户程序?
- 用户程序的长度是多少?
装载完用户程序后,应该跳转到哪里,即用户程序的执行入口在哪里?
机器在上电时自动把程序计数器设置为处理器厂商设计的某个固定值,对于ARM64处理器,这个固定值是0。处理器的内存管理单元(Memory Management Unit, MMU)负责把虚拟地址转换为物理地址,ARM64处理器刚上电的时候没有开启内存管理单元,物理地址和虚拟地址相同,所以ARM64处理器到物理地址0取第一条指令。嵌入式设备通常使用NOR闪存作为只读存储器来存放引导程序。NOR闪存的容量比较小,最小读写单位是字节,程序可以直接在芯片内执行。从物理地址0开始的一段物理地址空间被分配给NOR闪存。综上所述,ARM64处理器到虚拟地址0取指令,就是到物理地址0取指令,也就是到NOR闪存的起始位置取指令。 嵌入式设备通常使用U-Boot作为引导程序。U-Boot(Universal Boot Loader)是德国DENX软件工程中心开发的引导程序,是遵循GPL条款的开源项目。 U-Boot分为SPL和正常的U-Boot程序两个部分,如果想要编译为SPL,需要开启配置宏CONFIG_SPL_BUILD。SPL是“Secondary Program Loader”的简称,即第二阶段程序加载器,第二阶段是相对于处理器里面的只读存储器中的固化程序来说的,处理器启动时最先执行的是只读存储器中的固化程序。固化程序通过检测启动方式来加载第二阶段程序加载器。为什么需要第二阶段程序加载器?原因是:一些处理器内部集成的静态随机访问存储器比较小,无法装载一个完整的U-Boot镜像,此时需要第二阶段程序加载器,它主要负责初始化内存和存储设备驱动,然后把正常的U-Boot镜像从存储设备读到内存中执行。
- 操作系统内核初始化 执行内核程序,这一步所说的内核程序在上一步中指的就是”用户程序“。因为从CPU的角度来看,除了Bootloader之外的所有程序都是用户程序,只是从软件的角度来看,用户程序被分为”内核程序“和”应用程序“,而本步执行的是内核程序。 内核程序初始化时执行的操作包括,初始化各种硬件,包括内存、网络接口、显示器、输入设备、然后建立各种内部数据结构,这些数据结构将用于多线程调度及内存的管理等。当内核初始化完毕后就开始运行具体的应用程序了。在一般情况下,习惯于将第一个应用程序称为”Home程序“。 - 执行第一个应用程序 运行Home程序,比如Windows系统的桌面。之所以称为Home程序,是因为通过该程序可以方便的启动其他应用程序。而传统的Linux系统启动后第一个运行的程序一般是一个Terminal。 ### Android系统启动过程 目前的Android系统大多运行在ARM处理器之上。ARM本身是一个公司的名称,从技术的角度来看,它又是一种微处理器内核的架构。 对于ARM处理器,当复位完毕后,处理器首先执行芯片上ROM中的一小块程序。这块ROM的大小一般只有几KB,该段程序就是Bootloader程序,这段程序执行时会根据处理器上一些特定引脚的高低电平状态,选择从何种物理接口上装载用户程序,比如USB口、SD卡、并口Flash等。
多数基于ARM的实际硬件系统,会从并口NAND Flash芯片上的0x00000000地址处装载程序。对于一些小型嵌入式系统而言,该地址中的程序就是最终要执行的用户程序;而对于Android而言,该地址中的程序还不是Android程序,而是一个叫做uboot或者fastboot的程序,其作用是初始化硬件设备,比如网口、SDRAM、RS232等,并提供一些调试功能,比如向NAND Flash中写入新的数据,这可用于开发过程中的内核烧写、升级等。
当uboot(fastboot)被装载后便开始运行,它一般会先检测用户是否按下了某些特别的按键,这些特别按键是uboot在编译时预先预定好的,用于进入调试模式。如果用户没有按这些特殊的按键,则uboot会从NAND Flash中装载Linux内核,装载的地址是在编译uboot时预先约定好的。
Linux内核被装载后,就开始进行内核初始化的过程。
- Boot ROM: 当手机处于关机状态时,长按Power键开机,引导芯片开始从固化在
ROM
里的预设代码开始执行,然后加载引导程序到RAM
; - Boot Loader:这是启动Android系统之前的引导程序,主要是检查RAM,初始化硬件参数等功能。
Android平台的基础是Linux内核,比如ART虚拟机最终调用底层Linux内核来执行功能。Linux内核的安全机制为Android提供相应的保障,也允许设备制造商为内核开发硬件驱动程序。
- 启动Kernel的swapper进程(pid=0):该进程又称为idle进程, 系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Display,Camera Driver,Binder Driver等相关工作;
- 启动kthreadd进程(pid=2):是Linux系统的内核进程,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。
kthreadd进程是所有内核进程的鼻祖
。
这里的Native系统库主要包括init孵化来的用户空间的守护进程、HAL层以及开机动画等。启动init进程(pid=1),是Linux系统的用户进程,init进程是所有用户进程的鼻祖
。
- init进程会孵化出ueventd、logd、healthd、installd、adbd、lmkd等用户守护进程;
- init进程还启动
servicemanager
(binder服务管家)、bootanim
(开机动画)等重要服务 init进程孵化出Zygote进程,Zygote进程是Android系统的第一个Java进程(即虚拟机进程),
Zygote是所有Java进程的父进程
,Zygote进程本身是由init进程孵化而来的。Zygote进程.是由init进程通过解析init.rc文件后fork生成的. Zygote本身是一个Native的应用程序,与驱动、内核等均无关系。Zygote最初的名字叫“app_process”,这个名字是在Android.mk文件中指定的,但在运行过程中,app_process通过Linux下的pctrl系统调用将自己的名字换成了“Zygote”,所以我们通过ps命令看到的进程名是“Zygote”。Zygote是在Android系统中创建Java世界的盘古,它创建了第一个Java虚拟机,同时它又是女娲,它成功地繁殖了framework的核心system_server进程。 Zygote进程主要包含:
- 创建AppRuntime对象,并调用它的start。此后的活动则由AppRuntime来控制。
- 加载虚拟机:调用startVm创建Java虚拟机,然后调用startReg来注册JNI函数。
- 加载ZygoteInit类,通过JNI调用com.android.internal.os.ZygoteInit类的main函数,从此进入了Java世界。然而在这个世界刚开创的时候,什么东西都没有。注册Zygote Socket服务端套接字.Zygote及系统中其他程序的通信没有使用Binder,而是采用了基于AF_UNIX类型的Socket。调用registerZygoteSocket。通过这个函数,它可以响应子孙后代的请求。
- 启动System Server进程(分裂出了一个system_server进程):zygote觉得自己的工作压力太大,便通过调用startSystemServer分裂一个子进程system_server来为Java世界服务。Java世界中系统Service所驻留的进程system_server,该进程是framework的核心。如果它死了,就会导致Zygote自杀。
- 预加载类preloadClasses:preloadClass函数的执行时间比较长,这是导致Android系统启动慢的原因之一。
- 预加载资源preloadResouces:它主要是加载framework-res.apk中的资源。在UI编程中常使用的com.android.R.XXX资源是系统默认的资源,它们就是由Zygote加载的。
- System Server进程,是由Zygote进程fork而来,
System Server是Zygote孵化的第一个进程
,System Server负责启动和管理整个Java framework,包含ActivityManager,WindowManager,PackageManager,PowerManager等服务,也就是说Java中的核心Service都在这里启动。同时会启动Binder通信系统,调用Looper.loop()启动消息循环。 另外System Server进程还会启动一个Watch Dog的现成。Watch Dog的中文意思是“看门狗”。我依稀记得其最初存在的意义是因为早期嵌入式设备上的程序经常“跑飞”(比如说电磁干扰等),所以专门设置了一个硬件看门狗,每隔一段时间,看门狗就去检查一下某个参数是不是被设置了,如果发现该参数没有被设置,则判断为系统出错,然后就会强制重启。在软件层面上,Android对SystemServer的参数是否被设置也很谨慎,所以专门为它增加了一条看门狗,可它看的是哪个门呢?就是看几个重要Service的门,一旦发现Service出了问题,就会杀掉system_server,而这也会使zygote随其一起自杀,最后导致重启Java世界。具体实现是隔一段时间给另外一个线程发送一条MONITOR消息,那个线程将检查各个Service的健康情况。而看门狗会等待检查结果,如果第二次还没有返回结果,那么它会杀掉SS。 - Media Server进程,是由init进程fork而来,负责启动和管理整个C++ framework,包含AudioFlinger,Camera Service等服务。
- Zygote进程孵化出的第一个App进程是Launcher,这是用户看到的桌面App;
- Zygote进程还会创建Browser,Phone,Email等App进程,每个App至少运行在一个进程上。
- 所有的App进程都是由Zygote进程fork生成的。
虚拟化
程序运行时会发生什么?
- 首先,当然得进行编程,而编程需要计算机程序设计语言作为基础。对于绝大多数编写程序的人来说,使用的编程语言称为高级程序设计语言,如C、C++、Java等。但由于计算机并不认识高级语言编写的程序,编好的程序需要进行编译变成计算机能够识别的机器语言程序,而这需要编译器和汇编器的帮助。
- 其次,机器语言程序需要加载到内存,形成一个运动中的程序,即进程,而这需要操作系统的帮助。进程需要在计算机芯片CPU上执行才算是真正在执行,而将进程调度到CPU上运行也由操作系统完成。
- 最后,在CPU上执行的机器语言指令需要变成能够在一个个时钟脉冲里执行的基本操作,这需要指令集结构和计算机硬件的支持,而整个程序的执行过程还需要操作系统提供的服务和程序语言提供的执行环境(runtime environment)。这样,一个从程序到微指令执行的过程就完成了。
简单的来说一个正在运行的程序会做一件非常简单的事情:执行指令。处理器从内存中获取(fetch)一条指令,对其进行解码(decode)(弄清楚这是哪条指令),然后执行(execute)它(做它应该做的事情,如两个数相加、访问内存、检查条件、跳转到函数等)。完成这条指令后,处理器继续执行下一条指令,依此类推,直到程序最终完成。
这样,我们就描述了冯·诺依曼(Von Neumann)计算模型的基本概念。听起来很简单,对吧?但是在一个程序运行的同时,还有很多其他疯狂的事情也在同步进行——主要是为了让系统易于使用。
实际上,有一类软件负责让程序运行变得容易(甚至允许你同时运行多个程序),允许程序共享内存,让程序能够与设备交互,以及其他类似的有趣的工作。这些软件称为操作系统(Operating System,OS),因为它们负责确保系统既易于使用又正确高效地运行。
要做到这一点,操作系统主要利用一种通用的技术,我们称之为虚拟化(virtualization)。也就是说,操作系统将物理(physical)资源(如处理器、内存或磁盘)转换为更通用、更强大且更易于使用的虚拟形式。因此,我们有时将操作系统称为虚拟机(virtual machine)。
什么是虚拟化?
假设我们有一个桃子,我们称之为物理(physical)的桃子。但有很多想吃这个桃子的人,我们希望向每个想吃的人提供一个属于他的桃子,这样才能皆大欢喜。我们把给每个人的桃子称为虚拟(virtual)桃子。我们通过某种方式,从这个物理桃子创造出许多虚拟桃子。重要的是,在这种假象中,每个人看起来都有一个物理桃子,但实际上不是。每个人都不知道他在和别人一起分享这个桃子,但是如果我和别人分享同一个桃子我一定会发现这个问题。但是只有吃的人多才有这样的问题。多数时间他们都在打盹或者做其他事情,所以,你可以在他们打盹的时候把他手中的桃子拿过来分给其他人,这样我们就创造了有许多虚拟桃子的假象,每个人有一个桃子。
以最基本的计算机资源CPU为例,假设一个计算机只有一个CPU(尽管现在计算机一般拥有2个、4个或更多CPU),虚拟化要做的就是将这个CPU虚拟成多个虚拟CPU并分给每一个进程使用,因此,每个应用都以为自己在独占CPU,但实际上只有一个CPU。这样操作系统就创造了美丽的假象--它虚拟化了CPU。
操作系统通过进程抽象让每一个用户感觉有一台自己独享的CPU;通过虚拟内存抽象,让用户感觉物理内存空间具有无限扩张性,这就是把少变多。当然,操作系统的把少变多不是无中生有,变多也不是无限多,只是针对磁盘容量的大小。
CPU(Central Processing Unit)
CPU(中央处理器)是计算机的大脑,它主要和内存进行交互,从内存中提取指令并执行它。它是一块超大规模的集成电路(Integrated Circuit),是信息处理、程序运行的最终执行单元。其功能主要是解释计算机指令以及处理计算机软件中的数据。由于访问内存获取执行或数据要比执行指令花费的时间长,因此所有的 CPU 内部都会包含一些寄存器来保存关键变量和临时结果。
因此,在指令集中通常会有一些指令用于把关键字从内存中加载到寄存器中,以及把关键字从寄存器存入到内存中。
从功能方面来看,CPU的内部主要由寄存器,控制器,运算器构成,各部分之间由电流信号相互连通。其中运算器负责算术运算和逻辑运算,控制器负责计算指令的解析,产生各种控制指令,寄存器组用来临时存放参加运算的数据和计算的中间结果。
CPU计算结果最终需要写到内存中,内存的存取速度远低于CPU,为提升数据交换速率,CPU内部一般还集成了高速缓存(CACHE),其中缓存分为一级缓存和二级缓存,一级缓存和CPU速率相当,二级缓存次之。
CPU“取值-解码-执行”的速度叫时钟速度,单位是赫兹,赫兹是用来表示频率的单位。1赫兹代表1秒1个周期。
为了驱动CPU运转,称为“时钟信号”的电信号必不可少。这种电信号就好像带有一个时钟,滴答滴答地每隔一定时间就变换一次电压的高低。输出时钟信号的元件叫作“时钟发生器”。时钟发生器中带有晶振,根据其自身的频率(振动的次数)产生时钟信号。时钟信号的频率可以衡量CPU的运转速度。这里使用的是2.5MHz(兆赫兹)的时钟发生器。最大时钟频率(maximum clock speed),也称主频,是影响处理器速度的决定性因素之一。时钟频率决定了执行一条指令所需要的时间,处理器的数据位宽也影响处理器的速度。
操作系统通过虚拟化(virtualizing)CPU来提供这种假象。通过让一个进程只运行一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟CPU的假象。这就是时分共享(time sharing)CPU技术,允许用户如愿运行多个并发进程。潜在的开销就是性能损失,因为如果CPU必须共享,每个进程的运行就会慢一点。 要实现CPU的虚拟化,要实现得好,操作系统就需要一些低级机制以及一些高级智能。我们将低级机制称为机制(mechanism)。机制是一些低级方法或协议,实现了所需的功能。例如,我们稍后将学习如何实现上下文切换(context switch),它让操作系统能够停止运行一个程序,并开始在给定的CPU上运行另一个程序。所有现代操作系统都采用了这种分时机制。
CPU 的内部由寄存器、控制器、运算器和时钟四部分组成,各部分之间通过电信号连通。
寄存器
是中央处理器内的组成部分。它们可以用来暂存指令、数据和地址。可以将其看作是内存的一种。根据种类的不同,一个 CPU 内部会有 20 - 100个寄存器。控制器
负责把内存上的指令、数据读入寄存器,并根据指令的结果控制计算机运算器
负责运算从内存中读入寄存器的数据时钟
负责发出 CPU 开始计时的时钟信号
程序是把寄存器作为对象来描述的
使用高级语言编写的程序会在编译后转化成机器语言,然后通过CPU内部的寄存器来处理。不同类型的CPU,其内部寄存器的数量,种类以及寄存器存储的数值范围都是不同的。根据功能的不同,我们可以将寄存器大致划分为八类:
- 累加寄存器简称累加器(Accumulator Register, AC):是一个通用寄存器。存储执行运算的数据和运算后的数据。
- 标志寄存器(Flag Register):存储运算处理后的CPU的状态。
- 程序计数器(Program Counter, PC):记录将要取出的指令的地址。存储下一条指令所在内存的地址。
- 基址寄存器(Base Register):存储数据内存的起始地址。
- 变址寄存器(Index Register):存储基址寄存器的相对地址。
- 通用寄存器(General Purpose Register):存储任意数据。
- 指令寄存器(Instruction Register, IR):存储指令,CPU取到的指令存放在处理器的一个寄存器中,这个寄存器就是指令寄存器。CPU内部使用,程序员无法通过程序对该寄存器进行读写操作。
- 堆栈寄存器(Stack Register):目的是跟踪调用堆栈,存储栈区域的起始地址。指向内存中当前栈的顶端。堆栈指针会包含输入过程中的有关参数、局部变量以及没有保存在寄存器中的临时变量。
- 程序状态字寄存器(PSW(Program Status Word)):这个寄存器是由操作系统维护的8个字节(64位) long 类型的数据集合。它会跟踪当前系统的状态。除非发生系统结束,否则我们可以忽略 PSW 。用户程序通常可以读取整个PSW,但通常只能写入其某些字段。PSW 在系统调用和 I / O 中起着重要作用。
其中,程序计数器,累加寄存器,标志寄存器,指令寄存器和栈寄存器都只有一个,其他的寄存器一般有多个。
计算机执行的原理是: 取指执行
处理器执行的程序是由一组保存在存储器中的指令组成的。最简单的指令处理包括两步: 处理器从存储器中一次读(取)一条指令,然后执行每条指令。程序执行是由不断重复的取指令和执行指令的过程组成的。指令执行可能涉及很多操作,具体取决于指令本身。
在典型的处理器中,程序计数器(Program Counter, PC)保存下一次要取的指令地址。除非出现其它情况,否则处理器在每次取值令后总是递增PC,以便能按顺序取下一条指令(即位于下一个存储器地址的指令)。取到的指令放在处理器的一个寄存器中,这个寄存器称为指令寄存器(Instruction Register,IR)。指令中包含确定处理器将要执行的操作的位,处理器解释指令并执行对应的操作。大体上,这些动作可分为4类:
- 处理器-存储器:数据可以从处理器传送到存储器,或从存储器传送到处理器。
- 处理器-I/O:通过处理器和I/O模块间的数据传送,数据可以输出到外部设备,或从外部设备向处理器输入数据。
- 数据处理:处理器可以执行很多与数据相关的算术操作或逻辑操作。
- 控制: 某些指令可以改变执行顺序。例如,处理器从地址为149的存储单元中取出一条指令,该指令指向下一条指令应该从地址为182的存储单元中取,这样处理器就会把程序计数器置为182.因此在下一个取指阶段,将从地址182的存储单元而非150的存储单元中取指令。
CPU 指令执行过程
那么CPU是如何执行一条条的指令的呢?
几乎所有的冯·诺伊曼型计算机的CPU,其工作都可以分为5个阶段:取指令、指令译码、执行指令、访存取数、结果写回。
- 取指令阶段是将内存中的指令读取到CPU中寄存器的过程,程序寄存器用于存储下一条指令所在的地址
- 指令译码阶段,在取指令完成后,立马进入指令译码阶段,在指令译码阶段,指令译码器按照预定的指令格式,对取回的指令进行拆分和解释,识别区分出不同的指令类别以及各种获取操作数的方法。
- 执行指令阶段,译码完成后,就需要执行这一条指令了,此阶段的任务是完成指令所规定的各种操作,具体实现指令的功能。
- 访问取数阶段,根据指令的需要,有可能需要从内存中提取数据,此阶段的任务是:根据指令地址码,得到操作数在主存中的地址,并从主存中读取该操作数用于运算。
- 结果写回阶段,作为最后一个阶段,结果写回(Write Back,WB)阶段把执行指令阶段的运行结果数据“写回”到某种存储形式:结果数据经常被写到CPU的内部寄存器中,以便被后续的指令快速地存取;
假设目前一台机器的处理器包含一个累加器(AC)的数据寄存器,所有指令和数据长度均为16位,使用16位的单元或字来组织存储器。指令格式中有4位是操作码。操作码定义了处理器执行的操作。通过指令格式剩下的12位,来直接访问存储器。
如上图中的三个指令中的操作码描述了要执行的操作。
下图描述了程序的执行过程。给出的程序片段把地址为940的存储单元中的内容与地址为941的存储单元的内容相加,并将结果保存在后一个单元中。这需要三条指令。
该图中,为把地址为940的存储单元中的内容与地址为941的存储单元中的内容相加,一共需要三个指令周期,每个指令周期都包含一个取指阶段和一个执行阶段。具体步骤为 :
- PC中包含第一条指令的地址为300,该指令内容(值为十六进制数1940)被送入指令寄存器IR中,PC增加1。注意,该处理过程中使用了存储器地址寄存器(MAR)和存储器缓冲寄存器(MBR)。为简单起见,这里未显示这些中间寄存器。
- IR中取出操作码,也就是最初的4位(对应到这里的十六进制,就是第一位数也就是1),而在上面1对应的二进制操作码是0001 = 从内存中载入AC。剩下的12位(后三个十六进制数)表示的是地址为940。所以这里的意思就是将存储器940地址的单元加载到AC中。
- 然后继续去下一条指令,PC现在是301了,所以要从地址为301的存储单元中取下一条指令,也就是(5941),同时PC增加1。这条指令是5941,最初的4位操作码是5,对应的二进制是0101,上面的操作码图标中0101 = 从内存中添加到AC。剩下的12位(后三个十六进制数)表示的是地址为941。
- 内存地址为941的存储单元中的内容与AC中以前的内容相加,结果保存在AC中。
- 接着读下一个指令,现在PC是302了,从302的存储单元中读取下一个指令为2941,PC同时加1.前四位操作码对应的是2,转换成二进制也就是0010 = 将AC存储到内存。后十二位对应的地址是941。所以这条指令的意思就是将AC中的内容存储到地址为941的存储单元中。
- 执行指令,将AC中的内容存储到地址为941的存储单元中。现在941存储单元的内容变成了5.
为了进一步提高计算机的效率,在流水线的基础上,人们又发明了多流水线、超标量计算和超长指令字等多指令发射机制。这些机制的发明在提升计算机效率(主要是吞吐量)的同时,也极大地增加计算机结构的复杂度,并对操作系统和编译器提出了更高的要求。下图描述的是一个超标量发射的体系结构。这个结构有两队指令读取和译码单元,以及三个执行单元。通过一个指令保持缓冲区,就可以实现多路复用(Multiplex)和反多路复用(de-Multiplex),从而提高系统每个功能单元的利用率和整个系统的吞吐量。
操作系统的两种CPU状态
就像世界上的人并不平等一样,并不是所有的程序都是平等的。世界上有的人占有资源多,有的人占有资源少,有的人来了,别人得让出资源,有的人则专门为别人让出资源。程序也是这样的,有的程序可以访问计算机的任何资源,有的程序则只能访问少量受限资源。操作系统作为计算机的管理者,自然不能和被管理者享受一样的待遇。它应该享有更多的方便或权限。为了区分不同的程序的不同权限,人们发明了内核态和用户态的概念。
内核态(Kernel Mode):运行操作系统程序
内核态就是拥有资源多的状态,或者说访问资源多的状态,又称为特权态。
用户态(User Mode):运行用户程序
用户态就是非特权态,在此种状态下访问的资源将受到限制。
如果一个程序运行在特权态,则该程序就可以访问计算机的任何资源,即它的资源访问不受限制。如果一个程序运行在用户态,则其资源需求将受到各种限制。很显然,内核态和用户态各有优势:
- 运行在内核态的程序可以访问的资源多,但可靠性、安全性要求高,维护管理都较复杂;
- 用户态程序程序访问的资源有限,但可靠性、安全性要求低,自然编写维护起来比较简单。 一个程序到底应该运行在内核态还是用户态则取决于其对资源和效率的需求。一般来说,如果一个程序能够运行于用户态,就应该让它运行在用户态。只在迫不得已的情况下,才让程序运行于内核态。凡是牵扯到计算机本体根本运行的事情都应该在内核态下执行,只与用户数据和应用相关的东西则放在用户态执行。另外,对时序要求特别高的操作,也应该在内核态完成。
态势的识别
那么计算机是如何知道现在正在运转的程序是内核态程序呢?而正确做出内核态或用户态的判断对系统的正确运行至关重要。显然做出这种判断需要某种标志。这个标志就是处理器的一个状态位。这个状态位是CPU状态字里面的一个字位。也就是说,所谓的用户态、内核态实际上是处理器的一种状态,而不是程序的状态。我们通过设置该状态字,可以将CPU设置为内核态、用户态或者其他的子态(有的CPU有更多种子态)。一个程序运行时,CPU是什么态,这个程序就运行在什么态。
操作系统只需要这两种状态,同时这两种状态由对应的两种指令:
- 特权(privilege)指令:只能由操作系统使用、用户程序不能使用的指令
- 非特权指令:用户程序可以使用的指令
用户态 -> 内核态的唯一途径是通过陷入(trap,一种中断方式)机制(将系统调用的代码放在一个约定好的寄存器中,通过陷入将控制交给操作系统)。
内核态 -> 用户态是通过设置程序状态字PSW。
一旦 CPU 决定去实施中断后,程序计数器和 PSW 就会被压入到当前堆栈中并且 CPU 会切换到内核态。设备编号可以作为内存的一个引用,用来寻找该设备中断处理程序的地址。这部分内存称作中断向量(interrupt vector)
。一旦中断处理程序(中断设备的设备驱动程序的一部分)开始后,它会移除栈中的程序计数器和 PSW 寄存器,并把它们进行保存,然后查询设备的状态。在中断处理程序全部完成后,它会返回到先前用户程序尚未执行的第一条指令。
中断/异常
为什么要引入中断与异常?
中断的引入: 为了支持CPU和设备之间的并行操作。
当CPU启动设备进行输入/输出后,设备便可以独立工作,CPU转去处理与此次输入/输出不相关的事情。当设备完成输入/输出后,通过向CPU发中断报告此次输入/输出的结果,让CPU决定如何处理以后的事情。
异常的引入:表示CPU执行指令时本身出现的问题。
如算数溢出、除零、读取时的奇偶错,访问地址时的越界或执行了“陷入指令”等,这时硬件改变了CPU当前的执行流程,转到相应的错误处理程序或异常处理程序或执行系统调用。
中断的概念
所谓中断,是指CPU对系统中或系统外发生的异步事件的相应。
异步事件是指无一定时序关系的随机发生的时间,如外围设备完成数据传输,实时控制设备出现异常情况等。“中断”这个名称来源于:当这些异步事件发生后,打断了CPU对当前程序的执行,而转去处理该异步事件(执行该时间的中断处理程序)。直到处理完成之后,再转回原程序的中断点继续执行。
可以说操作系统是由“中断驱动”或者“时间驱动”的。中断/异常是CPU对系统发生的某个事件作出的一种反应。
所有计算机都提供了允许其他模块(I/O、存储器)中断处理器正常处理过程的机制。中断最初是用于提高处理器效率的一种手段。例如,多数I/O设备都要远慢于处理器,处理器必须暂停并保持空闲,直到打印机完成工作。暂停的时间长度可能相当于成百上千个不涉及存储器的指令周期,显然,这对于处理器的使用来说是非常浪费的。这种只有一个单独程序的情况,称为单道程序设计。在单道程序设计中处理器花费一定的运行时间进行计算,直到遇到一个I/O指令,这时它必须等到该I/O指令结束后才能继续执行。这种问题是可以避免的,就是存储器可以保存多个程序,在一个程序等待时通过切换去执行其他的程序,这种处理称为多道程序设计或多任务处理。它是现代操作系统的主要方案。多道程序设计的目的是为了让处理器和I/O设备(包括存储设备)同时保持忙状态,以实现最大的效率。
利用中断功能,处理器可以在I/O操作的执行过程中去执行其他命令。在这期间,如果I/O操作已经完成,此时外部设备在做好服务的准备后,即它准备好从处理器接收更多的数据时,外部设备的I/O模块给处理器发送一个中断请求信号。这时处理器会做出响应,暂停当前程序的处理,转去处理服务于特定I/O设备的程序,这种程序被称为中断处理程序(interupt handler)。在对该设备的服务响应完成后,处理器恢复原来的执行。
从用户程序的角度来看,中断打断了正常执行的序列。中断处理完成后,再恢复执行。因此,用户程序并不需要为中断添加任何特殊的代码,处理器和操作系统负责挂起用户程序,然后在同一个地方恢复执行。
为使用中断产生的情况,在指令周期中要增加一个中断阶段。在中断阶段,处理器检查是否有中断发生,即检查是否出现中断信号。若没有中断,处理器继续运行,并在取指周期取当前程序的下一条指令。若有中断,处理器挂起当前程序的执行,并执行一个中断处理程序。这个中断处理程序通常是操作系统的一部分,它确定中断的性质,并执行所需要的操作。
举个栗子
中断
有一天我正在看书的时候,来电话了,这时候我需要执行中断,我会把当前看到的书的进度用书签标记,然后去接电话,等电话接完后,再返回从书签的位置继续读书。
异常
同样我在读书,但是由于新买的书纸张太硬,一不小心把手划破流血了,那这个时候就是异常,我同样需要把当前看到的书的进度用书签标记,然后去用创可贴来处理伤口,等处理完后再返回书签的位置继续读书。
中断的出现解决了主机和外围设备并行工作的问题,消除了因外围设备的慢速而使得主机等待的现象,提高了可靠性,为多机操作和实时处理提供了硬件基础。引起中断的那些事件称为中断事件或中断源,中断源向CPU发出的请求信号称为中断请求,而处理中断事件的那段程序称为中断处理程序。发生中断时正在执行的程序的暂停点称为中断断点,CPU暂停当前程序转而处理中断的过程称为中断响应,中断处理结束之后恢复原来程序的执行称为中断返回。
中断系统
中断系统包括量大组成部分:
- 中断系统的硬件中断装置
- 软件中断处理程序
中断装置负责捕获中断源发出的中断请求,并以一定的方式响应中断源,然后将CPU的控制权移交给特定的中断处理程序。中断处理程序负责辨别中断类型,并根据请求作出相应的操作。中断装置提供了中断系统的基本框架,是中断系统的机制部分。中断处理程序是利用中断机制对处理能力的扩展和对多种处理需求的适应,属于中断系统的策略部分。
中断处理
当I/O设备完成一次I/O操作时,发生以下硬件事件:
- 设备给处理器发送一个中断信号。
- 处理器在响应中断前结束当前指令的执行。
- 处理器对中断进行测试,确定存在未响应的中断,并给提交中断的设备发送确认信号,确认信号允许该设备取消它的中断信号。
- 处理器需要准备把控制权转交给中断程序。首先,需要保存从中断点恢复当前程序所需要的信息,要求的最少信息包括程序状态字(PSW)和保存在程序计数器(PC)中的下一条要执行的指令地址,它们被压入系统控制栈。
- 处理器把响应此中断的中断处理程序入口地址装入程序计数器。每类中断可由一个中断处理程序,具体取决于计算机系统架构和操作系统的设计。如果有多个中断程序,这一信息可能已包含在最初的中断信号中,否则处理器必须给发中断的设备发送请求,以获取含有所需信息的响应。一旦装入程序计数器,处理器就继续执行下一个指令周期,该指令周期也从取指开始。由于取指是由程序计数器的内容决定的,因此控制权被转交给中断处理程序,该程序会引起以下操作:
- 在这一点,与被中断程序相关的程序计数器和PSW被保存到系统栈中,此外,还有一些其他信息被当做正在执行程序的状态的一部分。特别需要保存处理器寄存器的内容,因为中断处理程序可能会用到这些寄存器,因此所有这些值和任何其他状态信息都需要保存。
- 中断处理程序现在可以开始处理中断,其中包括检查与I/O操作相关的状态信息或其他引起中断的事件,还可能包括给I/O设备发送附加命令或应答。
- 中断处理结束后,被保存的寄存器值从栈中释放并恢复到寄存器中。
- 最后的操作是从栈中恢复PSW和程序计数器的值,因此下一条要执行的指令来自前面被中断的程序。
与每一 I/O 类相关联的是一个称作 中断向量(interrupt vector)
的位置(靠近内存底部的固定区域)。它包含中断服务程序的入口地址。假设当一个磁盘中断发生时,用户进程正在运行,则中断硬件将程序计数器、程序状态字、有时还有一个或多个寄存器压入堆栈,计算机随即跳转到中断向量所指示的地址。这就是硬件所做的事情。然后软件就随即接管一切剩余的工作。
当中断结束后,操作系统会调用一个 C 程序来处理中断剩下的工作。在完成剩下的工作后,会使某些进程就绪,接着调用调度程序,决定随后运行哪个进程。然后将控制权转移给一段汇编语言代码,为当前的进程装入寄存器值以及内存映射并启动该进程运行,下面显示了中断处理和调度的过程。
- 硬件压入堆栈程序计数器等
- 硬件从中断向量装入新的程序计数器
- 汇编语言过程保存寄存器的值
- 汇编语言过程设置新的堆栈
- C 中断服务器运行(典型的读和缓存写入)
- 调度器决定下面哪个程序先运行
- C 过程返回至汇编代码
- 汇编语言过程开始运行新的当前进程
一个进程在执行过程中可能被中断数千次,但关键每次中断后,被中断的进程都返回到与中断发生前完全相同的状态。
中断异常机制工作原理
中断/异常机制是现代计算机系统的核心机制之一。硬件和软件相互配合而使计算机系统得以充分发挥能力。
硬件该做什么是? --- 中断/异常响应 捕获中断源发出的中断/异常请求,以一定方式响应,将处理器控制权交给特定的处理程序。
软件要做什么是? ---中断/异常处理程序 识别终端/异常类型并完成相应的处理
存储器
在任何一种计算机中,第二种主要部件都是存储器。在理想情况下,存储器应该极为迅速(快于执行一条指令,这样CPU就不会受到存储器的限制),充分大并且非常便宜。但是目前的技术无法同时满足这三个目标。
- 存取时间越快,每“位”的价格越高
- 容量越大,每“位”的价格越低
- 容量越大,存取速度越慢
于是出现了多级存储器组织结构:
寄存器: 最快、最小和最贵的存储器类型由位于处理器内部的寄存器组成。它们用与CPU相同的材料制成,所以和CPU一样快。显然,访问它们是没有时延的。其典型的存储容量是,在32位CPU中为32x32位,而在64位CPU中为64x64位。在这两种情况下,其存储容量都小于1KB。典型情况下,一个处理器包含多个寄存器,某些处理器包含上百个寄存器。
高速缓存,它多数由硬件控制。
主存:这是存储器系统的主力。主存通常称为随机访问存储器(Random Access Memory,RAM),内存是计算机中主要的内部内部存储器系统。内存中的每个单元位置都有唯一的地址对应,而且大多数机器指令会访问一个或多个内存地址。内存通常是高速的、容量较小的高速缓存的扩展。
磁盘:磁盘同RAM相比,成本降低了,但是随机访问数据时间也慢了。其低速的原因是因为磁盘是一种机械装置。一个磁盘中有一个或多个金属盘片,他们以一定的速度旋转,从边缘开始有一个机械臂悬横在盘面上,这类似于老式播放塑料唱片的唱片机。

有时,还有一些实际上不是磁盘的磁盘,比如固态硬盘(Solid State Disk,SSD)。固态硬盘并没有可以移动的部分,外形也不像唱片那样,并且数据是存储在存储器(闪存)中的。与磁盘唯一的相似之处就是它也存储了大量即使在电源关闭时也不会丢失的数据。
这里要说一下闪存(flash memory),闪存是一种基于硅芯片的存储介质,可以用电写入或擦除。在便捷式电子设备中,闪存通常作为存储媒介。闪存是数码相机中的胶卷。是便捷式音乐播放器的磁盘。这仅仅是闪存用途的两项。闪存在速度上介于RAM和磁盘之间。另外,与磁盘存储器不同的是,如果闪存擦除次数过多,就被磨损了。
那闪存和固态硬盘有什么区别?固态硬盘也是将数据存储在闪存中。在存储行业中使用的最简单的类比之一是闪存就像鸡蛋,而SSD硬盘就像煎蛋卷一样。煎蛋卷主要是由鸡蛋制作的,而SSD硬盘主要由闪存制成的。
内存
通常所说的内存指的是计算机的主存储器(mainmemory)简称主存。主存通过控制芯片等与CPU相连,主要负责存储指令和数据。主存由可读写的元素构成,每个字节(1字节=8位)都带有一个地址编号。CPU可以通过该地址读取主存中的指令和数据,当然也可以写入数据。
内存实际上是一种名为内存IC的电子元件。虽然内存IC包括DRAM、SRAM、ROM等多种形式,但从外部来看,其基本机制都是一样的。内存IC中有电源、地址信号、数据信号、控制信号等用于输入输出的大量引脚(IC的引脚),通过为其指定地址(address),来进行数据的读写。
下面是内存IC(在这里假设它为RAM)的引脚配置示例。虽然这是一个虚拟的内存IC,但它的引脚和实际的内存IC是一样的。VCC和GND是电源,A0~A9是地址信号的引脚,D0~D7是数据信号的引脚,RD和WR是控制信号的引脚(读取或写入)。将电源连接到VCC和GND后,就可以给其他引脚传递比如0或者1这样的信号。大多数情况下,+ 5V的直流电压表示1,0V表示0。
那么,这个内存IC中能存储多少数据呢?数据信号引脚有D0~D7共八个,表示一次可以输入输出8位(=1字节)的数据。此外,地址信号引脚有A0~A9共十个,表示可以指定0000000000~1111111111共1024个地址。而地址用来表示数据的存储场所,因此我们可以得出这个内存IC中可以存储1024个1字节的数据。因为1024=1K,所以该内存IC的容量就是1KB。
虽然内存的实体是内存IC,不过从程序员的角度来看,也可以把它假想成每层都存储着数据的楼房,并不需要过多地关注内存IC的电源和控制信号等。
内存包括主存(内存条,基于DRAM(动态RAM))与高速缓存(Cache,基于SRAM(静态RAM,静态RAM速度很快但是成本很高,所以用于在CPU内部充当缓冲))两部分。可能是由于Cache相较内存条容量很小,毕竟内存容量只计内存条大小,加上重要性也不及内存条,一般人或许不知道Cache,所以就忽略了高速缓存Cache,直接将主存--内存条等同了内存吧。计算器内存条采用的是DRAM(动态随机存储器),即计算机的主存。通常所说的内存容量即指内存条DRAM的大小。
高速缓冲存储器Cache主要是为了解决CPU和主存速度不匹配而设计的。Cache一般由SRAM(静态随机存储器)芯片实现,它的存取速度接近CPU,快于DRAM,存储容量小于DRAM。它比主存的优先级高,CPU存取信息时优先访问Cache,找不到的话再去主存DRAM中找,同时把信息周围的数据块从主存复制到Cache中。 现代计算机系统基本都采用Cache-主存-辅存(即外存储器)三级存储系统。其中CPU可直接访问Cache和主存,辅存则通过主存与CPU交换信息。
主存的分类
RAM(Random-access memory)
随机存取存储器,一般使用动态半导体存储器件(DRAM),对于CPU来说,RAM是主要存放数据和程序的地方,所以也叫做“主存”。
ROM(Read-Only Memory)
只读存储器,出厂时其内容由厂家用掩膜技术写好,只可读出,但无法改写。信息已固化在存储器中,一般用于存放系统程序BIOS和用于微程序,断电也没有关系,放ROM的数据一辈子都不会变。
缓存
位于CPU与内存之间的临时存储器,它的容量比内存小的多但是交换速率却比内存要快得多。缓存的出现主要是为了解决CPU运算速率与内存读写速率不匹配的矛盾,缓存往往使用的是RAM,L1 Cache(一级缓存)是CPU第一层高速缓存,一般L1缓存的容量通常在32—256KB,L1分为数据Cache,指令Cache,L2 Cache(二级缓存)是CPU的第二层高速缓存,分内部和外部两种芯片,内部的芯片二级缓存运行速率与主频相同,而外部的二级缓存则只有主频的一半,缓存只是内存中少部分数据的复制品。二级缓存是比一级缓存速率更慢,容量更大的内存,主要就是做一级缓存和内存之间数据临时交换的地方用。为了适应速率更快的处理器。
缓存基本上都是采用SRAM存储器,它是一种具有静态存取功能的存储器,不需要刷新电路即能保存它内部存储的数据。不像DRAM内存那样需要刷新电路,每隔一段时间,固定要对DRAM刷新充电一次,否则内部的数据即会消失,因此SRAM具有较高的性能,但是SRAM也有它的缺点,即它的集成度较低,相同容量的DRAM内存可以设计为较小的体积,但是SRAM却需要很大的体积,这也是不能将缓存容量做得太大的重要原因。它的特点归纳如下:优点是节能、速率快、不必配合内存刷新电路、可提高整体的工作效率,缺点是集成度低、相同的容量体积较大、而且价格较高,只能少量用于关键性系统以提高效率。
缓存的工作原理是当CPU要读取一个数据时,首先从CPU缓存中查找,找到就立即读取并送给CPU处理;没有找到,就从速率相对较慢的内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。正是这样的读取机制使CPU读取缓存的命中率非常高,一般把静态RAM缓存叫一级缓存,而把后来增加的动态RAM叫二级缓存。
系统软件和应用软件
- 系统软件是指控制和协调计算机以及外部设备,支持应用软件开发和运行的系统,是无需用户干预的各种程序的集合。主要功能是调度、监控和维护计算机系统。例如: 操作系统和一系列的基本工具(编译器、数据库管理、存储器格式化、用户身份验证、网络连接)。
- 应用软件是和系统软件相对的,是用户可以使用的各种程序设计语言,以及用各种程序设计语言编译的应用程序的集合,分为应用软件包和用户程序。例如:互联网软件、多媒体软件、协作软件等。
Linux系统
Android系统
- 应用和框架:应用开发者最关心这一层及访问低层服务的API。
- Binder IPC:Binder进程间通信机制允许应用框架打破进程的界限来访问Android系统服务代码,从而允许系统的高层框架API与Android的系统服务进行交互。
- Android系统服务:框架中大部分能够调用系统服务的接口都向开发者开放,以便开发者能够使用底层的硬件和内核功能。Android系统服务分为两部分:媒体服务处理播放和录制媒体文件,系统服务处理应用所需要的系统功能。
- 硬件抽象层(HAL):HAL提供调用核心层设备驱动的标准接口,以便上层代码不需要关心具体驱动和硬件的实现袭击,Android的HAL与标准的HAL基本一致。
- Linux内核:Linux内核已被裁剪到满足移动环境的需求。
Android在Linux内核中增加了两个提升电源管理能力的新功能: 报警和唤醒锁。
报警功能是在Linux内核中实现的,开发者可通过调用运行库中的报警管理器来进行操作。通过报警管理器,应用可以请求定时叫醒服务。报警管理器是内核服务,目的是让应用即使在系统休眠的情况下也能触发警告提醒。这就使得系统随时可以进入休眠状态以节省电能,即使有一个进程有需要被唤醒的服务。
唤醒锁也可以阻止Android系统进入休眠模式。一个应用程序占有一下唤醒锁中的一个:
- full_wake_lock:处理器工作,屏幕亮,键盘亮。
- partial_wake_lock:处理器工作,屏幕关,键盘关。
- screen_dim_wake_lock:处理工作,屏幕暗,键盘关。
- screen_bright_wake_lock:处理器工作,屏幕亮,键盘关。
当应用要求被管理的外设保持供电时,会通过API请求对应的锁。若无唤醒锁存在,系统就会锁定并关闭设备以节省电能。
Linux操作系统特点
Linux是类Unix系统,借鉴了Unix的设计并实现相关接口,但并非Unix。Linux是由Linus Torvalds于1991年创造的开源免费系统,采用GNU GPL协议保护,下面列举Linux的一些主要特点:
- Linux系统中万物皆为文件,这种抽象方便操作数据或设备,只需一套统一的系统接口open, read, write, close即可完成对文件的操作
- Linux是单内核,支持动态加载内核模块,可在运行时根据需求动态加载和卸载部分内核代码;
- Linux内核支持可抢占;
- Linux内核创建进程,采用独特的fork()系统调用,创建进程较高效;
- Linux内核并不区分进程和线程,对于内核而言,进程与线程无非是共享资源的区别,对CPU调度来说并没有显著差异。
- 邮箱 :[email protected]
- Good Luck!