天天看點

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如此一次排列,圖解如下:

05.Binder系統:第6課第4節_Binder系統_驅動情景分析_服務注冊過程分析

假設在核心中執行

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

之後,如果沒有任何資料可讀,那麼他肯定進入休眠狀态,現在我們就假設他沒有資料可讀,繼續閱讀代碼:

可以看到

在這裡等待的時候,現在就到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進行傳輸(前面已經進行過具體分析,不再繼續講解)。現在我們梳理一下過程,先看框圖如下

05.Binder系統:第6課第4節_Binder系統_驅動情景分析_服務注冊過程分析

注冊服務的過程,一般如下:

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;
           

繼續閱讀