本文首發(fā)于微信公眾號(hào)「后廠技術(shù)官」
關(guān)聯(lián)系列
Android AOSP基礎(chǔ)系列
Android系統(tǒng)啟動(dòng)系列
應(yīng)用進(jìn)程啟動(dòng)系列
Android深入四大組件系列
Android深入理解Context系列
Android深入理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機(jī)制系列
Android輸入系統(tǒng)系列
前言
在上一篇文章中,我們以MediaPlayerService為例,講解了系統(tǒng)服務(wù)是如何注冊(cè)的(addService),既然有注冊(cè)就勢(shì)必要有獲取,但是在了解獲取服務(wù)前,我們最好先了解ServiceManager的啟動(dòng)過(guò)程,這樣更有助于理解系統(tǒng)服務(wù)的注冊(cè)和獲取的過(guò)程。
另外還有一點(diǎn)需要說(shuō)明的是,要想了解ServiceManager的啟動(dòng)過(guò)程,需要查看Kernel Binder部分的源碼,這部分代碼在內(nèi)核源碼中,AOSP源碼是不包括內(nèi)核源碼的,因此需要單獨(dú)下載,見(jiàn)Android AOSP基礎(chǔ)(二)AOSP源碼和內(nèi)核源碼下載這篇文章。
1.ServiceManager的入口函數(shù)
ServiceManager是init進(jìn)程負(fù)責(zé)啟動(dòng)的,具體是在解析init.rc配置文件時(shí)啟動(dòng)的,init進(jìn)程是在系統(tǒng)啟動(dòng)時(shí)啟動(dòng)的,因此ServiceManager亦是如此,不理解init進(jìn)程和init.rc的可以看Android系統(tǒng)啟動(dòng)流程(一)解析init進(jìn)程啟動(dòng)過(guò)程這篇文章。
rc文件內(nèi)部由Android初始化語(yǔ)言編寫(Android Init Language)編寫的腳本,它主要包含五種類型語(yǔ)句:Action、Commands、Services、Options和Import。
在Android 7.0中對(duì)init.rc文件進(jìn)行了拆分,每個(gè)服務(wù)一個(gè)rc文件。ServiceManager的啟動(dòng)腳本在servicemanager.rc中:
frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
class core animation
user system //1
group system readproc
critical //2
onrestart restart healthd
onrestart restart zygote
onrestart restart audioserver
onrestart restart media
onrestart restart surfaceflinger
onrestart restart inputflinger
onrestart restart drm
onrestart restart cameraserver
onrestart restart keystore
onrestart restart gatekeeperd
writepid /dev/cpuset/system-background/tasks
shutdown critical
service用于通知init進(jìn)程創(chuàng)建名為servicemanager的進(jìn)程,這個(gè)servicemanager進(jìn)程執(zhí)行程序的路徑為/system/bin/servicemanager。
注釋1的關(guān)鍵字user說(shuō)明servicemanager是以用戶system的身份運(yùn)行的,注釋2處的critical說(shuō)明servicemanager是系統(tǒng)中的關(guān)鍵服務(wù),關(guān)鍵服務(wù)是不會(huì)退出的,如果退出了,系統(tǒng)就會(huì)重啟,當(dāng)系統(tǒng)重啟時(shí)就會(huì)啟動(dòng)用onrestart關(guān)鍵字修飾的進(jìn)程,比如zygote、media、surfaceflinger等等。
servicemanager的入口函數(shù)在service_manager.c中:
frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv)
{
struct binder_state *bs;//1
union selinux_callback cb;
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
bs = binder_open(driver, 128*1024);//2
...
if (binder_become_context_manager(bs)) {//3
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
...
if (getcon(&service_manager_context) != 0) {
ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
abort();
}
binder_loop(bs, svcmgr_handler);//4
return 0;
}
注釋1處的binder_state結(jié)構(gòu)體用來(lái)存儲(chǔ)binder的三個(gè)信息:
struct binder_state
{
int fd; //binder設(shè)備的文件描述符
void *mapped; //binder設(shè)備文件映射到進(jìn)程的地址空間
size_t mapsize; //內(nèi)存映射后,系統(tǒng)分配的地址空間的大小,默認(rèn)為128KB
};
main函數(shù)主要做了三件事:
1.注釋2處調(diào)用binder_open函數(shù)用于打開binder設(shè)備文件,并申請(qǐng)128k字節(jié)大小的內(nèi)存空間。
2.注釋3處調(diào)用binder_become_context_manager函數(shù),將servicemanager注冊(cè)成為Binder機(jī)制的上下文管理者。
3.注釋4處調(diào)用binder_loop函數(shù),循環(huán)等待和處理client端發(fā)來(lái)的請(qǐng)求。
現(xiàn)在對(duì)這三件事分別進(jìn)行講解。
1.1 打開binder設(shè)備
binder_open函數(shù)用于打開binder設(shè)備文件,并且將它映射到進(jìn)程的地址空間,如下所示。
frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs;
struct binder_version vers;
bs = malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return NULL;
}
bs->fd = open(driver, O_RDWR | O_CLOEXEC);//1
if (bs->fd < 0) {
fprintf(stderr,"binder: cannot open %s (%s)\n",
driver, strerror(errno));
goto fail_open;
}
//獲取Binder的version
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {//2
fprintf(stderr,
"binder: kernel driver version (%d) differs from user space version (%d)\n",
vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
goto fail_open;
}
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);//3
if (bs->mapped == MAP_FAILED) {
fprintf(stderr,"binder: cannot map device (%s)\n",
strerror(errno));
goto fail_map;
}
return bs;
fail_map:
close(bs->fd);
fail_open:
free(bs);
return NULL;
}
注釋1處用于打開binder設(shè)備文件,后面會(huì)進(jìn)行分析。
注釋2處的ioctl函數(shù)用于獲取Binder的版本,如果獲取不到或者內(nèi)核空間和用戶空間的binder不是同一個(gè)版本就會(huì)直接goto到fail_open標(biāo)簽,釋放binder的內(nèi)存空間。
注釋3處調(diào)用mmap函數(shù)進(jìn)行內(nèi)存映射,通俗來(lái)講就是將binder設(shè)備文件映射到進(jìn)程的地址空間,地址空間的大小為mapsize,也就是128K。映射完畢后會(huì)將地址空間的起始地址和大小保存在binder_state結(jié)構(gòu)體中的mapped和mapsize變量中。
這里著重說(shuō)一下open函數(shù),它會(huì)調(diào)用Kernel Binder部分的binder_open函數(shù),這部分源碼位于內(nèi)核源碼中,這里展示的代碼版本為goldfish3.4。
用戶態(tài)和內(nèi)核態(tài)
臨時(shí)插入一個(gè)知識(shí)點(diǎn):用戶態(tài)和內(nèi)核態(tài)
Intel的X86架構(gòu)的CPU提供了0到3四個(gè)特權(quán)級(jí),數(shù)字越小,權(quán)限越高,Linux操作系統(tǒng)中主要采用了0和3兩個(gè)特權(quán)級(jí),分別對(duì)應(yīng)的就是內(nèi)核態(tài)與用戶態(tài)。用戶態(tài)的特權(quán)級(jí)別低,因此進(jìn)程在用戶態(tài)下不經(jīng)過(guò)系統(tǒng)調(diào)用是無(wú)法主動(dòng)訪問(wèn)到內(nèi)核空間中的數(shù)據(jù)的,這樣用戶無(wú)法隨意的進(jìn)入所有進(jìn)程共享的內(nèi)核空間,起到了保護(hù)的作用。下面來(lái)介紹下什么是用戶態(tài)和內(nèi)核態(tài)。
當(dāng)一個(gè)進(jìn)程在執(zhí)行用戶自己的代碼時(shí)處于用戶態(tài),比如open函數(shù),它運(yùn)行在用戶空間,當(dāng)前的進(jìn)程處于用戶態(tài)。
當(dāng)一個(gè)進(jìn)程因?yàn)橄到y(tǒng)調(diào)用進(jìn)入內(nèi)核代碼中執(zhí)行時(shí)就處于內(nèi)核態(tài),比如open函數(shù)通過(guò)系統(tǒng)調(diào)用(__open()函數(shù)),查找到了open函數(shù)在Kernel Binder對(duì)應(yīng)的函數(shù)為binder_open,這時(shí)binder_open運(yùn)行在內(nèi)核空間,當(dāng)前的進(jìn)程由用戶態(tài)切換到內(nèi)核態(tài)。
kernel/goldfish/drivers/staging/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{ //代表Binder進(jìn)程
struct binder_proc *proc;//1
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
//分配內(nèi)存空間
proc = kzalloc(sizeof(*proc), GFP_KERNEL);//2
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);
//binder同步鎖
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;//3
//binder同步鎖釋放
binder_unlock(__func__);
...
return 0;
}
注釋1處的binder_proc結(jié)構(gòu)體代表binder進(jìn)程,用于管理binder的各種信息。注釋2處用于為binder_proc分配內(nèi)存空間。注釋3處將binder_proc賦值給file指針的private_data變量,后面的1.2小節(jié)會(huì)再次提到這個(gè)private_data變量。
1.2 注冊(cè)成為Binder機(jī)制的上下文管理者
binder_become_context_manager函數(shù)用于將servicemanager注冊(cè)成為Binder機(jī)制的上下文管理者,這個(gè)管理者在整個(gè)系統(tǒng)只有一個(gè),代碼如下所示。
frameworks/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
ioctl函數(shù)會(huì)調(diào)用Binder驅(qū)動(dòng)的binder_ioctl函數(shù),binder_ioctl函數(shù)代碼比較多,這里截取BINDER_SET_CONTEXT_MGR的處理部分,代碼如下所示。
kernel/goldfish/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data; //1
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)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);//2
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
switch (cmd) {
...
case BINDER_SET_CONTEXT_MGR:
if (binder_context_mgr_node != NULL) {//3
printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto err;
}
ret = security_binder_set_context_mgr(proc->tsk);
if (ret < 0)
goto err;
if (binder_context_mgr_uid != -1) {//4
if (binder_context_mgr_uid != current->cred->euid) {//5
printk(KERN_ERR "binder: BINDER_SET_"
"CONTEXT_MGR bad uid %d != %d\n",
current->cred->euid,
binder_context_mgr_uid);
ret = -EPERM;
goto err;
}
} else
binder_context_mgr_uid = current->cred->euid;//6
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);//7
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto err;
}
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;
...
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
注釋1處將file指針中的private_data變量賦值給binder_proc,這個(gè)private_data變量在binder_open函數(shù)中講過(guò),是一個(gè)binder_proc結(jié)構(gòu)體。注釋2處的binder_get_thread函數(shù)用于獲取binder_thread,binder_thread結(jié)構(gòu)體指的是binder線程,binder_get_thread函數(shù)內(nèi)部會(huì)從傳入的參數(shù)binder_proc中查找binder_thread,如果查詢到直接返回,如果查詢不到會(huì)創(chuàng)建一個(gè)新的binder_thread并返回。
注釋3處的全局變量binder_context_mgr_node代表的是Binder機(jī)制的上下文管理者對(duì)應(yīng)的一個(gè)Binder對(duì)象,如果它不為NULL,說(shuō)明此前自身已經(jīng)被注冊(cè)為Binder的上下文管理者了,Binder的上下文管理者是不能重復(fù)注冊(cè)的,因此會(huì)goto到err標(biāo)簽。
注釋4處的全局變量binder_context_mgr_uid代表注冊(cè)了Binder機(jī)制上下文管理者的進(jìn)程的有效用戶ID,如果它的值不為-1,說(shuō)明此前已經(jīng)有進(jìn)程注冊(cè)Binder的上下文管理者了,因此在注釋5處判斷當(dāng)前進(jìn)程的有效用戶ID是否等于binder_context_mgr_uid,不等于就goto到err標(biāo)簽。
如果不滿足注釋4的條件,說(shuō)明此前沒(méi)有進(jìn)程注冊(cè)Binder機(jī)制的上下文管理者,就會(huì)在注釋6處將當(dāng)前進(jìn)程的有效用戶ID賦值給全局變量binder_context_mgr_uid,另外還會(huì)在注釋7處調(diào)用binder_new_node函數(shù)創(chuàng)建一個(gè)Binder對(duì)象并賦值給全局變量binder_context_mgr_node。
1.3 循環(huán)等待和處理client端發(fā)來(lái)的請(qǐng)求
servicemanager成功注冊(cè)成為Binder機(jī)制的上下文管理者后,servicemanager就是Binder機(jī)制的“總管”了,它需要在系統(tǒng)運(yùn)行期間處理client端的請(qǐng)求,由于client端的請(qǐng)求不確定何時(shí)發(fā)送,因此需要通過(guò)無(wú)限循環(huán)來(lái)實(shí)現(xiàn),實(shí)現(xiàn)這一需求的函數(shù)就是binder_loop。
frameworks/native/cmds/servicemanager/binder.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));//1
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//2
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);//3
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;
}
}
}
注釋1處將BC_ENTER_LOOPER指令通過(guò)binder_write函數(shù)寫入到Binder驅(qū)動(dòng)中,這樣當(dāng)前線程(ServiceManager的主線程)就成為了一個(gè)Binder線程,這樣就可以處理進(jìn)程間的請(qǐng)求了。
在無(wú)限循環(huán)中不斷的調(diào)用注釋2處的ioctl函數(shù),它不斷的使用BINDER_WRITE_READ指令查詢Binder驅(qū)動(dòng)中是否有新的請(qǐng)求,如果有就交給注釋3處的binder_parse函數(shù)處理。如果沒(méi)有,當(dāng)前線程就會(huì)在Binder驅(qū)動(dòng)中睡眠,等待新的進(jìn)程間請(qǐng)求。
由于binder_write函數(shù)的調(diào)用鏈中涉及到了內(nèi)核空間和用戶空間的交互,因此這里著重講解下。
frameworks/native/cmds/servicemanager/binder.c
int binder_write(struct binder_state *bs, void *data, size_t len)
{
struct binder_write_read bwr;//1
int res;
bwr.write_size = len;
bwr.write_consumed = 0;
bwr.write_buffer = (uintptr_t) data;//2
bwr.read_size = 0;
bwr.read_consumed = 0;
bwr.read_buffer = 0;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//3
if (res < 0) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n",
strerror(errno));
}
return res;
}
注釋1處定義binder_write_read結(jié)構(gòu)體,接下來(lái)的代碼對(duì)bwr進(jìn)行賦值,其中需要注意的是,注釋2處的data的值為BC_ENTER_LOOPER。注釋3處的ioctl函數(shù)將會(huì)bwr中的數(shù)據(jù)發(fā)送給binder驅(qū)動(dòng),我們已經(jīng)知道了ioctl函數(shù)在Kernel Binder中對(duì)應(yīng)的函數(shù)為binder_ioctl,此前分析過(guò)這個(gè)函數(shù),這里截取BINDER_WRITE_READ命令處理部分。
kernel/goldfish/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
void __user *ubuf = (void __user *)arg;
...
switch (cmd) {
case BINDER_WRITE_READ: {
struct binder_write_read bwr;
if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto err;
}
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {//1
ret = -EFAULT;
goto err;
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
proc->pid, thread->pid, bwr.write_size, bwr.write_buffer,
bwr.read_size, bwr.read_buffer);
if (bwr.write_size > 0) {//2
ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);//3
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto err;
}
}
...
binder_debug(BINDER_DEBUG_READ_WRITE,
"binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
bwr.read_consumed, bwr.read_size);
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {//4
ret = -EFAULT;
goto err;
}
break;
}
...
return ret;
}
注釋1處的copy_from_user函數(shù),在本系列的第一篇文章Android Binder原理(一)學(xué)習(xí)Binder前必須要了解的知識(shí)點(diǎn)提過(guò)。在這里,它用于將把用戶空間數(shù)據(jù)ubuf拷貝出來(lái)保存到內(nèi)核數(shù)據(jù)bwr(binder_write_read結(jié)構(gòu)體)中。
注釋2處,bwr的輸入緩存區(qū)有數(shù)據(jù)時(shí),會(huì)調(diào)用注釋3處的binder_thread_write函數(shù)來(lái)處理BC_ENTER_LOOPER協(xié)議,其內(nèi)部會(huì)將目標(biāo)線程的狀態(tài)設(shè)置為BINDER_LOOPER_STATE_ENTERED,這樣目標(biāo)線程就是一個(gè)Binder線程。
注釋4處通過(guò)copy_to_user函數(shù)將內(nèi)核空間數(shù)據(jù)bwr拷貝到用戶空間。
2.總結(jié)
ServiceManager的啟動(dòng)過(guò)程實(shí)際上就是分析ServiceManager的入口函數(shù),在入口函數(shù)中主要做了三件事,本篇文章深入到內(nèi)核源碼來(lái)對(duì)這三件逐一進(jìn)行分析,由于涉及的函數(shù)比較多,這篇文章只介紹了我們需要掌握的,剩余大家可以自行閱讀源碼,比如binder_thread_write、copy_to_user函數(shù)。
更多的內(nèi)容請(qǐng)關(guān)注我的獨(dú)立博客的知識(shí)體系:
http://liuwangshu.cn/system/
分享前沿技術(shù)、技術(shù)資訊、行業(yè)秘聞,技術(shù)管理,助力10萬(wàn)+程序員成長(zhǎng)為技術(shù)官和架構(gòu)師。
[圖片上傳失敗...(image-6b3192-1581019793755)]