作者:FloatingGuy 转载请注明出处:https://floatingguy.github.io/


《深入解析MAC OS X & iOS 操作系统》- 第 二 章: 合众为一:OSX & iOS 使用的技术

重点知识

本章重点介绍 架构设计原则基础模块构建

架构采用4层架构: 用户体验层 –> Darwin。
基础模块: 突出架构中的核心;会从4层架构中抽取来介绍的模块: Darwin、bundle、应用 APP、用户体验层–Aqua/QuickLook/Spotlight 等。

OSX & iOS框架设计


IOS/OSX 4层架构.

上面3 层闭源,Darwin 是开源的。

darwin 的层次 && 架构

  1. 层次

darwin -> xnu (混合内核) -> bsd (POSIX标准) -> mach (微内核) + iokit 组件

  1. 架构

Object-C 编写的 app 只需 要使用框架Cocoa的 接口。

组件/模块

组件中也包含了框架,因为每个组件自身存在框架,并且组件之间的联系也是系统框架(指 OSX/iOS系统)的一部分。

用户体验层

主要包括一下组件(介绍 osx+ios 对应的组件):

  • Aqua
  • Quick look
  • spotlight
  • Accessibility 选项

Aqua – OSX GUI

有2条流水线 (是顺序执行的关系):
【1】开启GUI
由launchd 启动 -> WindowServer -> …-> CGXServer fork 子进程

【2】启动交互式登录
launchd -> LoginWindow 进程

模块所在 路径
/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/WindowServer

Quick look – 预览

采用插件扩展架构,大部分工作由插件完成。

插件的文件格式 后缀.qlgenerator的 bundle.

插件的组成:特殊编译的二进制程序 + 配置文件
特殊编译的二进制程序 的入口点不是 main 而是 QuickLookGeneratorPluginFactory 入口点。
配置文件:将 插件和对应的文件类型关联起来。

文件类型 是什么?
UTI (Uniform Type Identifier)? 逆 DNS 表示法?

quicklookd “QuickLook 服务器”, 通过 /System/Library/。。。。/com.apple.quicklook.plist 文件在登录时启动。

工具:
qlmanage 插件管理工具,控制后台服务。

使用工具
qlmanage -m

qlmanage
qlmanage 显示 管理的插件(入口,文件,版本)

spotlight – 快速搜索工具

其核心是一个 mds 索引服务器,记录了系统所有文件(支持的文件类型)的 元数据。
在 创建、修改 和删除 事件发生时,内核通知 mds。 通知机制是 fsevents。

mds 负责接受消息,具体干活的是 mdworker 进程,负责将各种元数据信息导入数据库。
mdworker 可以加载 spotlight importer 导入器从文件中 提取元数据。

导入器 有系统提供的在 /System/Library/Spotlight 目录,也有用户自定义的在/Library/Spotlight/。

演示 使用 xcode 创建 spotlight 导入器工程
importer
importer

工具:

  • mdutil
  • mdfind
  • mdimport
  • mdls
  • mdcheckschema
  • mddiagnose

Darwin – UNIX 核心

OSX 开启 ssh
通过修改 /System/Library/LaunchDaemons 中的 plist (ssh.plist),将 Disabled 设置为 false.
ios 需要越狱后 单独安装 ssh, 并且默认有2个用户,root 和 mobile 。 root 默认密码 alpine。

HFS+ 文件系统

此文件系统 可以细分为4个类型:

  • 大小写敏感:此时 采用 HFSX 文件系统,是 iOS 上默认的文件系统。
  • 不敏感:OSX 采用此类 文件系统
  • 支持日志功能:此时 采用 JHFS 文件系统,日志文件系统 通过一个日志记录文件 系统任务执行状态,相当于一个快照,文件系统挂载时 可以去查找 日志。
  • 不支持日志功能:

从类型名称 就可以知道其区别,注意 大小写敏感文件系统显示 和 访问时的现象。

显示:不管大小写是否 敏感,都采用创建时的方式显示。
访问:大小写不敏感就可以 随意使用 大小写 。比如 LS/ETC 。。命令, 铭感的文件系统 就要区分大小写。

开发时是使用的 API 并不是 HFS+文件系统直接提供,而是由 虚拟文件交换系统 VFS 提供。VFS 是内核中使用的所有文件系统的统一接口。
VFS 的特性:同时支持 UNIX 文件系统和 外部文件系统。

UNIX 系统目录

OSX 中 UNIX 的目录


1
2
3
4
5
6
7
/bin/
/sbin/ 系统程序
/usr/ 安装第三方软件
/etc/
/dev BSD 设备文件
/tmp 临时文件
/var 杂项文件

