4.1 計算機體系結構(Computer Architecture)
4.1.1 馮·諾依曼結構
兩個深遠影響的觀點:
-
採用二進制,拋棄十進制
-
程序存儲(stored-program)
4.1.2 哈佛結構
對馮諾依曼結構的改進與完善,區別在指令與數據並不保存在同一個存儲器。
這意味着:
-
指令與數據可以有不同的的數據寬度;
-
執行速度更快。
計算機結構的基本元素:
-
中央處理器(CPU)
-
內存儲器
-
I/O設備
4.2 什麼是操作系統
定義:
計算機操作系統是負責管理系統硬件,併爲上層應用提供穩定編程接口和人機交互界面的軟件集合。
最核心的工作:硬件管理與抽象。
肩負兩大重任:
-
面向下層:管理硬件(CPU、內存、Flash、各種IO設備)
-
面向上層:一方面,爲用戶提供可用的人機交互界面;另一方面,負責爲第三方程序的研發提供便捷、可靠、高效的API。
操作系統的難點:進程和內存管理、硬件驅動的支持等,這正是Linux的長處所在。
4.3 進程間通信的經典實現
進程間的通信(Inter-process communication,IPC)是指運行在不同進程(不論是否在同一臺機器)中的若干線程間的數據交換。
實現方式:消息傳遞、管道、文件共享、操作系統提供的公共信息機制等等。
4.3.1 共享內存(Shared Memory)
一種常用的IPC機制,優勢:共享內存區域,減少數據的複製操作,速度快。
實現步驟:
-
創建內存共享區
-
映射內存共享區
-
訪問內存共享區
-
進程間通信
-
撤銷內存映射區
-
刪除內存共享區
4.3.2 管道(Pipe)
一種常見的IPC方式
-
分立管道的兩邊,進行數據的傳輸通信
-
管道是單向的
-
一根管道同時具有“讀取”端(read end)和“寫入”端(write end)
-
管道有容量限制
4.3.3 UNIX Domain Socket(UDS)
專門針對單機內的進程間通信,有時稱爲IPC Socket。
Network Socket是以TCP/IP協議棧爲基礎,UDS因爲是本地內的“安全可靠操作”,實現機制上並不依賴於這些協議。
典型流程:
UDS的基本流程與傳統Socket一致,只是在參數上有區分:
-
服務器端監聽IPC請求;
-
客戶端發起IPC申請;
-
雙方成功建立起IPC連接;
-
客戶端向服務端發送數據,證明IPC通信是有效的
4.3.4 Remote Procedure Calls(RPC)
涉及的通信雙方通常運行於兩臺不同的機器中。
4.4 同步機制的經典實現
同步:如果多個進程間存在時序關係,需要協同工作以完成一項任務
互斥:如果多個進程並不滿足協同的條件,而只是因爲共享具有排他性的資源時所產生的關係。
4.4.1 信號量(Semaphore)
涉及的元素:信號量(S)、PV原語操作(有時稱wait()、signal())。
S:共享資源的可用數量
P: 減少S的計數(進入共享區的操作)
V:增加S的計數(退出共享區的操作)
4.4.2 Mutex
是Mutual Exclusion的縮寫,即互斥體
4.4.3 管程(Monitor)
一種控制更爲簡單的同步手段。
可以被多個進程/線程安全訪問的對象(object)或模塊(module)。
具備安全性、互斥性、共享性。
4.4.4 Linux Futex
核心優勢是Fast。
4.4.5 同步範例
生產者與消費者問題:
兩個進程共享一塊大小爲N的緩衝區,其中一個進程負責填充數據(生產者),另一個進程負責讀取數據(消費者)。
問題的核心有兩點:
-
當緩衝區滿時,禁止生產者繼續添加數據,直到消費者讀取了部分數據;
-
當緩衝區空時,消費者應等待對方繼續生產後再執行操作。
解決方式:信用量,用到3個Semaphore,功能如下:
-
S_emptyCount: 用於生產者獲取可用的的緩衝空間大小,初始N
-
S_fillCount: 用於消費者獲取可用的數據大小,初始爲0
-
S_mutex: 用於操作緩衝區,初始爲1
生產者的執行步驟:
-
循環開始;
-
Produce_item;
-
P(S_emptyCount)
-
P(S_mutex)
-
Put_item_to_buffer
-
V(S_mutex)
-
V(S_fillCount)
-
繼續循環
消費者的執行步驟:
-
循環開始
-
P(S_fillCount)
-
P(S_mutex)
-
Read_item_from_buffer
-
V(S_mutex)
-
V(S_emptyCount)
-
Consume
-
繼續循環
4.5 Android中的同步機制
4.5.1 進程間同步 - Mutex
頭文件:frameworks/native/include/utils/Mutex.h
既可以處理進程內同步、又可以解決進程間同步。
4.5.2 條件判斷 - Condition
頭文件:frameworks/native/include/utils/Condition.h
核心思想:判斷“條件是否已經滿足”,如果滿足則馬上返回,繼續執行未完成的動作,否則就進行休眠等待,直到條件滿足時有人喚醒它。
4.5.3 “柵欄、障礙” - Barrier
頭文件:frameworks/native/services/surfaceflinger/Barrier.h
Barrier是填充了“具體條件”的Condition,專門爲SurfaceFlinger而設計。
通常被用於對某線程是否初始化完成進行判斷,這種場景具有不可逆性。
4.5.4 加解鎖的自動化操作 - Autolock
在Mutex類內部的嵌套類Autolock,實現加、解鎖的自動化操作。
當Auto構造時,會主動調用內部成員變量mLock的lock()方法獲取一個鎖。
而析構時正好相反,調用它的unlock()方法釋放鎖。
4.5.5 讀寫鎖 - ReaderWriterMutex
基礎仍是mutex,特點是 允許有多個對象共享Read鎖,但同時卻只能有唯一一個對象擁有Write鎖。
4.6 操作系統內存管理基礎
內存管理是操作系統的重點和難點
內存管理重點理解幾個核心:虛擬內存、內存分配與回收、內存保存。
4.6.1 虛擬內存(Virtual Memory)
虛擬內存:爲大體積程序的運行提供了可能。
基本思想:
-
將外存儲器的部分空間作爲內存的擴展
-
當內存資源不足時,系統將按照一定算法自動挑選優先級低的數據塊,並把它們存儲到硬盤中。
-
後續如果需要用到硬盤中的這些數據塊,系統將產生“缺頁”指令,然後把它們交換回內存中。
-
這些操作是由操作系統內部內核自動完成,對上層應用“完全透明”。
涉及3種不同的地址空間:
1、邏輯地址
是程序編譯後所產生的地址,Segment Selector(段選擇子, 16bit) + Offset (偏移值,32bit)
2、線性地址
是邏輯地址經過分段機制轉換後形成的
3、物理地址
是指機器真實的物理內存所能表示的地址空間範圍,64KB內存的物理地址範圍是 0x0000 - 0xFFFF
4.6.2 內存保護(Memory Protection)
4.6.3 內存分配與回收
分Native層(C/C++)、Java層
4.6.4 進程間通信 - mmap
IPC方式: 通過映射同一塊物理內存來共享內存,減少數據複製次數,提高效率。
mmap可以將某個設備或者文件映射到應用進程的內存空間中,這樣訪問這塊內存就相當於對設備/文件進行讀寫,而不需要通過read()、write()。
4.6.5 寫時拷貝技術(Copy on Write)
基本思想:多個對象在起始時共享某些資源(如代碼段、數據段),直到某個對象需要修改該資源才擁有自己的一份拷貝。
4.7 Android中的Low Memory Killer
Linux底層內核的內存監控機制:OOMKiller,核心思想:按照優先級順序,從低到高逐步殺掉進程,回收內存。
優先級的設定策略需綜合以下幾個因素:
-
進程消耗的內存
-
進程佔用的CPU時間
-
oom_adj(OOM權重)
Android擴展出自己的內存監控體系,Linux的“內存殺手”要等到系統資源“瀕臨絕境”的情況下才產生效果,而Android則實現了“不同梯級”的Killer(Low Memory Killer(LMK))。
LMK的源碼在內核工程的driver/staging/android/Lowmemorykiller.c中。
4.8 Android匿名共享內存(Anonymous Shared Memory)
Anonymous Shared Memory(簡稱Ashmem)是Android特有的內存共享機制,可以將制定的物理內存分別映射到各個進程自己的虛擬地址空間中,從而便捷地實現進程間的內存共享。
應用實例:基於ashmem設備來實現跨進程內存共享,如MemoryDealer。
匿名共享內存涉及設備驅動、Binder原理等一系列技術,比較難理解,等學習相關基礎知識,再來攻克。
4.9 JNI
JNI(Java Native Interface)是一種允許運行於JVM的Java程序去調用(反向亦然)本地代碼的編程框架。
有3種情況需要用到JNI:
-
應用程序需要一些平臺相關的feature的支持,而Java無法滿足
-
兼容以前的用其他語言書寫的代碼庫
-
應用程序的某些關鍵操作對運行速度要求較高。
JNI涉及以下兩方面:
-
Java Code -> Native Code
-
Native Code -> Java Code
4.9.1 Java函數的本地實現
創建一個可供Java代碼調用的本地函數步驟:
-
將需要本地實現的Java方法加上native聲明;
-
使用javac命令編譯Java類
-
使用javah生成.h頭文件
-
在本地代碼中實現native方法
-
編譯上述的本地方法,生成動態鏈接庫
-
在Java類中加載這一動態鏈接庫
-
Java代碼中其他地方可以正常調用這個native方法
4.9.2 本地代碼訪問JVM
上節內容的“逆向”操作,本地層(C/C++)訪問JVM空間(Java)。
4.10 Java中的反射機制
4.11 學習Android系統的兩條線索
主線:操作系統的體系結構、硬件組成
輔線:在主線的基礎上,以Android系統的5層框架爲輔,逐一解析各層框架中的重要元素,或拾級而上,或深入淺出,直到問題的最根源處。