Android Service Manager源碼剖析

vcD4KPHA+PGJyPgo8L3A+CjxoMj5TZXJ2aWNlIE1hbmFnZXLI57rOxvS2r7XEsKGjvyAgICA8L2gyPgo8cD48YnI+CjwvcD4KPHA+ICAgICAgzqrKssO00qq9slNlcnZpY2UgTWFuYWdlciC6zSBtZWRpYXNlcnZlcrXExvS2r8TYo78g0vLOqrrzw+bO0sPH1Nq9skNhbWVyYcH3s8w8L3A+CjxwPrXEyrG68qOsu+HTw7W9z+C52Naqyra146Gj19u6z87E1cK94bm51+nWr7yw1MS2wczl0em/vMLHo6y+9raovavV4sG9sr+31rbAwaKz9sC0o6w8L3A+CjxwPtXrttTQ1Nf2uPbGys72oaO52NPaQmluZGVysr+31qOsuPbIy9a7yse4+b7d19S8urXEwO294tC0tcS31s72o6yyos60zai5/cq11b3XpWxvZzwvcD4KPHA+tcS3vcq90+jS1Mi3yM+jrNTaz+C52MH3s8zA77jD1PXDtLzTbG9no6y7ucfruN/K1sPHsrvB37TNvcyjoaOhPC9wPgo8cD48YnI+CjwvcD4KPHA+ICAgIMeww+bO0r2yaW5pdC5yY9bQtcRzZXJ2aWNlysfI57rOxvS2r7XEo6yyotLUenlnb3RlzqrA/df2wcvLtcP3oaPP6sfpx+uyzr+8PC9wPgo8cD6htsnuyOvA7b3iQW5kcm9pZCC+7TGht7bByumxyrzHIKOo0rujqaGqoaoKIEFuZHJvaWQgSW5pdCDHs8721q60001haW6/qsq8tb1zZXJ2aWNlIHN0YXJ0PGJyPgo8L3A+CjxwPlNlcnZpY2UgTWFuYWdlctKyysewtNXVzazR+bXEt73KvbG7xvS2r7XEoaPO0sPHz8i/tL+0aW5pdC5yY9bQtcTP4LnYxqy2zqO6PC9wPgo8cD48L3A+CjxwcmUgY2xhc3M9″brush:java;”>service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
好瞭,我們重點是第一句: service servicemanager /system/bin/servicemanager

代碼在哪裡啊,入口在哪裡?

那這個servicemanager程序對於的源碼位於哪裡呢? 找Makefile!!怎麼找?搜索servicemanager找目錄,

文件?確實,就這麼找到瞭。看看frameworks/base/cmds/servicemanager/Android.mk怎麼寫的吧?

include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := service_manager.c binder.c
LOCAL_MODULE := servicemanager
include $(BUILD_EXECUTABLE)

太感動瞭,從LOCAL_MODULE為servicemanager,我們知道要找的東東就是它瞭!! 涉及到此目錄

下的service_manager.c和binder.c兩個源文件(和binder.h一個頭文件)。

接下來的第一步就是找程序的入口函數——main函數啦。 哇塞,我找到瞭,太激動瞭有莫有。。。

int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;

    bs = binder_open(128*1024);

    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}

變量賦值神馬的,暫時不管,我們先看看調用的函數做瞭些啥。

細說binder_open

struct binder_state *binder_open(unsigned mapsize)
{
    struct binder_state *bs;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return 0;
    }

    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

        /* TODO: check version */

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return 0;
}

就是打開/dev/binder設備,調用mmap將設備映射到大小為(128*1024)字節的內存裡。但這邊open,

mmap,其實會調用binder驅動註冊的open和mmap函數。 以open為例,對應的就是

kernel/drivers/staging/android/binder.c中的binder_open函數。

static int binder_open(struct inode *nodp, struct file *filp)
{
	struct binder_proc *proc;

	binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
		     current->group_leader->pid, current->pid);

	proc = kzalloc(sizeof(*proc), GFP_KERNEL);
	if (proc == NULL)
		return -ENOMEM;
	get_task_struct(current);
	proc->tsk = current;
	INIT_LIST_HEAD(&proc->todo);
	init_waitqueue_head(&proc->wait);
	proc->default_priority = task_nice(current);
#ifdef RT_PRIO_INHERIT
	proc->default_rt_prio = current->rt_priority;
	proc->default_policy = current->policy;
#endif

	binder_lock(__func__);

	binder_stats_created(BINDER_STAT_PROC);
	hlist_add_head(&proc->proc_node, &binder_procs);
	proc->pid = current->group_leader->pid;
	INIT_LIST_HEAD(&proc->delivered_death);
	filp->private_data = proc;

	binder_unlock(__func__);

	if (binder_debugfs_dir_entry_proc) {
		char strbuf[11];
		snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
		proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
			binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
	}

	return 0;
}

