本文首發(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)過程,這樣更有助于理解系統(tǒng)服務(wù)的注冊(cè)和獲取的過程。
另外還有一點(diǎn)需要說明的是,要想了解ServiceManager的啟動(dòng)過程檩小,需要查看Kernel Binder部分的源碼烟勋,這部分代碼在內(nèi)核源碼中阻肿,AOSP源碼是不包括內(nèi)核源碼的畜疾,因此需要單獨(dú)下載,見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)過程這篇文章。
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說明servicemanager是以用戶system的身份運(yùn)行的泊碑,注釋2處的critical說明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)體用來存儲(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ā)來的請(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)存映射似枕,通俗來講就是將binder設(shè)備文件映射到進(jìn)程的地址空間,地址空間的大小為mapsize年柠,也就是128K凿歼。映射完畢后會(huì)將地址空間的起始地址和大小保存在binder_state結(jié)構(gòu)體中的mapped和mapsize變量中。
這里著重說一下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)過系統(tǒng)調(diào)用是無(wú)法主動(dòng)訪問到內(nèi)核空間中的數(shù)據(jù)的态兴,這樣用戶無(wú)法隨意的進(jìn)入所有進(jìn)程共享的內(nèi)核空間狠持,起到了保護(hù)的作用。下面來介紹下什么是用戶態(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ù)通過系統(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ù)中講過,是一個(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脆荷,說明此前自身已經(jīng)被注冊(cè)為Binder的上下文管理者了悴品,Binder的上下文管理者是不能重復(fù)注冊(cè)的,因此會(huì)goto到err標(biāo)簽简烘。
注釋4處的全局變量binder_context_mgr_uid代表注冊(cè)了Binder機(jī)制上下文管理者的進(jìn)程的有效用戶ID苔严,如果它的值不為-1,說明此前已經(jīng)有進(jìn)程注冊(cè)Binder的上下文管理者了孤澎,因此在注釋5處判斷當(dāng)前進(jìn)程的有效用戶ID是否等于binder_context_mgr_uid届氢,不等于就goto到err標(biāo)簽。
如果不滿足注釋4的條件覆旭,說明此前沒有進(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ā)來的請(qǐng)求
servicemanager成功注冊(cè)成為Binder機(jī)制的上下文管理者后寂祥,servicemanager就是Binder機(jī)制的“總管”了,它需要在系統(tǒng)運(yùn)行期間處理client端的請(qǐng)求七兜,由于client端的請(qǐng)求不確定何時(shí)發(fā)送丸凭,因此需要通過無(wú)限循環(huán)來實(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指令通過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ù)處理涛菠。如果沒有莉御,當(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)體言疗,接下來的代碼對(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,此前分析過這個(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)提過碰缔。在這里账劲,它用于將把用戶空間數(shù)據(jù)ubuf拷貝出來保存到內(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ù)來處理BC_ENTER_LOOPER協(xié)議,其內(nèi)部會(huì)將目標(biāo)線程的狀態(tài)設(shè)置為BINDER_LOOPER_STATE_ENTERED梗肝,這樣目標(biāo)線程就是一個(gè)Binder線程榛瓮。
注釋4處通過copy_to_user函數(shù)將內(nèi)核空間數(shù)據(jù)bwr拷貝到用戶空間。
2.總結(jié)
ServiceManager的啟動(dòng)過程實(shí)際上就是分析ServiceManager的入口函數(shù)巫击,在入口函數(shù)中主要做了三件事禀晓,本篇文章深入到內(nè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)]