05.Binder系统:第6课第4节_Binder系统_驱动情景分析_服务注册过程分析

在第6课第一小节中,test_server.c文件中,我们注册了两个服务,分别为"hello"与"goodbye"服务,为了方便分析,我们现在只注册"hello"服务,把"goodbye"服务注释掉,然后重新编译,下载到开发板。

service_manager分析

打开service_manager文件,我们从main函数看起,

main()
	binder_loop(bs, svcmgr_handler);
	binder_write(bs, readbuf, sizeof(uint32_t));
		/*表示监测所有进程*/
		readbuf[0] = BC_ENTER_LOOPER;
		res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//导致内核ioctl被调用
		//------------------------内核态---------------------------------
		binder_ioctl() //该ioctrl可能
		//描述调用该ioctl的进程,该结构体在open的时候已经被创建
		struct binder_proc *proc = filp->private_data; 
		//描述该ioctrl的线程,在第一次执行binder_ioctl的时候被创建
		struct binder_thread *thread; 
		thread = binder_get_thread(proc); //从进程中获取该线程,
			thread = binder_get_thread_ilocked(proc, NULL); //从红黑树寻找线程,
			if (!thread)//如果没有找到
				new_thread = kzalloc(sizeof(*thread), GFP_KERNEL);//就新创建一个线程描述结构体,
			return thread; //返回该线程
		binder_ioctl_write_read(filp, cmd, arg, thread);
				if (bwr.write_size > 0) { //bwr.write_size==0,没有执行
					ret = binder_thread_write()
				if (bwr.read_size > 0) {  //bwr.write_size>0,执行
					ret = binder_thread_read()
						if (*consumed == 0) {
							if (put_user(BR_NOOP, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);//copy一个BR_NOOP到用户态,	
							
		//-----------------------------------------------------------------			

从上面我们可以分析出,对于所有的读操作,第一次读取到的数据头都是BR_NOOP,我们通过binder_thread_read()读取数据,读取到readbuf,其中的前4个字节就是BR_NOOP,接下来是一个cmd,然后是数据,接着又一个cmd,data,cmd,data如此一次排列,图解如下:
在这里插入图片描述
假设在内核中执行

if (*consumed == 0) {
							if (put_user(BR_NOOP, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);//copy一个BR_NOOP到用户态,	

之后,如果没有任何数据可读,那么他肯定进入休眠状态,现在我们就假设他没有数据可读,继续阅读代码:
可以看到

	wait_event_interruptible(binder_user_error_wait,binder_stop_on_user_error < 2);

在这里等待的时候,现在就到test_server.c分析了,我们打开test_server.c进入其main函数:

test_server分析

main(int argc, char **argv)
	svcmgr_publish(bs, svcmgr, "hello", hello_service_handler);
		-------------------------------数据构造-----------------------------
		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);
	    ------------------------------------------------------------------
		binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE)
			ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

先构造数据,我们在开发板上先执行./test_server ,其打印信息如下

qytech_azalea:/data/adb # ./test_server                                        
[  227.213422] test_server (1605, 1605), binder_thread_write : BC_TRANSACTION
[  227.213565] 1605:1605 BC_TRANSACTION 62854 -> 223 - node 2, data 0000007fec922b28-0000007fec922b08 size 104-8-0
[  227.213595] test_server (1605, 1605), binder_transaction , print datas :
[  227.213612] 0000: 00 . 00 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 .
[  227.213756] 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 .
[  227.213891] 0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 .
[  227.214026] 0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 .
[  227.214157] 0064: 05 . 00 . 00 . 00 . 68 h 00 . 65 e 00 . 6c l 00 . 6c l 00 . 6f o 00 . 00 . 00 .
[  227.214371] 0080: 85 . 2a * 62 b 73 s 7f . 01 . 00 . 00 . 98 . 06 . 40 @ 00 . 00 . 00 . 00 . 00 .
[  227.214549] 0096: 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
[  227.214786] test_server (1605, 1605), binder_thread_read : BR_NOOP
[  227.214845] test_server (1605, 1605), binder_thread_read : BR_TRANSACTION_COMPLETE
[  227.214879] test_server (1605, 1605), binder_thread_read : BR_NOOP
[  227.216529] servicemanager (223, 223), binder_thread_read : BR_TRANSACTION
[  227.216610] servicemanager (223, 223), binder_thread_read , print datas :
[  227.216627] 0000: 00 . 00 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 .
[  227.216814] 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 .
[  227.216998] 0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 .
[  227.217123] 0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 .
[  227.217304] 0064: 05 . 00 . 00 . 00 . 68 h 00 . 65 e 00 . 6c l 00 . 6c l 00 . 6f o 00 . 00 . 00 .
[  227.217432] 0080: 85 . 2a * 68 h 73 s 7f . 01 . 00 . 00 . 77 w 00 . 00 . 00 . 00 . 00 . 00 . 00 .
[  227.217556] 0096: 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
[  227.218606] servicemanager (223, 223), binder_thread_write : BC_ACQUIRE
[  227.218678] servicemanager (223, 223), binder_thread_write : BC_REQUEST_DEATH_NOTIFICATION
[  227.218782] servicemanager (223, 223), binder_thread_write : BC_FREE_BUFFER
[  227.218828] servicemanager (223, 223), binder_thread_write : BC_REPLY
[  227.218864] 223:223 BC_REPLY 62857 -> 1605:1605, data 0000007fe116e208-0000007fe116e1e8 size 4-0-0
[  227.218916] servicemanager (223, 223), binder_transaction , print datas :
[  227.218978] 0000: 00 . 00 . 00 . 00 .
[  227.219068] servicemanager (223, 223), binder_thread_read : BR_NOOP
[  227.219095] servicemanager (223, 223), binder_thread_read : BR_TRANSACTION_COMPLETE
[  227.219128] servicemanager (223, 223), binder_thread_read : BR_NOOP
[  227.219170] test_server (1605, 1605), binder_thread_read : BR_REPLY
[  227.219218] test_server (1605, 1605), binder_thread_read , print datas :
[  227.219240] 0000: 00 . 00 . 00 . 00 .
[  227.219396] test_server (1605, 1605), binder_thread_write : BC_FREE_BUFFER
[  227.219453] test_server (1605, 1605), binder_thread_write : BC_ENTER_LOOPER
[  227.219491] test_server (1605, 1605), binder_thread_read : BR_NOOP

筛选出我们其中打印的data数据:

[  227.213612] 0000: 00 . 00 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 .
[  227.213756] 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 .
[  227.213891] 0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 .
[  227.214026] 0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 .
[  227.214157] 0064: 05 . 00 . 00 . 00 . 68 h 00 . 65 e 00 . 6c l 00 . 6c l 00 . 6f o 00 . 00 . 00 .
[  227.214371] 0080: 85 . 2a * 62 b 73 s 7f . 01 . 00 . 00 . 98 . 06 . 40 @ 00 . 00 . 00 . 00 . 00 .
[  227.214549] 0096: 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .

我们进入bio_init(&msg, iodata, sizeof(iodata), 4)函数:

void bio_init(struct binder_io *bio, void *data,size_t maxdata, size_t maxoffs)
{
    size_t n = maxoffs * sizeof(size_t); //保留16字节的空间

    if (n > maxdata) { //如果预留空间大于最大值
        bio->flags = BIO_F_OVERFLOW;
        bio->data_avail = 0;
        bio->offs_avail = 0;
        return;
    }
    bio->data = bio->data0 = (char *) data + n; //指向数据位置
    bio->offs = bio->offs0 = data; //偏移位置也指向数据位置
    bio->data_avail = maxdata - n; 
    bio->offs_avail = maxoffs;
    bio->flags = 0;
}

bio_put_uint32(&msg, 0):存放四个字节

	void bio_put_uint32(struct binder_io *bio, uint32_t n)
	{
	    uint32_t *ptr = bio_alloc(bio, sizeof(n));//为msg分配是个字节的空间存放n(0)
	    if (ptr)
	        *ptr = n;
	}

得到data:00 00 00 00

bio_put_string16_x(&msg, SVC_MGR_NAME):#define SVC_MGR_NAME “android.os.IServiceManager”
bio_put_string16_x(&msg, name):name = “hello”

void bio_put_string16_x(struct binder_io *bio, const char *_str)
{
    unsigned char *str = (unsigned char*) _str;
    size_t len;
    uint16_t *ptr;

    if (!str) {
        bio_put_uint32(bio, 0xffffffff); 
        return;
    }

    len = strlen(_str); //测量字符串的长度

    if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) {
        bio_put_uint32(bio, 0xffffffff);
        return;
    }

    /* Note: The payload will carry 32bit size instead of size_t */
    bio_put_uint32(bio, len);  //先放入长度msg
    ptr = bio_alloc(bio, (len + 1) * sizeof(uint16_t));//分配长度加1(存放“\0”)的空间,
    if (!ptr)
        return;

    while (*str) //放入data数据
        *ptr++ = *str++;
    *ptr++ = 0;
}
先得到data:1a 00 00 00 xx xx xx xx ... ... (x共0xla*2个字节,每个字符占两个字节)
再得到data:05 00 00 00 xx xx xx ... ...(x共0x06*2个字节,每个字符占两个字节)

把他上面分析的data组合在一起,就与我们打印的log信息的data吻合了,下面我们看看bio_put_obj:

void bio_put_obj(struct binder_io *bio, void *ptr)
{
    struct flat_binder_object *obj;

    obj = bio_alloc_obj(bio);//分配flat_binder_object结构体
    if (!obj)
        return;
	/*填充数据*/
    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    obj->type = BINDER_TYPE_BINDER;//BINDER_TYPE_BINDER	= B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
    obj->binder = (uintptr_t)ptr;
    obj->cookie = 0;
}

在第6课第1节中,server传入flat_binder_object结构体,根据该结构体为每个服务创建(驱动程序)binder_node,binder_node.proc = server进程。从 obj->type = BINDER_TYPE_BINDER我们获得:s,b,*,我们查看打印log,找到0x80字节,可以找到:s,b,*,其中的 obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;等等我们都能在log中找到与其对应的字符或者数值。这个结构体是存放在前面的data后面的。

那么出现了一个问题,在驱动程序中是怎么知道在接收的一堆数据混有一个struct flat_binder_object *obj,前面在bio_init中,预留了16个字节。其中每四个字节作为一个指针,分别指向一个struct flat_binder_object结构体,如果其值为指针0值,则代表没有struct flat_binder_object 结构体。在构建完struct binder_io msg时,会通过binder_call调用ioctrl发送msg,

binder_call(struct binder_state *bs,struct binder_io *msg, struct binder_io *reply,uint32_t target, uint32_t code)
	struct {
        uint32_t cmd;
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;
    /*注意,这个是我们关心的四大类型之一地方*/
    writebuf.cmd = BC_TRANSACTION;
    
    /*在这里表示这个数据是发送给service_manager*/
    writebuf.txn.data_size = msg->data - msg->data0;
    writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
    /*该处code为SVC_MGR_ADD_SERVICE,表示注册服务*/
    writebuf.txn.code = code;
    writebuf.txn.flags = 0;
    /*以下为存放的数据*/
    writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
    writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;
	bwr.write_size = sizeof(writebuf);
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) &writebuf;

binder_call先从msg中提取需要的信息,赋值给writebuf。然后writebuf保存在bwr.write_buffer中,然后通过ioctrl进行传输(前面已经进行过具体分析,不再继续讲解)。现在我们梳理一下过程,先看框图如下
在这里插入图片描述
注册服务的过程,一般如下:

 1.构造数据
 	a. 构造一个binder_io
 	b. binder_io转化为struct binder_transaction_data txn;
 	c. struct binder_transaction_data txn.放入binder_write_read
 	
 2.发送数据:调用Ioctrol
 
 3.进入binder_ioctrol,把输入目的进程的todo链表,并且唤醒目的进程(该处为service_manager)
 	a.根据handlle找到目的进程
 	b.把数据copy_from_user,放入到目的进程mmap的空间
 	c.处理offset数据flat_binder_object:
 		(1).构造binder_node给service_manager
 		(2).构造binder_ref给service_service_manager
 		(3).增加引用计数
  	d.唤醒目的进程

假设test_server.c应用程序调用ioctrol,导致驱动程序的ioctrol被调用:

/*驱动程序*/
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);		
		/*把空间构造好的数据拷贝到内核,注意:该处只是一个头部,数据本身还没有被复制*/
		copy_from_user(&bwr, ubuf, sizeof(bwr))
		/*如果有数据需要写入*/
		if (bwr.write_size > 0) {
			binder_thread_write(proc, thread,bwr.write_buffer,bwr.write_size,&bwr.write_consumed);
				uint32_t cmd;//根据cmd做出不同的处理
				case BC_TRANSACTION:
				case BC_REPLY: {
					binder_transaction()
						/*寻找目的进程*/
						if (reply) {
							if()
								......
							else
								if (tr->target.handle) {
									/*根据handle找到ref*/
									struct binder_ref *ref;
												ref = binder_get_ref_olocked(proc, tr->target.handle,true);
												/*根据ref找到目的节点*/
												target_node = binder_get_node_refs_for_txn(ref->node, &target_proc,&return_error);
					/*把数据拷贝到目的进程mmap映射的空间*/					
					copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size)
						
		/*如果需要读出数据*/
		if (bwr.read_size > 0) {
			binder_thread_read(proc, thread, bwr.read_buffer,bwr.read_size,&bwr.read_consumed,filp->f_flags & O_NONBLOCK);
	/*处理完成*/
	tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
	binder_enqueue_work(proc, tcomplete, &thread->todo);
	t->work.type = BINDER_WORK_TRANSACTION;

这样就把数据写入到目的进程mmap的空间,并且唤醒目的进程。

sever会传入一个flat_binder_object结构体给binder驱动,binder驱动会为每一个进程创建一个binder节点。我们的test_sever去注册hello服务的时候也是一样,构建一个flat_binder_object结构体传递给驱动程序,binder驱动把他取出来之后会构建一个binder_node节点,在讲解之前,我们先来了解一下flat_binder_object结构体。

struct flat_binder_object {
	/* 8 bytes for large_flat_header. */
	//表示传入的是实体还是引用,只有server才能传入实体
	__u32	type;
	__u32	flags;
	/* 8 bytes of data. 引用时为handle一个标号*/
	/*实体表示binder*/
	union {
		binder_uintptr_t	binder;	/* local object */
		__u32			handle;	/* remote object */
	};
	/* extra data associated with local object */
	binder_uintptr_t	cookie;
};

之前提到应用程序在调用open打开binder驱动的时候,会创建一个binder_proc。后续在注册服务的时候,会为每个服务在红褐色树上创建一个binder_node节点,如我们的holle服务。

在service_manager之中,有与自己对应的binder_proc结构体,其中有两颗红河树:

	/*refs:引用,可以根据node也可以根据desc快速找到你想要的节点*/
	struct rb_root refs_by_desc; //每个节点为binder_ref,在这里为heiilo服务的引用
	struct rb_root refs_by_node;
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南才尽,年少无知!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值