OSX 中 特殊的目录


1
2
3
4
5
6
7
Applications/
Developer/
Volumes/
Library/
System/ 系统文件。包含了系统中所有重要的组件,如框架--/System/Library/Frameworks,内核模块--/System/Library/Extensions
Users/
/Core 核心转储文件。如果 ulimit() 命令允许创建 核心转储文件,那么当进程奔溃时会创建。包含了进程的核心虚拟内存镜像。

iOS 中 目录 & 与 OSX 的目录区别


iOS 内核镜像和 OSX 的区别:

除了上面介绍的文件系统的区别,内核镜像上也存在差异。

iOS 的内核以 kernelcache 的形式将内核扩展 打包在内核中。(kernelcache 在/System/Library/Caches/com.apple.kernelcaches中)。
iOS 内核缓存是加密的 Img3文件。

相关章节:
5、18 章

bundle

bundle 在 OSX 中有多种解释,不再只是 一种文件格式类型,bundle 同时表示一种 目录结构(也称为”包 (package)”)。

bundle 在 OSX 中应用在: 框架、插件、widget、内核扩展中,这些组件都被打包成 bundle。

bundle 中包含了 代码、配置文件(plist)、资源文件。

应用程序和 app

GUID 目录名??
在 iOS app 运行时会被 chroot 到自己的应用目录下 – 名字为GUID的目录–并且不能逃脱这个目录访问文件系统的其他目录。
应用程序会将 自己GUID名字的目录当做根目录,因此需要创建临时目录时,/tmp 指向 GUID/tmp.

ios app 标准目录下的文件/目录 用途
Documents 应用数据
iTunesArtwork app 的图标,一般是 JPG 文件
iTunesMetaData.plist app的属性列表文件,二进制的 plist 文件
Library/ 杂项文件,包括 Caches、Cookies、Preferences、WebKit
tmp 临时文件目录

OSX 上的应用格式

非常标准的 bundle 格式存储文件

iOS 应用格式

混乱的 app 格式。
ll ~/Music/iTunes/iTunes\ Media/Mobile\ Applications

从 APP Store 下载应用时,应用以.ipa 文件的形式打包–实际就是 zip 文件。

info.plist 文件

首先 介绍这个最重要的文件, 此文件相当于 android apk 里的 Manifest.xml。
此文件 记录了 bundle 的配置信息、依赖关系和其他属性。

plist 文件的格式,系统支持3种格式的 plist。分别是:XML/JSON/二进制格式–BPlists。
除了 JSON使用的较少,其他2种较常见。

工具:plutil 可以将 plist 转换成 不同的格式。
plutil -convert xml1 --o - <Info.plist> output.Info.plist

plist 的内容(p28):

Key 描述/Value
CFBundleDisplayName 显示给用户的 Bundle 名称
。。 。。

列表中所有的 key 都是 CF 开发,表示这些 key 由 Core Foundation框架定义并处理。

类似的:Cocoa app 包含 NS 开头的 key, NSxxx定义的功能包括:是否允许脚本操作、Java需要、系统偏好设置等等。大部分 NSxxx 只能用于 OSX 不能在 iOS上使用。

其他格式的文件

这些文件在 Resources 目录下。

  1. NIB 文件, 后缀是.nib。该文件是二进制的 plist 文件,其中保存了应用程序的 GUI 组件的位置和设置信息。
  2. lproj 文件, 后缀是.lproj。 支持国际化的文件,每种语言对应一个.lproj 的文件。
  3. icns 图标文件
  4. CodeResources文件,签过文件。 这是一个符号链接,指向_CodeSignature/CodeResources。此文件包含 bundle 中所有其他文件的列表。 key是文件名,valuse 是文件的 bash64.

加载默认应用程序

当点击一个 文件时,系统会自动使用某个 APP 打开程序。实现文件类型 和默认应用程序的绑定是由 LaunchServices 框架实现(次框架和 launchd 没有关系),此框架是 CoreServices 框架的一部分。

/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/

