Android Binder 修炼之道(二)Client Server 实例
2017-07-29 15:26
441 查看
前面一节,分析了 Binder 系统中的骨架,了解了 Client ServiceManager Server 三者之间的关系,重点针对 ServiceManager 分析了服务的注册过程以及查询过程。本节,将重点放在实现一个简单的 Server 和 Client ,加深对于代码的理解。
首先是 Server:
我们要向 ServiceManager 注册 1 个 calculate 服务,这个服务有两个功能,一个加 1,一个减 1
#ifndef _TEST_SERVER_H
#define _TEST_SERVER_H
#define SVR_CMD_ADD_ONE 0
#define SVR_CMD_REDUCE_ONE 1
#endif // _TEST_SERVER_H
/* Copyright 2008 The Android Open Source Project
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include<stdbool.h>
#include <string.h>
#include <private/android_filesystem_config.h>
#include "binder.h"
#include "test_server.h"
首先来看 main 函数
int main(int argc, char **argv)
{
int fd;
struct binder_state *bs;
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
int ret;
/* 打开 binder 驱动 */
bs = binder_open(128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
}
/* 注册服务,最后一个参数不知道干啥用,先随便写 */
ret = svcmgr_publish(bs, svcmgr, "calculate", (void *)123);
if (ret) {
fprintf(stderr, "failed to publish calculate service\n");
return -1;
}
/* 陷入循环,等待 Client 上钩 */
binder_loop(bs, service_handler);
return 0;
}
看注册服务函数:
int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
int status;
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, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
/* 暂时不知道干啥用的 */
bio_put_obj(&msg, ptr);
if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
return -1;
status = bio_get_uint32(&reply);
/* 这个也不太清楚 */
binder_done(bs, &msg, &reply);
return status;
}
构造 binder_io 的时候先放入了一个 0 ,然后将ServiceManager的名字填充进去,最后把要注册的服务的名字注册进去,然后调用 binder_call 去访问 ServiceManager 的 addService 函数。不搭理那句 bio_put_obj 的话,注册服务的实质就是告诉 ServiceManager 一个服务的名字而已,仅此而已。
注册完两个服务,那么就要等待 Client 上钩,发送数据给我们处理了:
int service_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
/* 根据txn->code知道要调用哪一个函数
* 如果需要参数, 可以从msg取出
* 如果要返回结果, 可以把结果放入reply
*/
uint32_t n,result;
char name[512];
size_t len;
uint32_t handle;
uint32_t strict_policy;
int i;
// Binder 通信的时候都会先塞个0进去,取数据的一方要先把这个0取出来
strict_policy = bio_get_uint32(msg);
switch(txn->code) {
case SVR_CMD_ADD_ONE:
/* 从msg里取出要处理的数据,并调用处理函数处理 */
n = bio_get_uint32(msg);
result = addone(n);
/* 把结果放入reply */
bio_put_uint32(reply, result);
return 0;
case SVR_CMD_REDUCE_ONE:
/* 从msg里取出要处理的数据,并调用处理函数处理 */
n = bio_get_uint32(msg);
result = reduceone(n);
/* 把结果放入reply */
bio_put_uint32(reply, result);
return 0;
default:
fprintf(stderr, "unknown code %d\n", txn->code);
return -1;
}
return 0;
}
这里会产生一个疑问,回复的数据放入 reply 就可以了吗?不需要发送出去?答案是肯定的,你只需要放入 reply 即可了,bind_loop 中会帮你发送出去,看一眼代码踏实:
res = func(bs, txn, &msg, &reply);
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
可以看出,调用我们的处理函数之后,紧接着就是发送回复的数据。
至于这俩函数,不必多说啥
uint32_t addone(uint32_t n)
{
fprintf(stderr, "i am server,add one %d\n", n);
return ++n;
}
uint32_t reduceone(uint32_t n)
{
fprintf(stderr, "i am server,reduce one %d\n", n);
return --n;
}
下面来看 Client 程序:
明确 Client 要干的事情,我有一个数,我想知道它加1是多少,减1是多少,但是自己不会算,得找个服务商算一算。算之前,得找个中介查查,哪个服务商能提供这个服务。Server 进程相当于服务商,他提供 calculate 服务。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include<stdbool.h>
#include <string.h>
#include <private/android_filesystem_config.h>
#include "binder.h"
#include "test_server.h"
struct binder_state *g_bs;
uint32_t g_handle;
int main(int argc, char **argv)
{
int fd;
struct binder_state *bs;
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
int ret;
/* 一样的,先打开 Binder 驱动 */
bs = binder_open(128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
}
g_bs = bs;
/* 查询服务,获得服务 handle */
handle = svcmgr_lookup(bs, svcmgr, "calculate");
if (!handle) {
fprintf(stderr, "failed to get addone service\n");
return -1;
}
g_handle = handle;
/* send data to server */
if (!strcmp(argv[1], "a")) {
ret = addone(atoi(argv[2]));
fprintf(stderr, "get ret of addone= %d\n", ret);
} else if (!strcmp(argv[1], "r")) {
ret = reduceone(atoi(argv[2]));
fprintf(stderr, "get ret of reduceone= %d\n", ret);
}
/* Client 使用完就释放服务 */
binder_release(bs, handle);
return 0;
}
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;
/* 构造 binder_io 参数是想要获取 service 的名字 */
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);
/* 查询服务是否存在 */
if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
return 0;
/* 得到服务的 handle */
handle = bio_get_ref(&reply);
/* 申请服务,用完 release */
if (handle)
binder_acquire(bs, handle);
binder_done(bs, &msg, &reply);
return handle;
}
int addone(int n)
{
unsigned iodata[512/4];
struct binder_io msg, reply;
int ret;
/* 构造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
/* 放入参数 */
bio_put_uint32(&msg, n);
/* 调用binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, SVR_CMD_ADD_ONE))
return;
/* 从reply中解析出返回值 */
ret = bio_get_uint32(&reply);
binder_done(g_bs, &msg, &reply);
return ret;
}
int reduceone(int n)
{
unsigned iodata[512/4];
struct binder_io msg, reply;
int ret;
/* 构造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
/* 放入参数 */
bio_put_uint32(&msg, n);
/* 调用binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, SVR_CMD_REDUCE_ONE))
return 0;
/* 从reply中解析出返回值 */
ret = bio_get_uint32(&reply);
binder_done(g_bs, &msg, &reply);
return ret;
}
编译:
运行:
不要忘了我们忽略了一个问题,bio_put_obj(&msg, ptr);这个东西是干嘛的?
前面的例子中,我们的 Server 只提供了一个 calculate 服务,如果这个服务商业务比较广,他还能提供查水表服务,那么Server的代码该如何写?
在我们的代码中只有一个服务处理函数,通过binder_loop来处理client的需求,binder_loop(bs, service_handler);
分析 binder_loop 的代码不难发现,这个 Server 进程只要有数据进来,就会调用 service_handler ,至于 client 访问的是哪个服务,该怎样区别对待,就需要用到前面忽略的 ptr 了。
首先,service_handler 作为进程数据的总入口,它有一个参数,binder_transaction_data
struct binder_transaction_data {
union {
__u32 handle; /* target descriptor of command transaction */
binder_uintptr_t ptr; /* target descriptor of return transaction */
} target;
...
}
我们可以通过 ptr 来区分是哪个服务被调用,从而调用该服务的处理函数,那么ptr是什么东西?是服务处理函数的函数指针。
binder_loop(bs, main_server_handler);
int main_server_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
int (*handler)(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply);
handler = (int (*)(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply))txn->target.ptr;
return handler(bs, txn, msg, reply);
}
写一个 main_server_handler 作为总入口,然后根据其传入的函数指针调用处理函数,也就是 calculate 或 查水表的服务函数
那么这个函数指针 Binder 系统是如何知道的呢?它怎么能知道一个服务对应处理函数的函数指针是多少?这就需要在注册服务的同时,传入这个函数指针。
ret = svcmgr_publish(bs, svcmgr, "calculate", calculate_service_handler);
ret = svcmgr_publish(bs, svcmgr, "chashuibiao", chashuibiao_service_handler);
首先是 Server:
我们要向 ServiceManager 注册 1 个 calculate 服务,这个服务有两个功能,一个加 1,一个减 1
#ifndef _TEST_SERVER_H
#define _TEST_SERVER_H
#define SVR_CMD_ADD_ONE 0
#define SVR_CMD_REDUCE_ONE 1
#endif // _TEST_SERVER_H
/* Copyright 2008 The Android Open Source Project
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include<stdbool.h>
#include <string.h>
#include <private/android_filesystem_config.h>
#include "binder.h"
#include "test_server.h"
首先来看 main 函数
int main(int argc, char **argv)
{
int fd;
struct binder_state *bs;
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
int ret;
/* 打开 binder 驱动 */
bs = binder_open(128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
}
/* 注册服务,最后一个参数不知道干啥用,先随便写 */
ret = svcmgr_publish(bs, svcmgr, "calculate", (void *)123);
if (ret) {
fprintf(stderr, "failed to publish calculate service\n");
return -1;
}
/* 陷入循环,等待 Client 上钩 */
binder_loop(bs, service_handler);
return 0;
}
看注册服务函数:
int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
int status;
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, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
/* 暂时不知道干啥用的 */
bio_put_obj(&msg, ptr);
if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
return -1;
status = bio_get_uint32(&reply);
/* 这个也不太清楚 */
binder_done(bs, &msg, &reply);
return status;
}
构造 binder_io 的时候先放入了一个 0 ,然后将ServiceManager的名字填充进去,最后把要注册的服务的名字注册进去,然后调用 binder_call 去访问 ServiceManager 的 addService 函数。不搭理那句 bio_put_obj 的话,注册服务的实质就是告诉 ServiceManager 一个服务的名字而已,仅此而已。
注册完两个服务,那么就要等待 Client 上钩,发送数据给我们处理了:
int service_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
/* 根据txn->code知道要调用哪一个函数
* 如果需要参数, 可以从msg取出
* 如果要返回结果, 可以把结果放入reply
*/
uint32_t n,result;
char name[512];
size_t len;
uint32_t handle;
uint32_t strict_policy;
int i;
// Binder 通信的时候都会先塞个0进去,取数据的一方要先把这个0取出来
strict_policy = bio_get_uint32(msg);
switch(txn->code) {
case SVR_CMD_ADD_ONE:
/* 从msg里取出要处理的数据,并调用处理函数处理 */
n = bio_get_uint32(msg);
result = addone(n);
/* 把结果放入reply */
bio_put_uint32(reply, result);
return 0;
case SVR_CMD_REDUCE_ONE:
/* 从msg里取出要处理的数据,并调用处理函数处理 */
n = bio_get_uint32(msg);
result = reduceone(n);
/* 把结果放入reply */
bio_put_uint32(reply, result);
return 0;
default:
fprintf(stderr, "unknown code %d\n", txn->code);
return -1;
}
return 0;
}
这里会产生一个疑问,回复的数据放入 reply 就可以了吗?不需要发送出去?答案是肯定的,你只需要放入 reply 即可了,bind_loop 中会帮你发送出去,看一眼代码踏实:
res = func(bs, txn, &msg, &reply);
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
可以看出,调用我们的处理函数之后,紧接着就是发送回复的数据。
至于这俩函数,不必多说啥
uint32_t addone(uint32_t n)
{
fprintf(stderr, "i am server,add one %d\n", n);
return ++n;
}
uint32_t reduceone(uint32_t n)
{
fprintf(stderr, "i am server,reduce one %d\n", n);
return --n;
}
下面来看 Client 程序:
明确 Client 要干的事情,我有一个数,我想知道它加1是多少,减1是多少,但是自己不会算,得找个服务商算一算。算之前,得找个中介查查,哪个服务商能提供这个服务。Server 进程相当于服务商,他提供 calculate 服务。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include<stdbool.h>
#include <string.h>
#include <private/android_filesystem_config.h>
#include "binder.h"
#include "test_server.h"
struct binder_state *g_bs;
uint32_t g_handle;
int main(int argc, char **argv)
{
int fd;
struct binder_state *bs;
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
int ret;
/* 一样的,先打开 Binder 驱动 */
bs = binder_open(128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
}
g_bs = bs;
/* 查询服务,获得服务 handle */
handle = svcmgr_lookup(bs, svcmgr, "calculate");
if (!handle) {
fprintf(stderr, "failed to get addone service\n");
return -1;
}
g_handle = handle;
/* send data to server */
if (!strcmp(argv[1], "a")) {
ret = addone(atoi(argv[2]));
fprintf(stderr, "get ret of addone= %d\n", ret);
} else if (!strcmp(argv[1], "r")) {
ret = reduceone(atoi(argv[2]));
fprintf(stderr, "get ret of reduceone= %d\n", ret);
}
/* Client 使用完就释放服务 */
binder_release(bs, handle);
return 0;
}
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;
/* 构造 binder_io 参数是想要获取 service 的名字 */
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);
/* 查询服务是否存在 */
if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
return 0;
/* 得到服务的 handle */
handle = bio_get_ref(&reply);
/* 申请服务,用完 release */
if (handle)
binder_acquire(bs, handle);
binder_done(bs, &msg, &reply);
return handle;
}
int addone(int n)
{
unsigned iodata[512/4];
struct binder_io msg, reply;
int ret;
/* 构造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
/* 放入参数 */
bio_put_uint32(&msg, n);
/* 调用binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, SVR_CMD_ADD_ONE))
return;
/* 从reply中解析出返回值 */
ret = bio_get_uint32(&reply);
binder_done(g_bs, &msg, &reply);
return ret;
}
int reduceone(int n)
{
unsigned iodata[512/4];
struct binder_io msg, reply;
int ret;
/* 构造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
/* 放入参数 */
bio_put_uint32(&msg, n);
/* 调用binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, SVR_CMD_REDUCE_ONE))
return 0;
/* 从reply中解析出返回值 */
ret = bio_get_uint32(&reply);
binder_done(g_bs, &msg, &reply);
return ret;
}
编译:
运行:
不要忘了我们忽略了一个问题,bio_put_obj(&msg, ptr);这个东西是干嘛的?
前面的例子中,我们的 Server 只提供了一个 calculate 服务,如果这个服务商业务比较广,他还能提供查水表服务,那么Server的代码该如何写?
在我们的代码中只有一个服务处理函数,通过binder_loop来处理client的需求,binder_loop(bs, service_handler);
分析 binder_loop 的代码不难发现,这个 Server 进程只要有数据进来,就会调用 service_handler ,至于 client 访问的是哪个服务,该怎样区别对待,就需要用到前面忽略的 ptr 了。
首先,service_handler 作为进程数据的总入口,它有一个参数,binder_transaction_data
struct binder_transaction_data {
union {
__u32 handle; /* target descriptor of command transaction */
binder_uintptr_t ptr; /* target descriptor of return transaction */
} target;
...
}
我们可以通过 ptr 来区分是哪个服务被调用,从而调用该服务的处理函数,那么ptr是什么东西?是服务处理函数的函数指针。
binder_loop(bs, main_server_handler);
int main_server_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
int (*handler)(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply);
handler = (int (*)(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply))txn->target.ptr;
return handler(bs, txn, msg, reply);
}
写一个 main_server_handler 作为总入口,然后根据其传入的函数指针调用处理函数,也就是 calculate 或 查水表的服务函数
那么这个函数指针 Binder 系统是如何知道的呢?它怎么能知道一个服务对应处理函数的函数指针是多少?这就需要在注册服务的同时,传入这个函数指针。
ret = svcmgr_publish(bs, svcmgr, "calculate", calculate_service_handler);
ret = svcmgr_publish(bs, svcmgr, "chashuibiao", chashuibiao_service_handler);
相关文章推荐
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析(3)
- 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
- android IPC binder -- client/server与service manager交互流程
- 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
- Android Binder中的Server和Client获得Service Manager接口
- Android源码解析之Binder中Server和Client获得Service Manager接口
- 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- Android-Camera Client/Server的binder IPC机制
- Android系统Binder机制中的四个组件Client、Server、Service Manager和Binder驱动程序的关系
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
- 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
- Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
- 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- [Binder.2] 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路