binder_become_context_manager初探

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

原來是通過ioctl,發送瞭BINDER_SET_CONTEXT_MGR的命令給/dev/binder設備。有點Linux Driver基礎知識

的人都知道,如果設備的驅動程序註冊的file operation函數指針數組裡面如有指定ioctl函數,那麼此處調用的

ioctl函數將是那個對應的註冊函數。

關於binder相關的代碼,位於kernel/drivers/staging/android目錄下。在此目錄的Binder.c中,我們看到瞭

binder_ioctl函數,初步猜測這個就是我們要找的binder設備的ioctl函數。代碼中是如何印證這一點的呢?

在該文件中搜索binder_ioctl,我們可以看到:

static const struct file_operations binder_fops = {
	.owner = THIS_MODULE,
	.poll = binder_poll,
	.unlocked_ioctl = binder_ioctl,
	.mmap = binder_mmap,
	.open = binder_open,
	.flush = binder_flush,
	.release = binder_release,
};

static struct miscdevice binder_miscdev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "binder",
	.fops = &binder_fops
};

看到這個東東,是否覺得似曾相識? 沒錯,在很多講驅動程序的地方,我們會看到字符設備驅動的范例。

裡面會有module_init/ module_exit。會講到模塊的插入和移除,字符設備的註冊和註銷,以及該module的

file operation數組。該數組裡面會指定設備文件的各種操作函數,諸如open,ioctl,release等。

這裡也是那樣的。還有個問題,那設備驅動程序的入口(insert module時執行的函數)在哪裡呢?搜索下

上面那個binder_miscdev,看看哪個地方註冊的這個設備。原來是在binder_init函數裡:

ret = misc_register(&binder_miscdev);

那誰調用的binder_init呢?

device_initcall(binder_init);

#define device_initcall(fn)		module_init(fn)