使用 LaunchServices 提供的工具 lsregister 可以查看文件类型的注册信息。
cd /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/
./lsregister -dump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
➜ Support ./lsregister -dump | more
Checking data integrity......done.
Status: Database is seeded.
Status: Preferences are loaded.
Seeded System Version: 10.12.3 (16D32)
CacheGUID: 9E923EE5-DAEF-44B8-8B13-3EAAFDB11DEF
CacheSequenceNum: 8712
Path: /var/folders/fj/5bqzvxbd15s86mvc3kc44ypw0000gn/0/com.apple.LaunchServices-175-v2.csstore
DB Object: <LSDatabase 0x7fd904800000> { path = '/var/folders/fj/5bqzvxbd15s86mvc3kc44ypw0000gn/0/com.apple.LaunchServices-175-v2.csstore' }
Store Object: <CSStore 0x7fd902f00da0> { p = 0x10d800000, length = 6508544/6506368/6484712 }
+++++++++++++++++++++ MEMORY USAGE +++++++++++++++++++++
sizeof(Data): 100 ( 100 bytes) --------
sizeof(Table): 80 ( 80 bytes) --------
sizeof(Unit): 8 ( 8 bytes) --------
sizeof(IdentifierCache): 4 ( 4 bytes) --------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Data: 2222152 ( 2.2 MB) 8397 u
Container: 288 ( 288 bytes) 8 u
HandlerPref: 5000 ( 5 KB) 50 u
BindableKeyMap: 130576 ( 131 KB) 1 u
PluginProtocolBinding: 4096 ( 4 KB) 1 u
Type: 147288 ( 147 KB) 1938 u
UTIBinding: 32656 ( 33 KB) 1 u
Bundle: 449152 ( 449 KB) 1276 u
Alias: 1352372 ( 1.4 MB) 1383 u
PluginUUIDBinding: 4096 ( 4 KB) 1 u
Service: 5616 ( 6 KB) 108 u
ActivityTypeBinding: 32656 ( 33 KB) 1 u
String: 261136 ( 261 KB) 1 u
...
Container mount state: mounted
bundle id: 5164
Mach-O UUIDs: 75CF346E-BF45-3B11-A064-5A90985162E9
sequenceNum: 5164
FamilyID: 0
PurchaserID: 0
DownloaderID: 0
installType: 0
appContainer: #
dataContainer: #
path: /private/var/folders/fj/5bqzvxbd15s86mvc3kc44ypw0000gn/C/com.apple.DeveloperTools/All/Xcode/EmbeddedAppDeltas/HelloWorld-OC.app.xDWFUc/HelloWorld-OC.app_sparse.ipa/Payload/HelloWorld-OC.app
name: HelloWorld-OC
displayName: (null)
itemName: (null)
teamID: VYDSDANL33
staticSize: 0
storeFront: 0
versionID: 0
sourceAppID: (null)
ratingLabel: (null)
ratingRank: 0
category: (null)
2ry category: (null)
....

框架

框架也是一种 bundle 格式。框架是不可移植的,是苹果系统特有的。大部分框架是闭源的,框架提供了完整的运行时接口,隐藏底层的系统和库。

框架的内容:

1
2
3
4
5
6
7
8
9
CodeResources/
Headers -> Versions/Current/Headers
IOKit -> Versions/Current/IOKit
Resources -> Versions/Current/Resources
Versions/
XPCServices -> Versions/Current/XPCServices
module.map
Current/ 框架首选的版本的符号链接
framework-name 框架首选的二进制文件的符号链接

OSX 和 iOS 的 GCC 支持 -framework 选项,此选项可以包含任何依赖的框架(第三方框架+系统框架)。
此选项 可以指向头文件 或者 库文件。

框架搜索路的环境变量:

框架保存的位置:

  • /System/Library/Frameworks/ 系统框架
  • /Library/Frameworks 第三方框架
  • ~/Library/Frameworks 用户提供的框架

重要的框架

  1. Cocoa: 首选的应用程序编程环境
    支持的语言:Object-C, java, AppleSCript。

位置: /System/Library/Frameworks/Cocoa.framework/

此框架依赖于:Foundation、AppKit、CoreData

1
2
3
4
5
Versions/Current/Headers/Cocoa.h
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <CoreData/CoreData.h>

保护伞框架(umbrella):封装了其他框架的框架。
封装有2种:

  1. 只是 #import 。 如 Cocoa
  2. 嵌套了其他框架。 如 Application Service

2.Carbon: OS9 遗留编程接口的名称

MAC OSX 和 iOS的公共框架列表

表2-2 (p35)

Mac 包含多套 API 可用。
包括:

  1. BSD api 方便移植 unix的代码库 (库 Api)
  2. Mach Trap(Mach 陷阱) OSX 特有的 Api
  3. 框架 Api

1-2 可以使用C/C++ 开发
3 框架Api 有些必须使用OC开发,比如cocoa, 有些框架可以使用C/C++开发,比如 Carbon

