android管道
㈠ Carson帶你學Android:全面剖析Binder跨進程通信原理
從而全方位地介紹 Binder ,希望你們會喜歡。
在本文的講解中,按照 大角度 -> 小角度 去分析 Binder ,即:
從而全方位地介紹 Binder ,希望你們會喜歡。
在講解 Binder 前,我們先了解一些 Linux 的基礎知識
具體請看文章: 操作系統:圖文詳解 內存映射
Binder 跨進程通信機制 模型 基於 Client - Server 模式
此處重點講解 Binder 驅動作用中的跨進程通信的原理:
原因:
所以,原理圖可表示為以下:
所以,在進行跨進程通信時,開發者只需自定義 Client & Server 進程 並 顯式使用上述3個步驟,最終藉助 Android 的基本架構功能就可完成進程間通信
注冊服務後, Binder 驅動持有 Server 進程創建的 Binder 實體
此時, Client 進程與 Server 進程已經建立了連接
Client 進程 根據獲取到的 Service 信息( Binder 代理對象),通過 Binder 驅動 建立與 該 Service 所在 Server 進程通信的鏈路,並開始使用服務
步驟1: Client 進程 將參數(整數a和b)發送到 Server 進程
步驟2: Server 進程根據 Client 進要求 調用 目標方法(即加法函數)
步驟3: Server 進程 將目標方法的結果(即加法後的結果)返回給 Client 進程
對比 Linux ( Android 基於 Linux )上的其他進程通信方式(管道、消息隊列、共享內存、
信號量、 Socket ), Binder 機制的優點有:
特別地,對於從模型結構組成的Binder驅動來說:
不定期分享關於 安卓開發 的干貨,追求 短、平、快 ,但 卻不缺深度 。
㈡ Android四大組件-ContentProvide
Android四大組件 Activity 、 Service 、 BroadcastReceiver 、 ContentProvide
1.什麼是ContentProvide
ContentProvider是Android中提供的專雹返門用於不同應用間數據交互和共享的組件。其本質上是一個標准化的數據管道,它屏冊肆埋蔽了底層的數據管理和服務等細節,以標准化的方式在Android 應用間共享數據、數據交互,跨進程通信。
2.使用方州螞法
1、在當前應用自定義ContentProvider類
2、在AndroidManifest.xml中進行注冊
3、其他應用使用ContentResolver對數據進行CRUD操作
3.其他相關類
4.應用場景
㈢ Android之ContentProvider使用
ContentProvider是Android四大組件之一,其本質上是一個標准化的數據管道,它屏蔽了底層的數據管理和服務等細節,以標准化的方式在Android 應用間共享數據。用戶可以靈活實現ContentProvider所封裝的數據存儲以及增刪改查等,所有的ContentProvider 必須實現一個對外統一的介面(URI)。
使用的時候需要在androidmanifest.xml文件中注冊provider。
在androidmanifest.xml中注冊provider需要以下3個屬性:
㈣ 手機管道計算軟體有哪些
管道薯租計算工具》是一款生活實用類軟體,運行環境支持Android1.6。
軟體名稱
管道計算工具
軟體平台
mobile
軟體版本
1.2
運行環境
Android 1.6
應用類型
生活實用類
應用介紹
用數茄兆手機計算鋼管道的流速、保溫層體積、管道面積、金屬重量和容積,通過選擇SH的SCH查取納好壁厚,快速方便,也適用於小屏
㈤ 033 Android多進程-共享內存
要使用一塊共享內存
還是先看共享內存的使用方法,我主要介紹兩個函數:
通過 shmget() 函數申請共享內存,它的入參如下
通過 shmat() 函數將我們申請到的共享內存映射到自己的用戶空間,映射成功會返回地址,有了這個地址,我們就可以隨意的讀寫數據了,我們繼續看一下這個函數的入參
共享內存的原理是在內存中單獨開辟的一段內存空間,這段內存空間其實就是一個tempfs(臨時虛擬文件),tempfs是VFS的一種文件系統,掛載在/dev/shm上,前面提到的管道pipefs也是VFS的一種文件系統。
由於共享的內存空間對使用和接收進程來講,完全無感知,就像是在自己的內存上讀寫數據一樣,所以也是 效率最高 的一種IPC方式。
上面提到的IPC的方式都是 在內核空擾灶間中開辟內存來存儲數據 ,寫數據時,需要將數據從用戶空間拷貝到內核空間,讀數據時,需要從內核空間拷貝到自己的用戶空間,
共享內存就只需要一次拷貝 ,而且共享內存不是在內核開辟空間,所以可以 傳輸的數據量大 。
但是 共享內存最大的缺點就是沒有並發的控制,我們一般通過信號量配合共享內存使用,進行同步和並發的控制 。
共享內存在Android系統中主要的使用場景是 用來傳輸大數據 ,並且 Android並沒有直接使用Linux原生的共享內存方式,而是設計了Ashmem匿名共享內存 。
之前說到有名管道和匿名管道的區別在於有名管道可以在vfs目錄樹中查看到這個管道的文件,但是匿名管道不行, 所以匿名共享內存同樣也是無法在vfs目錄中查看到 的, Android之所以要設計匿名共享內存 ,我覺得主要是為了安全性的考慮吧。
我們來看看共享內存的一個使用場景,在Android中,如果蠢卜我們想要將當前的界面顯示出來,需要將當前界面的圖元數據傳遞Surfaceflinger去做圖層混合,圖層混合之後的數據會直接送入幀緩存,送入幀緩存後,顯卡就會直接取出幀緩存里的圖元數據顯示了。
那麼我們如何將應用的Activity的圖元數據傳遞給SurfaceFlinger呢?想要將圖像數據這樣比較大的數據跨進程傳輸,靠binder是不行的,所以這兒便用到匿名共享內存。
從谷歌官方提供的架構圖可以看到,圖元數據是通過BufferQueue傳遞到SurfaceFlinger去的,當我們想要繪制圖像的時候, 需要從BufferQueue中申請一個Buffer,Buffer會調用Gralloc模塊來分配共享內存 當作圖元緩沖區存放我們的圖元數據。
可以看到Android的匿名共享內存是通過 ashmem_create_region() 函數來申請共享內存的,它會在/dev/ashmem下創建一個虛擬文件,Linux原生共享內存是通過shmget()函數,並會在/dev/shm下創建虛擬文件。
匿名共享內存是通過 mmap() 函數將申請到的內存映射到自己的進程空間,而Linux是通過*shmat()函數。
雖然函數不一樣,但是帶李穗Android的匿名共享內存和Linux的共享內存在本質上是大同小異的。
。
㈥ Android Camera2 教程 · 第一章 · 概覽
從 Android 5.0 開始,Google 引入了一套全新的相機框架 Camera2(android.hardware.camera2)並且廢棄了舊的相機框架 Camera1(android.hardware.Camera)。作為一個專門從事相機應用開發的開發者來說,這一刻我等了太久了,Camera1 那寥寥無幾的 API 和極差的靈活性早已不能滿足日益復雜的相機功能開發。Camera2 的出現給相機應用程序帶來了巨大的變革,因為它的目的是為了給應用層提供更多的相機控制許可權,從而構建出更高質量的相機應用程序。本文是 Camera2 教程的開篇作,本章將介紹以下幾個內容:
Camera2 的 API 模型被設計成一個 Pipeline(管道),它按順序處理每一幀的請求並返回請求結果給客戶端。下面這張來自官方的圖展示了 Pipeline 的工作流程,我們會通過一個簡單的例子詳細解釋這張圖。
為了解釋上面的示意圖,假設我們想要同時拍攝兩張不同尺寸的圖片,並且在拍攝的過程中閃光燈必須亮起來。整個拍攝流程如下:
一個新的 CaptureRequest 會被放入一個被稱作 Pending Request Queue 的隊列中等待被執行,當 In-Flight Capture Queue 隊列空閑的時候就會從 Pending Request Queue 獲取若干個待處理的 CaptureRequest,並且根據每一個 CaptureRequest 的配置進行 Capture 操作。最後我們從不同尺寸的 Surface 中獲取圖片數據並且還會得到一個包含了很多與本次拍照相關的信息的 CaptureResult,流程結束。
相機功能的強大與否和硬體息息相關,不同廠商對 Camera2 的支持程度也不同,所以 Camera2 定義了一個叫做 Supported Hardware Level 的重要概念,其作用是將不同設備上的 Camera2 根據功能的支持情況劃分成多個不同級別以便開發者能夠大概了解當前設備上 Camera2 的支持情況。截止到 Android P 為止,從低到高一共有 LEGACY、LIMITED、FULL 和 LEVEL_3 四個級別:
相機的所有操作和參數配置最終都是服務於圖像捕獲,例如對焦是為了讓某一個區域的圖像更加清晰,調節曝光補償是為了調節圖像的亮度。因此,在 Camera2 裡面所有的相機操作和參數配置都被抽象成 Capture(捕獲),所以不要簡單的把 Capture 直接理解成是拍照,因為 Capture 操作可能僅僅是為了讓預覽畫面更清晰而進行對焦而已。如果你熟悉 Camera1,那你可能會問 setFlashMode() 在哪? setFocusMode() 在哪? takePicture() 在哪?告訴你,它們都是通過 Capture 來實現的。
Capture 從執行方式上又被細分為【單次模式】、【多次模式】和【重復模式】三種,我們來一一解釋下:
CameraManager 是一個負責查詢和建立相機連接的系統服務,它的功能不多,這里列出幾個 CameraManager 的關鍵功能:
CameraCharacteristics 是一個只讀的相機信息提供者,其內部攜帶大量的相機信息,包括代表相機朝向的 LENS_FACING ;判斷閃光燈是否可用的 FLASH_INFO_AVAILABLE ;獲取所有可用 AE 模式的 CONTROL_AE_AVAILABLE_MODES 等等。如果你對 Camera1 比較熟悉,那麼 CameraCharacteristics 有點像 Camera1 的 Camera.CameraInfo 或者 Camera.Parameters 。
CameraDevice 代表當前連接的相機設備,它的職責有以下四個:
熟悉 Camera1 的人可能會說 CameraDevice 就是 Camera1 的 Camera 類,實則不是,Camera 類幾乎負責了所有相機的操作,而 CameraDevice 的功能則十分的單一,就是只負責建立相機連接的事務,而更加細化的相機操作則交給了稍後會介紹的 CameraCaptureSession。
Surface 是一塊用於填充圖像數據的內存空間,例如你可以使用 SurfaceView 的 Surface 接收每一幀預覽數據用於顯示預覽畫面,也可以使用 ImageReader 的 Surface 接收 JPEG 或 YUV 數據。每一個 Surface 都可以有自己的尺寸和數據格式,你可以從 CameraCharacteristics 獲取某一個數據格式支持的尺寸列表。
CameraCaptureSession 實際上就是配置了目標 Surface 的 Pipeline 實例,我們在使用相機功能之前必須先創建 CameraCaptureSession 實例。一個 CameraDevice 一次只能開啟一個 CameraCaptureSession,絕大部分的相機操作都是通過向 CameraCaptureSession 提交一個 Capture 請求實現的,例如拍照、連拍、設置閃光燈模式、觸摸對焦、顯示預覽畫面等等。
CaptureRequest 是向 CameraCaptureSession 提交 Capture 請求時的信息載體,其內部包括了本次 Capture 的參數配置和接收圖像數據的 Surface。CaptureRequest 可以配置的信息非常多,包括圖像格式、圖像解析度、感測器控制、閃光燈控制、3A 控制等等,可以說絕大部分的相機參數都是通過 CaptureRequest 配置的。值得注意的是每一個 CaptureRequest 表示一幀畫面的操作,這意味著你可以精確控制每一幀的 Capture 操作。
CaptureResult 是每一次 Capture 操作的結果,裡麵包括了很多狀態信息,包括閃光燈狀態、對焦狀態、時間戳等等。例如你可以在拍照完成的時候,通過 CaptureResult 獲取本次拍照時的對焦狀態和時間戳。需要注意的是,CaptureResult 並不包含任何圖像數據,前面我們在介紹 Surface 的時候說了,圖像數據都是從 Surface 獲取的。
如果要我給出強有力的理由解釋為什麼要使用 Camera2,那麼通過 Camera2 提供的高級特性可以構建出更加高質量的相機應用程序應該是最佳理由了。
如果你熟悉 Camera1,並且打算從 Camera1 遷移到 Camera2 的話,希望以下幾個建議可以對你起到幫助:
本章到此結束,主要是介紹了 Camera2 的一些基礎概念,讓大家能夠基本了解 Camera2 的工作流程和基礎概念,並且知道使用 Camera2 能夠做些什麼。如果你對 Camera2 還是感到很陌生,不要緊,後續的教程會帶領大家逐步深入了解 Camera2。
㈦ Android面試必問handler機制淺析
Handler是Android中的非同步消息處理機制。當發送一個消息之後,這個消息是進入一個消息隊列(MessageQueue),在消息隊列中通過Looper去循環的獲取隊列中的消息,然後將消息分派給對應的處理者進行處理。
Message:存儲需要處理操作的信息
MessageQueue:先進先出,存儲handler發送過來的消息
Looper:循環器,它是消息隊列和handler的通信媒介,1:循環的取出消息隊列中的消息;2:將取出的消息發送給對應的處理者
Handler:主線程和子線程的通信媒介,1:添加消息到消息隊列; 2:處理循環器分派過來的消息
在handler機制中,Looper.loop方法會不斷循環獲取Message, 其中的消息的獲取是通過調用MessageQueue的next()方法獲取的,而該方法會調用nativePollOnce()方法 ,這是一個native方法。底層的實現涉及到Linux pipe/epoll機制,nativePollOnce()被阻塞時,主線程會釋放CPU資源,進入休眠狀態. 直到下個消息到達或者有事務發生,會通過pipe管道寫端寫入數據來喚醒looper工作。
Android6.0及以前的版本使用管道與epoll來完成Looper的休眠與喚醒的
Android6.0及以後的版本使用eventfd與epoll來完成Looper的休眠與喚醒的
如果不處理的話,會阻塞線程,處理方案是調用Looper的quit()(清空所有的延遲和非延遲的消息)和quitSafely()(只清空延遲消息); 這個方法會調用MessageQueue的quit()方法,清空所有的Message,並調用nativeWake()方法喚醒之前被阻塞的nativePollOnce(),使得方法next()方法中的for循環繼續執行,接下來發現Message為null後就會結束循環,Looper結束。如此便可以釋放內存和線程
同進程線程間內存共享,通過handler通信,消息的內容是不需要從一個線程拷貝到另一個線程,因為兩個線程間可使用的內存是同一個區域。(注意:線程私有區域ThreadLocal)
管道的作用就是當一個線程准備好Message,並放入消息池,這時需要通知了一個線程B去處理這個消息。線程A向管道的寫端寫入數據,管道有數據便會喚醒線程B去處理消息。管道的作用是用於通知另一個線程的,這便是最核心的作用。
從內存角度,通信過程中binder涉及到一次內存拷貝,handler機制中的Message根本不需要拷貝,本身就是在同一片內存。
從CPU角度,為了Binder通信底層驅動還需要創建一個binder線程池,每次通信涉及binder線程的創建和內存的分配等比較浪費CPU資源
原因:handler發送的消息在當前handler的消息隊列中,如果此時activity被finish掉了,那麼消息隊列的消息依舊由handler進行處理,若此時handler申明為內存類(非靜態內部類),內部類持有外部類的實例引用,這樣在GC垃圾回收時發現Activity還有其他引用存在,因而就不會去回首這個Activity,進而導致Activity泄漏。
方法:使用靜態內部類,並且使用WeakReference包裹外部類的對象。首先靜態內部類不持有外部類的引用,使用靜態的handler不會導致activity的泄漏,handler定義static的同時,還要用WeakReference包裹外部類的對象。
㈧ 如何創建命名管道在Android的
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("廳清悔喊tel:12345678"扮前前 );
startActivity(callIntent);
㈨ 淺談Android之Linux pipe/epoll
管道
管道的概念:
管道是一種最基本的IPC機制,作用於有血緣關系的進程之間臘仔掘,完成數據傳遞。調用pipe系統函數即可創建一個管道。有如下特質:
1. 其本質是一個偽文件(實為內核緩沖區)
2. 由兩個文件描述符引用,一個表示讀端,一個表示寫端。
3. 規定數據從管道的寫端流入管道,從讀端流出。
管道的原理: 管道實為內核使用環形隊列機制,藉助內核緩沖區(4k)實現。
管道的局限性:
① 數據自己讀不能自己寫。
② 數據一旦被讀走,便不在管道中存在,不可反復讀取。
③ 由於管道採用半雙工通信方式。因此,數據只能在一個方向上流動。
④ 只能在有公共祖先的進程間使用管道。
常見的通信方式有,單工通信、半雙工通信、全雙工通信。
簡單來說這個管道是一個文件,但又和普通輪核文件不通:管道緩沖區大小一般為1頁,即4K位元組,管道分讀端和寫端,讀端負責從管道拿數據,當數據為空時則阻塞;寫端向管道寫數據,當管道緩存區滿時則阻塞。
pipe函數
創建管道
int pipe(int pipefd[2]); 成功:0;失敗:-1,設置errno
函數調用成功返回r/w兩個文件描述符。無需open,但需手動close。規定:fd[0] → r; fd[1] → w,就像0對應標准輸入,1對應標准輸出一樣。向管道文件讀寫數據其實是在讀寫內核緩沖區。
管道創建成功以後,創建該管道的進程(父進程)同時掌握著管道的讀端和寫端。如何實現父子進程間通信呢?通常可以採用如下步驟:
1. 父進程調用pipe函數創建管道,得到兩個文件描述符fd[0]、fd[1]指向管道的讀端和寫端。
2. 父進程調用fork創建子進程,那麼子進程也有兩個文件描述符指向同一管道。
3. 父進程關閉管道讀端,子進程關閉管道寫端。父進程可以向管道中寫入數據,子進程將管道中的數據讀出。由於管道戚芹是利用環形隊列實現的,數據從寫端流入管道,從讀端流出,這樣就實現了進程間通信。
管道的讀寫行為
使用管道需要注意以下4種特殊情況(假設都是阻塞I/O操作,沒有設置O_NONBLOCK標志):
1. 如果所有指向管道寫端的文件描述符都關閉了(管道寫端引用計數為0),而仍然有進程從管道的讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會返回0,就像讀到文件末尾一樣。
2. 如果有指向管道寫端的文件描述符沒關閉(管道寫端引用計數大於0),而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會阻塞,直到管道中有數據可讀了才讀取數據並返回。
3. 如果所有指向管道讀端的文件描述符都關閉了(管道讀端引用計數為0),這時有進程向管道的寫端write,那麼該進程會收到信號SIGPIPE,通常會導致進程異常終止。當然也可以對SIGPIPE信號實施捕捉,不終止進程。具體方法信號章節詳細介紹。
4. 如果有指向管道讀端的文件描述符沒關閉(管道讀端引用計數大於0),而持有管道讀端的進程也沒有從管道中讀數據,這時有進程向管道寫端寫數據,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據並返回。
總結:
① 讀管道: 1. 管道中有數據,read返回實際讀到的位元組數。
2. 管道中無數據:
(1) 管道寫端被全部關閉,read返回0 (好像讀到文件結尾)
(2) 寫端沒有全部被關閉,read阻塞等待(不久的將來可能有數據遞達,此時會讓出cpu)
② 寫管道: 1. 管道讀端全部被關閉, 進程異常終止(也可使用捕捉SIGPIPE信號,使進程不終止)
2. 管道讀端沒有全部關閉:
(1) 管道已滿,write阻塞。
(2) 管道未滿,write將數據寫入,並返回實際寫入的位元組數。
Epoll的概念
Epoll可以使用一次等待監聽多個描述符的可讀/可寫狀態.等待返回時攜帶了可讀的描述符或者自定義的數據.不需要為每個描述符創建獨立的線程進行阻塞讀取,
Linux系統中的epoll機制為處理大批量句柄而作了改進的poll,是Linux下多路復用IO介面select/poll的增強版本,它能顯著減少程序在大量並發連接中只有少量活躍的情況下的系統CPU利用率
(01) pipe(wakeFds),該函數創建了兩個管道句柄。
(02) mWakeReadPipeFd=wakeFds[0],是讀管道的句柄。
(03) mWakeWritePipeFd=wakeFds 1 ,是寫管道的句柄。
(04) epoll_create(EPOLL_SIZE_HINT)是創建epoll句柄。
(05) epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem),它的作用是告訴mEpollFd,它要監控mWakeReadPipeFd文件描述符的EPOLLIN事件,即當管道中有內容可讀時,就喚醒當前正在等待管道中的內容的線程。
回到Android中的epoll大致流程如下:
Looper.loop -> MessageQueue.nativePollOnce
epoll_create() epoll_ctl() 注冊事件的回調
looper.pollInner() -> epoll_wait() 等待接受事件喚醒的回調
MessageQueue.enqueueMessage(Message msg, long when) -> MessageQueue.nativeWake(long ptr)
參考鏈接如下
鏈接:https://www.jianshu.com/p/8656bebc27cb
鏈接:https://blog.csdn.net/oguro/article/details/53841949
㈩ Android通信方式篇(七)-Binder機制(Native層(下))
本篇文章針對向ServiceManager注冊服務 和 獲取服務兩個流程來做總結。在這兩個過程中,ServiceManager都扮演的是服務端,與客戶端之間的通信也是通過Binder IPC。
在此之前先了解下Binder的進程與線程的關系:
用戶空間 :ProcessState描述一個進程,IPCThreadState對應一個進程中的一個線程。
內核空間 :binder_proc描述一個進程,統一由binder_procs全局鏈表保存,binder_thread對應進程的一個線程。
ProcessState與binder_proc是一一對應的。
Binder線程池 :每個Server進程在啟動時會創建一個binder線程池,並向其中注冊一個Binder線程;之後Server進程也可以向binder線程池注冊新的線程,或者Binder驅動在探測到沒有空閑binder線程時會主動向Server進程注冊新的的binder線程。對於一個Server進程有一個最大Binder線程數限制15,(#define DEFAULT_MAX_BINDER_THREADS 15)。對於所有Client端進程的binder請求都是交由Server端進程的binder線程來處理的。我的理解是:binder線程是進程進行binder ipc時的一條數據處理路徑。
MediaPlayerService向ServiceManager注冊過程如下:
相關類:
整個過程總結如下:
1 獲取BpServiceManager 與 BpBinder
由defaultServiceManager()返回的是BpServiceManager,同時會創建ProcessState對象和BpBinder對象。然後通過BpBinder執行transact,把真正工作交給IPCThreadState來處理。
2 BpBinder transact
Binder代理類調用transact()方法,真正工作還是交給IPCThreadState來進行transact工作。
3 通過IPCThreadState 包裝並轉換數據並進行transact事務處理
每個線程都有一個IPCThreadState,每個IPCThreadState中都有一對Parcel變數:mIn、mOut。相當於兩根數據管道:
最後執行talkWithDriver。
writeTransactionData:將BC Protocol + binder_transaction_data結構體 寫入mOut, 然後執行waitForResponse:
由talkWithDriver將數據進一步封裝到binder_write_read結構體,通過ioctl(BINDER_WRITE_READ)與驅動通信。同時等待驅動返回的接收BR命令,從mIn取出返回的數據。
mIn包裝的數據結構(注冊服務handle = 0 ,code 為ADD_SERVICE_TRANSACTION):
4 Binder Driver
把binder_write_read結構體write_buffer里數據取出來,分別得到BC命令和封裝好數據的事務binder_transaction_data, 然後根據handler,在當前binder_proc中,找到相應的binder_ref,由binder_ref再找到目標binder_node實體,由目標binder_node再找到目標進程binder_proc。然後就是插入數據:當binder驅動可以找到合適的線程,就會把binder_transaction節點插入到servciemanager的線程的todo隊列中,如果找不到合適的線程,就把節點之間插入servciemanager的binder_proc的todo隊列。
5 ServiceManager
經過Binder Driver的處理,數據已經到了ServiceManager進程,在BR_TRANSACTION的引導下,在binder_loop()中執行binder_parser()取出數據,執行do_add_service()操作,最終向 svcinfo 列表中添加已經注冊的服務(沒有數據的返回)。最後發送 BR_REPLY 命令喚醒等待的線程,通知注冊成功。結束MediaPlayerService進程 waitForResponse()的狀態,整個注冊過程結束。
獲取服務的過程與注冊類似,首先 ServiceManager 向 Binder 驅動發送 BC_TRANSACTION 命令攜帶 CHECK_SERVICE_TRANSACTION 命令,同時獲取服務的線程進入等待狀態 waitForResponse()。Binder 驅動收到請求命令向 ServiceManager 的發送 BC_TRANSACTION 查詢已注冊的服務,會區分請求服務所屬進程情況。
查詢到直接響應 BR_REPLY 喚醒等待的線程。若查詢不到將與 binder_procs 鏈表中的服務進行一次通訊再響應。
以startService為例來簡單總結下執行流程:
3.1 從方法執行流程來看:
Client :
1 AMP.startService 標記方法以及通過Parcel包裝數據;
2 BinderProxy.transact 實際調用native的 android_os_BinderProxy_transact 傳遞數據;
3 獲取BpServiceManager 與 BpBinder 同時會創建ProcessState。然後通過BpBinder執行transact,把真正工作交給IPCThreadState來處理;
4 IPC.transact 主要執行writeTransactionData,將上層傳來的數據重新包裝成binder_transaction_data,並將BC Protocol + binder_transaction_data結構體 寫入mOut;
5 IPC waitForResponse talkWithDriver + 等待返回數據;
6 talkWithDriver 將數據進一步封裝成binder_write_read,通過ioctl(BINDER_WRITE_READ)與驅動通信;
Kernel :
7 binder ioctl 接收BINDER_WRITE_READ ioctl命令;
8 binder_ioctl_write_read 把用戶空間數據ubuf拷貝到內核空間bwr;
9 binder_thread_write 當bwr寫緩存有數據,則執行binder_thread_write;當寫失敗則將bwr數據寫回用戶空間並退出;
10 binder_transaction 找到目標進程binder_proc並插入數據到目標進程的線程todo隊列,最終執行到它
時,將發起端數據拷貝到接收端進程的buffer結構體;
11 binder_thread_read 根據binder_transaction結構體和binder_buffer結構體數據生成新的binder_transaction_data結構體,寫入bwr的read_buffer,當bwr讀緩存有數據,則執行binder_thread_read;當讀失敗則再將bwr數據寫回用戶空間並退出;最後,把內核數據bwr拷貝到用戶空間ubuf。
12 binder_thread_write + binder_ioctl BR命令和數據傳遞
Server:
13 IPC.executeCommand 解析kernel傳過來的binder_transaction_data數據,找到目標BBinder並調用其transact()方法;
14 IPC.joinThreadPool 採用循環不斷地執行getAndExecuteCommand()方法, 處理事務。當bwr的讀寫buffer都沒有數據時,則阻塞在binder_thread_read的wait_event過程. 另外,正常情況下binder線程一旦創建則不會退出.
15 BBinder.transact 到Binder.exeTransact 調用 AMN.onTransact
16 AMN.onTransact 把數據傳遞到AMS.starService去執行
17 AMS.starService Server處理了Client的請求了
然後原路replay回去,talkWithDriver 到Kernel ,然後找到Client進程,把數據拷貝到read_buffer里,最終喚醒IPC,把反饋傳遞回AMP.startService。完成啟動服務。
3.2 從通信協議流程來看:
非oneWay:
oneway:
oneway與非oneway區別: 都是需要等待Binder Driver的回應消息BR_TRANSACTION_COMPLETE. 主要區別在於oneway的通信收到BR_TRANSACTION_COMPLETE則返回,而不會再等待BR_REPLY消息的到來. 另外,oneway的binder IPC則接收端無法獲取對方的pid.
3.3 從數據流來看
從用戶空間開始:
進入驅動後:
回到用戶空間:
參考:
http://gityuan.com/2016/09/04/binder-start-service/
http://gityuan.com/2015/11/28/binder-summary/
http://gityuan.com/2015/11/14/binder-add-service/
http://gityuan.com/2015/11/15/binder-get-service/