/* Each module must use one module_init(). */
#define module_init(initfn)					\
	static inline initcall_t __inittest(void)		\
	{ return initfn; }					\
	int init_module(void) __attribute__((alias(#initfn)));

終於水落石出瞭,原來init_module時調用的就是這個binder_init啊。OK,追根溯源結束瞭,我們回過頭來

看看那個binder_ioctl裡針對BINDER_SET_CONTEXT_MGR到底做瞭啥。

struct binder_proc *proc = filp->private_data;
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;

	/*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/

	trace_binder_ioctl(cmd, arg);

	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		goto err_unlocked;

	binder_lock(__func__);
	thread = binder_get_thread(proc);
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

case BINDER_SET_CONTEXT_MGR:
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
		if (binder_context_mgr_node == NULL) {
			ret = -ENOMEM;
			goto err;
		}
#ifdef BINDER_MONITOR
		strcpy(binder_context_mgr_node->name, "servicemanager");
#endif
		binder_context_mgr_node->local_weak_refs++;
		binder_context_mgr_node->local_strong_refs++;
		binder_context_mgr_node->has_strong_ref = 1;
		binder_context_mgr_node->has_weak_ref = 1;
		break;

先看看這個binder_new_node做瞭什麼。

static struct binder_node *binder_new_node(struct binder_proc *proc,
					   void __user *ptr,
					   void __user *cookie)
{
	struct rb_node **p = &proc->nodes.rb_node;
	struct rb_node *parent = NULL;
	struct binder_node *node;

	while (*p) {
		parent = *p;
		node = rb_entry(parent, struct binder_node, rb_node);

		if (ptr ptr)
			p = &(*p)->rb_left;
		else if (ptr > node->ptr)
			p = &(*p)->rb_right;
		else
			return NULL;
	}

	node = kzalloc(sizeof(*node), GFP_KERNEL);
	if (node == NULL)
		return NULL;
	binder_stats_created(BINDER_STAT_NODE);
	rb_link_node(&node->rb_node, parent, p);
	rb_insert_color(&node->rb_node, &proc->nodes);
	node->debug_id = ++binder_last_id;
	node->proc = proc;
	node->ptr = ptr;
	node->cookie = cookie;
	node->work.type = BINDER_WORK_NODE;
	INIT_LIST_HEAD(&node->work.entry);
	INIT_LIST_HEAD(&node->async_todo);
	binder_debug(BINDER_DEBUG_INTERNAL_REFS,
		     "binder: %d:%d node %d u%p c%p created\n",
		     proc->pid, current->pid, node->debug_id,
		     node->ptr, node->cookie);
	return node;
}

從函數和變量名稱來看,跟紅黑樹有關。先在紅黑樹中查找此結點,後面調用rb_link_node和rb_insert_color

將結點插入到紅黑樹上。這裡的debug_id唯一地標識瞭每一個創建的binder node,傳入的ptr和cookie

均為NULL。然後創建此code的work.entry和async_todo鏈表。對此片段更詳細的解讀,還是需要大神指點。

#ifdef BINDER_MONITOR
		strcpy(binder_context_mgr_node->name, "servicemanager");
#endif

這句就是關鍵瞭,我們明確知道瞭這個binder_context_mgr_node就是Service Manager。這個全局變量

binder_context_mgr_node“代表”的就是service manager,今後看到此變量時得多多留意哦!!!

binder_loop 和 svcmgr_handler

附帶說明下,svcmgr_handle 此時被賦值為NULL。

#define BINDER_SERVICE_MANAGER ((void*) 0)

void *svcmgr = BINDER_SERVICE_MANAGER;

svcmgr_handle = svcmgr;

我們回到主題,看看這個binder_loop函數怎麼寫的。

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    
    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));

int binder_write(struct binder_state *bs, void *data, unsigned len)
{
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (unsigned) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}

由此可見,這個binder_write其實是設置瞭binder_write_read結構體裡面的write部分,而read

部分為空(read_size為0,read_buffer為NULL),然後通過ioctl發送BINDER_WRITE_READ命令。

case BINDER_WRITE_READ: 
...
...
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) 
{
        ret = -EFAULT;
        goto err;
}

...
...

if (bwr.write_size > 0) 
{
	ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
	trace_binder_write_done(ret);
	if (ret < 0) 
        {
		bwr.read_consumed = 0;
		if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
			ret = -EFAULT;
			goto err;
	}
}

if (bwr.read_size > 0) 
{
	ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
	trace_binder_read_done(ret);
	if (!list_empty(&proc->todo))
		wake_up_interruptible(&proc->wait);
	if (ret < 0) {
		if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
			ret = -EFAULT;
			goto err;
	}
}

if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
	ret = -EFAULT;
	goto err;
}

既然是驅動,就涉及到瞭內核空間和用戶空間數據的傳遞。通過copy_from_user將用戶空間的數據拷貝到

內核空間(此處對應驅動程序處理數據之前),copy_to_user將內核空間的數據拷貝到用戶空間

(此處對應驅動程序處理完數據後)。

此處我們的write_size > 0, 而read_size為0。繼續看binder_thread_write函數:

case BC_ENTER_LOOPER:
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
	break;

哦,原來隻是設置瞭binder_thread結構的looper成員的狀態。果真是跟BC_ENTER_LOOPER這個命令

對應啊。繼續看binder_loop的其他部分。

for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }

        res = binder_parse(bs, 0, 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;
        }
    }

這個死循環隻有在出錯情況下才會退出。前面講瞭BC_ENTER_LOOPER的前半段,就是binder_write,

去寫數據(set command)。這裡繼續講其後半段——讀數據( Binder Reply)。

上面那個binder_write執行完瞭,在這裡的loop中,我們看到read_size不為0,如上面所貼出的

case BINDER_WRITE_READ,binder_thread_read函數會被執行。

if (*consumed == 0) {
		if (put_user(BR_NOOP, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
	}

將會把BR_NOOP (也就是Binder Reply NO OPeraterion 放到用戶空間。在這裡順便說下

put_user與copy_to_user的區別,前者是將基本類型數據(1字節,2字節,4字節,8字節)

拷貝到用戶空間,後者可以拷貝任意長度的數據(參數裡面有長度,有數據指針)。

此時thread_todo鏈表應該為空,也沒有transaction吧(個人推測)?那麼wait_for_proc_work

此時應該為FALSE。(關於return_error,thread->todo,請參考binder_get_thread,這個在

binder_ioctl裡面會調用到)

wait_for_proc_work = thread->transaction_stack == NULL &&
				list_empty(&thread->todo);

上面這個應該是設置binder reply的狀態信息。

thread->looper |= BINDER_LOOPER_STATE_WAITING;
        ...
        ...
binder_unlock(__func__);

之所以這裡直接用瞭binder_unlock,是因為之前在binder_ioctl 已經調用瞭binder_lock。

if (non_block) {
    if (!binder_has_thread_work(thread))
		ret = -EAGAIN;
}

static int binder_has_thread_work(struct binder_thread *thread)
{
	return !list_empty(&thread->todo) || thread->return_error != BR_OK ||
		(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}

為瞭確認函數binder_has_thread_work的返回值的真假,我們需要逐個判斷函數裡面 “||”的幾個條件:

(1)list_empty(&thread->todo)

這個thread->todo是在binder_get_thread裡面被賦值的。

INIT_LIST_HEAD(&thread->todo);

static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

static inline int list_empty(const struct list_head *head)
{
	return head->next == head;
}

在插入結點到此鏈表之前,頭結點指針為INIT_LIST_HEAD所指定,滿足list_empty條件。

(2)關於return_error和thread->looper在binder_get_thread裡的賦值

thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;

由於相關地方並未清除或者改變已經設置的狀態位,所以,thread->return_error != BR_OK

判斷結果為FALSE,而(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)判斷

結果為TRUE,於是函數返回TRUE。

binder_lock(__func__);

thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

清除瞭之前設置的WAITING狀態。 那我們繼續往下看,會看到while(1)循環,相關內容如下:

if (!list_empty(&thread->todo))
	w = list_first_entry(&thread->todo, struct binder_work, entry);
else if (!list_empty(&proc->todo) && wait_for_proc_work)
	w = list_first_entry(&proc->todo, struct binder_work, entry);
else {
	if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
		goto retry;
	break;
}

首先,list_empty判斷為TRUE。接著,else if裡面的wait_for_proc_work判斷為FALSE,所以

也不會被執行。else裡面的那個thread->looper & BINDER_LOOPER_STATE_NEED_RETURN

為TRUE,所以goto retry不會被執行,下面的break會被執行,while(1)循環退出。

done:

	*consumed = ptr - buffer;
	if (proc->requested_threads + proc->ready_threads == 0 &&
	    proc->requested_threads_started max_threads &&
	    (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
	     BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
	     /*spawn a new thread if we leave this out */) {
		proc->requested_threads++;
		binder_debug(BINDER_DEBUG_THREADS,
			     "binder: %d:%d BR_SPAWN_LOOPER\n",
			     proc->pid, thread->pid);
		if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
			return -EFAULT;
		binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
	}
	return 0;

requested_threads,ready_threads,requested_threads_started均為0(未見其賦值),

max_threads在binder_ioctl中的case BINDER_SET_MAX_THREADS 被設置。
frameworks/native/libs/binder/ProcessState.cpp中的open_driver函數用來打開binder設備,其中有句:

size_t maxThreads = 15;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);

也就是說max_threads為15。

另外,由於thread->looper的狀態包含BINDER_LOOPER_STATE_ENTERED,所以此處判斷條件為TRUE。

將會把BR_SPAWN_LOOPER傳到用戶空間,然後設置binder reply的相關狀態為BR_SPAWN_LOOPER。

OK,到此binder_ioctl這邊走完瞭,對應的就是binder_loop中的ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

執行完畢瞭,那麼返回到binder_loop中,由於binder_ioctl返回值為0,接下來執行的是binder_parse。

先看看binder_parse傳入的參數:

(1)從bwr.read_buffer = (unsigned) readbuf; 可知,readbuf對應的是read_buffer,它會在ioctl調用

過程中被改變。

binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      void  __user *buffer, int size,
			      signed long *consumed, int non_block)

