binder驅動和內核交互筆記

進程只運行在進程固有的虛擬地址空間, 剩下的1G是內核空間 用戶代碼和相關庫都運行在用戶空間的代碼區域. 兩個進程共享的內核空間  binder driver是通信媒介
ipc由調用服務號,調用函數名,binder協議構成
handle是指服務號, 區分服務, binder driver通過handle值確定binder ipc數據傳遞到哪個服務中
RPC代碼表示待調函數 RPC數據 是傳遞的參數
文件運算符函數  open系統調用到內核binder_open  __open()是系統調用
mmap函數,在內核中開闢一塊區域,存放接收IPC數據. 調用ioctl()方法,將IPC數據作爲參數,傳遞給Binder
        BINDER_WRITE_READ   進程間接收發送Binder IPC數據  struct binder_write_read
        BINDER_SET_MAX_THREADS  設定註冊到binder driver中binder線程的最大個數   size_t
        BINDER_SET_CONTEXT_MGR   設定服務管理者
        BINDER_THREAD_EXIT  刪除Binder線程
        根據Binder協議,決定是否傳輸協議
IPC層傳遞給binder driver的BINDER_COMMAND_PROTOCOL 通過Binder driver向接收端發送IPC數據時使用
binder driver傳遞給ipc層的BINDER_RETURN_PROTOCOL
/home/mec/goldfish/drivers/staging/android/bind.h
驅動收到信息會查詢ipc數據中的handle信息查找相應的Service,然後driver將協議更改爲BR_TRANSACTION,並將其重新插入IPC數據中,然後傳遞到server中  具體調用IPC數據中的哪個函數由ipc數據中的RPC代碼決定
IPC應答數據  BR_REPLY   BC_REPLY
調用一個已經定義好的函數,通常需要提出函數的名稱,並傳遞相應的參數.
binder驅動通過handle查找server,我們稱爲Binder尋址. 爲了順利實現尋址,server一般必須先把自身服務的訪問信息註冊到Manager中.  註冊的過程中 server會向binder驅動傳輸IPC數據,其中包含RPC代碼 ADD_SERVICE RPC數據(註冊服務名稱)
並且handle值設置爲0, service會將IPC數據傳遞給Manager,然後driver驅動會生成一個binder節點.用來表示server中的服務A.
接下來生成binder節點引用數據,以便manager識別binder節點,並將相關節點連接起來,根據生成的順序,引用數據會被編號.這種編號會將插入IPC數據中,傳遞給Manager, manager會根據IPC數據中服務的名稱和BInder節點編號,將服務註冊到自身的服務目錄列表中
manager根據請求的服務名稱 在自身持有的服務列表中查找相對應的服務編號.  驅動將客戶端的引用數據和manager引用數據所指的binder節點連接起來.
manager(接收IPC數據,發送IPC應答數據)中mmap函數在內核中開闢一塊用於接收ipc數據的buffer 再調用ioctl函數進入待機狀態 等待ipc數據.
server((發送IPC數據,接收IPC應答數據)中調用mmap函數在內核中開闢一塊用於接收ipc 應答數據 的buffer  移動的數據是RPC數據和binder協議,ipc數據在內核組裝
服務的編號 就是服務引用
註冊是時候 RPC數據是服務名稱  RPC代碼註冊函數ADD_SERVICE
使用服務   RPC代碼 制定服務函數,RPC數據 服務函數的參數
驅動角度分析  binder_proc open的時候創建   接收IPC數據的buffer 大小爲調用mmap時制定的大小,保存在binder_buffer結構體中  而後binder_buffer結構體註冊到binder_proc
驅動通過handle查找到manager的binder_node和binder_proc結構體,然後爲要註冊的服務,生成binder_node結構體.驅動將binder_node分別註冊到server的binder_proc和manager的binder_proc,(驅動會分別將binder_node註冊到server的binder_proc和manager的binder_proc結構體之中)然後驅動找到manager的binder_buffer發送IPC數據.


進程 buffer發送ipc數據->驅動,驅動通過handle找到服務和binder_proc,然後把數據通過其中的buffer繼續轉發


檢索的時候 是指向manager 請求指定名稱的handle作爲返回值
驅動將服務的binder_node結構體註冊到客戶端的binder_proc


一般server向manager註冊的時候纔會生成binder_node結構體,但是manager直接訪問驅動,生成binder_node.並且該binder_node的編號爲0.


發送請求的時候會在驅動中找到 請求的binder_proc結構體,向其中接收的buffer中複製ipc數據.
請求返回的時候會把服務的binder節點註冊到請求的binder_proc結構體,將ipc應答數據複製到buffer進去,喚醒客戶端傳遞ipc應答數據.
驅動根據manager返回的服務編號handle查找相對於的binder_node結構體,而後將查找到的binder_node結構體註冊到客戶端的binder_proc,binder_node結構體用於在服務使用階段查找server的binder_proc結構體.


服務使用
驅動接收到數據,經過處理,會喚醒一方,然後讓一方進入待機狀態.
函數分析
        進程使用binder驅動第一步就是打開文件binder_open 會初始化binder_proc結構體,初始化待機隊列(wait queue).用來將進程切換到待機狀態下.
        binder_proc稱爲binder驅動的根結構體
mmap將用戶空間的特定區域映射到內核空間的特定區域.  用戶空間中的進程不能直接訪問內核空間,所以只能通過內核空間的特定映射區域來訪問內核空間.
cat pid/maps查看鏈接信息
/home/mec/froyo/build/core/prelink-linux-arm.map
使用binder的進程調用驅動的mmap函數創建與用戶空間映射的binder mmap區域, 並且 驅動只在內核空間的數據接收區保存數據,
用戶空間的接收區域與內核空間的ipc數據接收區域由binder_mmap函數映射在一起.
在內核中開闢buffer,然後和用戶空間的buffer映射起來
用戶空間和內核空間的buffer要映射到實際的物理內存上.
binder_update_page_range分配物理頁,
binder_insert_free_buffer   free_buffers變量是接收ipc數據的buffer 根據ipc數據的大小,分配不同大小的free_buffer
//binder_write_read是在RPC代碼中  BINDER_WRITE_READ命令碼在在RPC中
上面的應該是ipc代碼???????????
binder_write_read中的write_buffer成員變量 在傳遞經由驅動的數據時使用,來發送ipc數據或ipc應答數據
中的read_buffer 接收來自驅動的數據  ipc數據以何種形態存在binder_write_read結構體中的write_buffer 和read_buffer
handle RPC代碼和RPC數據保存在binder_transaction_data的結構體中  加上binder協議 就是IPC數據
        binder協議在前,後面依次是handle RPC代碼和RPC數據   包含在了write_buffer中228圖有問題
binder_write_read結構體在傳輸的過程中(用戶空間到內核空間) 使用了copy_from_user
 接收完數據要傳遞到用戶空間處理
將用戶空間中的數據拷貝到自身的binder_write_read結構體中
        根據read_size和write_size 變量是發送ipc數據還是接收ipc數據
                接收時將生成相應的read_buffer,並且read_size大於零
interruptible可中斷的  exclusive獨有的  通過當前進程的binder_proc結構體wait變量將當前任務註冊到待機隊列中.  然後將task_truct結構體內的state變量又TASK_RUNNING 更改爲TASK_INTERRUPTIBLE再調用schedule函數
wake_up即可喚醒
binder_write_read
        中的結構體,一個是接收ipc數據,一個是發送ipc數據.
                服務名稱和flat_binder_object(數據,註冊和檢索時,用於生成或查找binder節點)結構體會被註冊到ipc數據的RPC數據中flat_binder_object要麼代碼節點(BINDER_TYPE_BINDER)要麼是引用  傳送write_size就大於零
                consumed指ipc數據的個數   get_user獲取binder協議
在binder_transaction_data中 協議排在第一位,然後是RPC數據  將指針所指向的內容重新拷貝到內核空間中
接收端和發送端通過binder_transaction結構體收發數據
        target_proc指向了接收端的binder_proc結構體  wait_queue_head_t用來幫助接收端脫離待機狀態
通過handle找到binder_node然後找到對於的接收端進程
        binder_proc中有很多Binder_node,且binder_node中的binder_proc指向同一個


向接收端發送數據
        t->from 來自線程此處的thread爲binder_get_thread獲取的 通過線程查找到所在進程
           t->to_proc 到達進程
t.buffer 爲binder驅動程序爲該事務分配的一塊內存緩衝區然後把事務註冊
        struct binder_transaction *transaction;  //每一個事務都關聯一個目標Binder實體對象
                一個事務中含有一個小的binder_buffer結構體,來傳送ipc數據
/////////////////////////////////////////////
一個copy_from_user完成了從發送進程到接收進程的轉換
binder_buffer結構體是小的.
新生成的binder節點會被註冊到當前進程的binder_proc中
binder_get_ref_for_node檢查binder節點是否存在於binder_proc進程中 若存在,則返回,不然新建,並將binder節點註冊其中.並且binder_ref也註冊到進程中refs_by_node變量中
        desc首次註冊服務時生成,編號用來在manager中管理服務列表中不同的服務.   這些東西都註冊到了binder_proc中
wait_queue_head_t  *target_wait;   target_list 接收端的 是todo(數據在這裏面找 binder_work(更底層了) 傳輸數據協議) 添加到wait中執行
wake_up_interruptible函數喚醒
//////////////////////////////////驅動中完成
發送端執行
binder_thread_write函數進入等待  在binder_ioctl層面

        在接收端先查找出來binder_work,   

Container_of第一參數是結構體成員變量的地址,第二個是結構體定義,第三個是第一個參數在結構體中的名稱

接收的時候不要調用copy_to_user,應用內核和用戶空間都映射到了物理空間上面
BR_TRANSACTION是在接收端的內核進程中加的.
binder_write_read.read_buffer  等於binde協議BR_TRANSACTION + binder_transaction_data
把binder_transaction保存在線程的transaction_stack              查找另一端接收進程


服務檢索階段 的時候返回會創建flat_binder_object
binder_write_read.write_buffer  等於binde協議BC_TRANSACTION + binder_transaction_data
                                                handle(0) RPC代碼 RPC數據(服務名稱)
  服務端應答
binder_write_read.write_buffer  等於binde協議BC_REPLY + binder_transaction_data
          handle(0) RPC代碼 RPC數據(binder_object)服務目錄編號 type設爲HANDLE
服務使用階段
binder_write_read.write_buffer  等於binde協議BC_TRANSACTION + binder_transaction_data
          handle(binder_ref中的desc) RPC代碼服務函數 RPC數據(服務函數參數)
          handle-> binder_ref->binder_node->binder_proc
          通過handle獲取binder_ref(此結構體由manager持有)
        還要向服務客戶端註冊binder節點,都會生成一個binder_ref,同時desc加1,然後修改handle編號的值
服務目錄 通過/system/service程序即可查看  service list查看服務列表
manager採用c語言編寫  驅動編寫 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章