Binder 程式示例與分析之C語言篇續
在Binder 程式示例與分析之C語言篇中講解了如何完成一個 Binder C 語言示例程式並詳細分析了服務的註冊過程,接下來我們繼續分析以下兩個情景:
- 服務的獲取過程
- 服務的使用過程
1. 服務的獲取過程
服務的獲取過程主要有以下幾個流程:
- 呼叫 binder_open 完成 binder 驅動初始化
- 呼叫 svcmgr_lookup 獲取到服務的 handle 值
- 通過 binder_call 發起遠端呼叫
binder_client.c :
```c int main(int argc, char *argv) { int fd; struct binder_state bs; uint32_t svcmgr = BINDER_SERVICE_MANAGER; int ret;
//完成 binder 初始化
bs = binder_open("/dev/binder", 128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
}
g_bs = bs;
//獲取 hello 服務的 handle 值
g_handle = svcmgr_lookup(bs, svcmgr, "hello");
if (!g_handle) {
return -1;
}
//呼叫服務
sayhello();
}
void sayhello(void) { unsigned iodata[512/4]; struct binder_io msg, reply;
/* 構造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);
/* 放入引數 */
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, "IHelloService");
/* 呼叫binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, HELLO_SVR_CMD_SAYHELLO))
return ;
/* 從reply中解析出返回值 */
binder_done(g_bs, &msg, &reply);
} ```
接下來逐一分析程式碼:
binder_open 初始化好原始碼,該函式前面已做分析,這裡就不在細說了。
svcmgr_lookup 用於發起遠端呼叫,查詢服務的 handle 值。
handle 是服務在核心中的索引(有點檔案描述符 fd 的感覺),通過 handle 才能向目標程序的服務傳送資料。
svcmgr_lookup 函式有三個引數: * struct binder_state *bs : binder_open 返回的 binder 狀態值 * target:目標程序的 handle 值,ServiceManager 對應的 handle 值固定為 0 * name:服務的名字
```c / * 根據服務的名字(name),返回服務在核心中的 handle 值 * target:目標程序的 handle 值,ServiceManager 對應的 handle 值固定為 0 * name:服務的名字 / uint32_t svcmgr_lookup(struct binder_state bs, uint32_t target, const char *name) { uint32_t handle; unsigned iodata[512/4]; struct binder_io msg, reply;
bio_init(&msg, iodata, sizeof(iodata), 4);
//第一個資料 0
bio_put_uint32(&msg, 0); // strict mode header
//"android.os.IServiceManager"
bio_put_string16_x(&msg, SVC_MGR_NAME);
//服務的名字 "hello"
bio_put_string16_x(&msg, name);
//發起遠端過程呼叫
//呼叫的方法是 SVC_MGR_CHECK_SERVICE
//client 端開始休眠
if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
return 0;
//......
} ```
發起遠端呼叫後,ServiceManager 被喚醒,執行到以下程式碼:
```c void binder_loop(struct binder_state *bs, binder_handler func) { int res; struct binder_write_read bwr; uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
//ServieManager 從這裡喚醒
//獲取到 Server 傳過來的資料
//核心的資料是
// SVC_MGR_CHECK_SERVICE 表示要呼叫的方法
// "hello" 傳入方法的資料
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
//解析收到的資料,並回調 func 方法
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
} ```
接下來看看 binder_parse
方法:
這裡會解析我們收到的資料,我們先看看我們收到的資料的具體格式:
```c //刪除不相關程式碼 int binder_parse(struct binder_state bs, struct binder_io bio, uintptr_t ptr, size_t size, binder_handler func) { int r = 1; uintptr_t end = ptr + (uintptr_t) size;
//可能存在多組資料,通過 while 迴圈將每個資料解析完後再退出迴圈
while (ptr < end) {
//獲取到命令 cmd,不同的 cmd 執行不同的操作
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
switch(cmd) {
//......
//會走到這個分支
case BR_TRANSACTION_SEC_CTX:
case BR_TRANSACTION: {
struct binder_transaction_data_secctx txn;
if (cmd == BR_TRANSACTION_SEC_CTX) {
//......
} else /* BR_TRANSACTION */ { //程式碼會走這裡
if ((end - ptr) < sizeof(struct binder_transaction_data)) {
ALOGE("parse: txn too small (binder_transaction_data)!\n");
return -1;
}
//解析出 binder_transaction_data 結構體
memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data));
ptr += sizeof(struct binder_transaction_data);
txn.secctx = 0;
}
//......
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
struct binder_io reply;
int res;
bio_init(&reply, rdata, sizeof(rdata), 4);
//進一步解析資料
//解析出 binder_io 結構體
bio_init_from_txn(&msg, &txn.transaction_data);
//呼叫回撥函式
res = func(bs, &txn, &msg, &reply);
//回覆資料
if (txn.transaction_data.flags & TF_ONE_WAY) {
binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);
} else {
binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);
}
}
break;
}
//......
default:
ALOGE("parse: OOPS %d\n", cmd);
return -1;
}
}
return r;
} ```
上面的程式碼走到 res = func(bs, &txn, &msg, &reply);
時就會執行到 binder_loop 傳入的回撥函式 svcmgr_handler
:
```c //已刪減部分不相關程式碼 int svcmgr_handler(struct binder_state bs, struct binder_transaction_data_secctx txn_secctx, struct binder_io msg, struct binder_io reply) { struct svcinfo si; uint16_t s; size_t len; uint32_t handle; uint32_t strict_policy; int allow_isolated; uint32_t dumpsys_priority;
struct binder_transaction_data *txn = &txn_secctx->transaction_data;
if (txn->target.ptr != BINDER_SERVICE_MANAGER)
return -1;
if (txn->code == PING_TRANSACTION)
return 0;
strict_policy = bio_get_uint32(msg);
bio_get_uint32(msg); // Ignore worksource header.
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
if ((len != (sizeof(svcmgr_id) / 2)) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
fprintf(stderr,"invalid id %s\n", str8(s, len));
return -1;
}
//根據 code 值呼叫不同的方法
//當前場景下 code == SVC_MGR_CHECK_SERVICE
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//s 是服務的名字 hello
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
//從連結串列 svclist 中找到服務,返回服務的 handle
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
(const char*) txn_secctx->secctx);
if (!handle)
break;
//將 handle 寫入 reply
bio_put_ref(reply, handle);
return 0;
//......
default:
ALOGE("unknown code %d\n", txn->code);
return -1;
}
bio_put_uint32(reply, 0);
return 0;
} ```
接下來呼叫 binder_send_reply 將 reply 返回給 Client:
```c void binder_send_reply(struct binder_state bs, struct binder_io reply, binder_uintptr_t buffer_to_free, int status) { //構建了兩個資料塊 struct { uint32_t cmd_free; binder_uintptr_t buffer; uint32_t cmd_reply; struct binder_transaction_data txn; } attribute((packed)) data;
//第一個命令告知驅動清理記憶體
data.cmd_free = BC_FREE_BUFFER;
data.buffer = buffer_to_free;
//第二個命令給 client 傳送 reply 資料
//傳送 BC_REPLY 接收方會收到 BR_REPLY
data.cmd_reply = BC_REPLY;
data.txn.target.ptr = 0;
data.txn.cookie = 0;
data.txn.code = 0;
if (status) {
data.txn.flags = TF_STATUS_CODE;
data.txn.data_size = sizeof(int);
data.txn.offsets_size = 0;
data.txn.data.ptr.buffer = (uintptr_t)&status;
data.txn.data.ptr.offsets = 0;
} else { //svcmgr_handler 返回0, 走這裡
data.txn.flags = 0;
data.txn.data_size = reply->data - reply->data0;
data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
}
//發起寫操作
binder_write(bs, &data, sizeof(data));
} ```
接下來 Client 被喚醒:
```c int binder_call(struct binder_state bs, struct binder_io msg, struct binder_io *reply, uint32_t target, uint32_t code) { //...... for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (uintptr_t) readbuf;
//從這裡喚醒
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));
goto fail;
}
//解析收到的資料
res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);
if (res == 0) return 0;
if (res < 0) goto fail;
}
//...... } ```
解析資料過程:
```c int binder_parse(struct binder_state bs, struct binder_io bio, uintptr_t ptr, size_t size, binder_handler func) { int r = 1; uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
switch(cmd) {
//......
//程式碼走這裡
case BR_REPLY: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if ((end - ptr) < sizeof(*txn)) {
ALOGE("parse: reply too small!\n");
return -1;
}
binder_dump_txn(txn);
if (bio) {
//將資料解析到 bio 中,也就是傳入的 reply
bio_init_from_txn(bio, txn);
bio = 0;
} else {
/* todo FREE BUFFER */
}
ptr += sizeof(*txn);
r = 0;
break;
}
default:
ALOGE("parse: OOPS %d\n", cmd);
return -1;
}
}
return r;
} ```
解析出 reply 後,程式碼回到 binder_call,binder_call 接著又返回到 svcmgr_lookup:
```c uint32_t svcmgr_lookup(struct binder_state bs, uint32_t target, const char name) { uint32_t handle; unsigned iodata[512/4]; struct binder_io msg, reply;
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
//從這裡返回
//handle 值已寫入 reply
if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
return 0;
//從 reply 中解析出 handle
handle = bio_get_ref(&reply);
if (handle)
binder_acquire(bs, handle);
//告知驅動,binder 呼叫已完成
binder_done(bs, &msg, &reply);
return handle;
} ```
到這裡,client 獲取到 hello 服務的 handle 值,服務請求階段完畢。
2. 服務的使用過程
服務的使用過程就是呼叫客戶端 sayhello 的過程:
```c void sayhello(void) { unsigned iodata[512/4]; struct binder_io msg, reply;
/* 構造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);
/* 放入引數 */
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, "IHelloService");
/* 呼叫binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, HELLO_SVR_CMD_SAYHELLO))
return ;
/* 從reply中解析出返回值 */
binder_done(g_bs, &msg, &reply);
} ``` 這部分內容與服務獲取過程有 80% 以上的重複,有了前面的基礎,這部分解析就非常簡單了,這部分內容就留給讀者自行分析了。