一般框架代码会同时支持cocoa和Carbon, 比如,访问语音合成器的SpeechSynthesis.Framework框架同时位于 ApplicationServices框架(Carbon,C/C++实现)和AppKit(对于Coacoa, OC实现)

系统调用

OSX 的特殊之处在 OSX 的系统调用提供了2套调用接口。

  • POSIX 调用
  • mach 调用
  1. POSIX 调用

这是一套 编程标准,专门负责定义API声明(具体到参数类型,格式,返回值,函数名等等)。
当前的POSIX主要分为四个部分:Base Definitions、System Interfaces、Shell and Utilities和Rationale

OSX提供的系统调用,在 系统调用原型继承了POSIX标准,但是 系统编号OSX和标准的编号不同。
在 unistd.h 定义了系统调用原型。

如果系统调用原型和系统编号都一样,那么就可以在多台机型上移植二进制程序(机型必须要支持对应的二进制格式,OSX不支持ELF格式)
BSD 层对 mach做了一次封装。

当前的BSD操作系统变种支持各种通用标准,包括IEEE、ANSI、ISO以及POSIX,同时保持传统BSD的良好风范。

  1. mach接口
    32位,mach系统调用号 是负数, POSIX 的系统调用号时 非负数,所以2者共存。
    64位,mach系统调用号 从0x2000000 开头,POSIX以 0x1000000 开头。

总结BSD和mach的系统调用:
32位libSystem.B.dylib库,32位系统调用的指令是 sysenter, 负责系统调用的函数是 __sysenter_trap。

1
2
3
4
5
6
7
8
9
// mach系统调用
movl $0xffffffe5 , %eax ;系统调用号(负数, 有符号)
call __sysenter_trap ;
__sysenter_trap:
pop %edx
movl %esp, %ecx
sysenter  ;执行 系统调用 指令
nopl (%eax)

1
2
3
4
5
// BSD 系统调用
movl $0x000c0010, %eax ; 系统调用号(非负)
call 0x00000dd8 ;跳转到 __sysenter_trap
jae 0x0005d36a ;
...

64 位系统调用的指令是 syscall

1
2
3
4
// mach系统调用
movq %rcx, %r10
movl %0x0100001a, %eax ;系统调用号
syscall ;

1
2
3
4
// BSD系统调用
movl $0x02000010, %eax
movq %rcx, %r10
syscall ;

发现不管是32位还是64位除了系统调用号不同,其他的地方没有区别(寄存器使用 和 系统调用命令)

系统调用指令 systenter 具体干什么事?

XNU

xnu 是一个混合内核,xnu由以下几部分构成:

  • Mach微内核
  • BSD 层
  • libKern
  • I/O Kit

同时 内核允许动态加载 扩展插件(Kernel Extension , KExt)

Mach 

提供了 系统的基本职责, 但是 Mach本身的Api很少。

  • 进程和线程的抽象
  • 虚拟内存
  • 任务调度
  • IPC 和 消息传递的机制

BSD

这一层是对Mach 的封装,并且兼容POSIX 标准。包括的内容:

  • UNIX 进程模型
  • POSIX 线程模型 (Pthread), 同步原语
  • UNIX 用户和用户组
  • 网络协议栈
  • 文件系统访问
  • 设备访问 (/dev 下的驱动设备)

libkern

I/O Kit驱动程序 使用C++语言编写。 libkern 库负责提供C++运行时需要的基类。
libkern 是自包含的C++库,提供了C++特性,继承,重载,

I/O kit

I/O Kit 本身就是一套自包含的系统。

个人总结

第二章结束了,对OSX 和iOS 的 架构关键组件有了基本认识。同时 对本书的结构有了大体的掌握。
通过分析上面2点已知的内容,可以快速定位 需要的知识,帮助完成突发的工作需求。

第一部分(1-7章) 第二部分(8-19章)
描述 1 分散的知识点 系统的知识
描述 2 Apple系统的特色,比如:安全防护技术(3),Mach-O 可执行格式(4),引导过程(6)。 内核特性。
描述 3 还有操作系统中 重要且必备的模块,比如: 进程,调试,启动进程init/Launchd。 主要介绍构成 XNU 的4个部分。目前的重点 放在第二部分,重点放在 理解XNU 各部分的代码实现

学习的顺序,时间安排,记录的标准。这三个指标需要 动态调整。
注意: 记录重要的代码 位置,功能,函数
xmind

Todo:

1. 从xnu开源代码中梳理出这4个部分

Change Log

Time Change
2017-3-13 2.7 节
2017-03-14 第二章 结束
2017-03-14 制定 xnu 学习计划