void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;

if (put_user(BR_NOOP, (uint32_t __user *)ptr))

那個read_buffer對應於binder_thread_read利民的buffer(註意指針類型為void __user *),指針ptr也是

指向這塊buffer,那上面的put_user就是改變裡面的內容。可是問題來瞭,後面不是也有句

(put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) 麼? 由於函數進來時,ptr 等於buffer,

那麼,這個就會改掉之前寫入的BR_NOOP,但這樣做的話,binder_parse中執行的將是:

default:
    ALOGE("parse: OOPS %d\n", cmd);
    return -1;

會導致binder_loop裡面的for循環退出。在這裡,我不得不忽悠下,上面對於binder_thread_read裡面

done標號語句的分析可能有誤。其中,”requested_threads,ready_threads,requested_threads_started

均為0(未見其賦值)“的判斷可能不正確,這裡不能執行if下的語句。還望高人指點!!

OK,那read_buffer裡面就隻有BR_NOOP瞭。

(2) bwr.read_consumed 為多少? 下面兩句會被執行,所以read_consumed為sizeof(uint32_t) 。

ptr += sizeof(uint32_t);

*consumed = ptr - buffer;

(3)傳入的binder_handler函數是? 就是 svcmgr_handler !!

int r = 1;
uint32_t *end = ptr + (size / 4);

case BR_NOOP:
    break;

return r;

分析: 上面那個size / 4 結果為1,所以while 循環隻有一次,隻會處理一個binder reply —— BR_NOOP 。

binder_parse返回1後,回到binder_loop裡的for循環,然後繼續ioctl, binder_parse。

也就是說,在沒有命令到來的時候,這邊處理的REPLY就是BR_NOOP 。

如需轉載,請註明出處: https://blog.csdn.net/happy08god/article/category/1881463

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *