本文主要分析ServiceManager
系統(tǒng)服務(wù)管理進(jìn)程對binder的管理流程。
大綱:
- 揭開Binder面紗
- Binder的管理
- 1 打開binder驅(qū)動
- 2 成為系統(tǒng)唯一的上下文
- 3 進(jìn)入binder循環(huán)
- 4 系統(tǒng)服務(wù)的注冊和獲取
- 總結(jié)
- 參考資料
本文約3.7k字,閱讀大約15分鐘已添。
Android源碼基于8.0规婆。
揭開Binder面紗
Binder跟鍵盤纬朝、顯示器一樣屬于一種外設(shè)(沒有實(shí)體的外設(shè))苹熏。由于外設(shè)種類繁多挽霉,操作系統(tǒng)如Linux抽象出文件視圖來方便用戶使用外設(shè)翩蘸。即對用戶來說所意,通過讀寫外設(shè)文件,讓操作系統(tǒng)將指令發(fā)送給外設(shè)控制器,來實(shí)現(xiàn)對外設(shè)的操作扶踊。
在Linux中泄鹏,各種外設(shè)文件放在/dev
目錄下:
不過這些文件并不是像Windows上的那些外設(shè)驅(qū)動程序,而是提供給用戶去訪問外設(shè)的一個(gè)端口(就跟文件訪問一樣)秧耗,如:
- /dev/console:系統(tǒng)控制臺
- /dev/mem:物理內(nèi)存的全鏡像命满。可以用來直接存取物理內(nèi)存绣版。
- /dev/kmem:內(nèi)核看到的虛擬內(nèi)存的全鏡像胶台。可以用來訪問內(nèi)核中的內(nèi)容杂抽。
- /dev/tty0:虛擬終端
- ...
Linux抽象出文件視圖诈唬,為用戶提供統(tǒng)一接口,一段簡單的操作外設(shè)的程序如下:
//打開 /dev 下的外設(shè)文件
int fd = open(“/dev/xxx”);
for (int i = 0; i < 10; i++) {
//進(jìn)行讀寫操作
write(fd,i,sizeof(int));
}
//關(guān)閉文件
close(fd);
用戶讀寫外設(shè)文件缩麸,Linux會通過外設(shè)文件找到外設(shè)控制器的地址铸磅、內(nèi)容格式等信息,向他發(fā)送合適的指令來操作外設(shè)杭朱。
現(xiàn)在我們通過adb shell
進(jìn)入Android設(shè)備阅仔,看下他的/dev
目錄長啥樣:
可以看到有binder,標(biāo)黃部分的3個(gè)分別是binder
弧械、hwbinder
八酒、vndbinder
,我們只關(guān)注binder
就行了刃唐。
從「一圖摸清Android應(yīng)用進(jìn)程的啟動」一文可知羞迷,在應(yīng)用程序啟動binder線程池時(shí),ProcessState.cpp有這么一段代碼画饥,
//ProcessState.cpp
sp<ProcessState> ProcessState::self(){
//傳入 binder 外設(shè)文件路徑
gProcess = new ProcessState("/dev/binder");
return gProcess;
}
//ProcessState構(gòu)造函數(shù)
ProcessState::ProcessState(const char *driver)
//路徑賦給 mDriverName
: mDriverName(String8(driver))
//1. 打開 binder 驅(qū)動
, mDriverFD(open_driver(driver))
,//...
{
//2. 映射內(nèi)存
mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
}
我們看下打開binder驅(qū)動的open_driver
函數(shù)衔瓮,
//ProcessState.cpp
static int open_driver(const char *driver){
//打開外設(shè)文件 /dev/binder
int fd = open(driver, O_RDWR | O_CLOEXEC);
int vers = 0;
//獲取 binder 版本進(jìn)行檢查
status_t result = ioctl(fd, BINDER_VERSION, &vers);
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
//設(shè)置 binder 最大線程數(shù)為 15
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
//返回 int 類型的 fd 給 mDriverFD
return fd;
}
看起來是不是跟Linux操作外設(shè)的那段程序很像,只不過這里的讀寫操作由write
換成了ioctl
抖甘,
在計(jì)算機(jī)中热鞍,ioctl(input/output control)是一個(gè)專用于設(shè)備輸入輸出操作的系統(tǒng)調(diào)用,該調(diào)用傳入一個(gè)跟設(shè)備有關(guān)的請求碼,系統(tǒng)調(diào)用的功能完全取決于請求碼衔彻。舉個(gè)例子薇宠,CD-ROM驅(qū)動程序可以彈出光驅(qū),它就提供了一個(gè)對應(yīng)的Ioctl請求碼米奸。設(shè)備無關(guān)的請求碼則提供了內(nèi)核調(diào)用權(quán)限昼接。ioctl這名字第一次出現(xiàn)在Unix第七版中爽篷,他在很多類unix系統(tǒng)(比如Linux悴晰、Mac OSX等)都有提供,不過不同系統(tǒng)的請求碼對應(yīng)的設(shè)備有所不同。
-- 引用自百科 ioctl
可見ioctl
是一個(gè)可以控制設(shè)備I/O通道的系統(tǒng)調(diào)用铡溪,通過它用戶空間可以跟設(shè)備驅(qū)動溝通。
至于為什么要有ioctl
,主要是為非標(biāo)準(zhǔn)設(shè)備考慮的(如binder就是一種非標(biāo)準(zhǔn)外設(shè))伪很,詳見百科 ioctl 背景毛甲。
ioctl
函數(shù)如下:
int ioctl(int fd, ind cmd, …);
第一個(gè)參數(shù)fd
是文件描述符哈扮,如binder外設(shè)文件纬纪;
第二個(gè)參數(shù)cmd
則是控制命令,如指令BINDER_SET_MAX_THREADS
是“設(shè)置線程數(shù)”滑肉,最后的省略號則是各指令所需的參數(shù)包各,如maxThreads
表示最大線程數(shù)為 15。
指令BINDER_SET_MAX_THREADS
的定義如下:
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
_IOW
是一個(gè)宏靶庙,Linux內(nèi)核提供了一些宏來方便用戶定義指令(傳入各種參數(shù)進(jìn)行包裝):
// nr為序號问畅,datatype 為數(shù)據(jù)類型,如 int
_IO(type, nr ) //沒有參數(shù)的命令
_IOR(type, nr, datatype) //從驅(qū)動中讀數(shù)據(jù)
_IOW(type, nr, datatype) //寫數(shù)據(jù)到驅(qū)動
_IOWR(type,nr, datatype) //雙向傳送
名字很好理解,就是 io read write的縮寫六荒。
對binder的了解暫且到這护姆,只需知道他是一個(gè)外設(shè),以文件形式通過ioctl
來操作就行了掏击。
Binder的管理
從「一圖摸清Android系統(tǒng)服務(wù)」一文可知卵皂,init進(jìn)程會啟動運(yùn)行在獨(dú)立進(jìn)程的ServiceManager
服務(wù)來統(tǒng)一管理系統(tǒng)服務(wù)的注冊和獲取。
在ServiceManager
的入口函數(shù)即service_manager.c的main函數(shù)中砚亭,
//frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv){
char *driver = "/dev/binder";
//1. 打開 binder 驅(qū)動
struct binder_state *bs = binder_open(driver, 128*1024);
//2. 讓自己成為整個(gè)系統(tǒng)唯一的上下文管理器渐裂,
// 這樣其他進(jìn)程就能找到 ServiceManager 來注冊服務(wù)了
binder_become_context_manager(bs);
//3. 進(jìn)入binder循環(huán),等待系統(tǒng)服務(wù)的注冊和查找請求
binder_loop(bs, svcmgr_handler);
}
下面分析這3個(gè)步驟钠惩。
1 打開binder驅(qū)動
128 * 1024即128kb是mapsize柒凉,表示把binder驅(qū)動文件的128kb
映射到內(nèi)存空間,而在「一圖摸清Android應(yīng)用進(jìn)程的啟動」一文可知應(yīng)用進(jìn)程使用的mapsize大小為BINDER_VM_SIZE
即1MB-8kb
篓跛,可見兩者的大小是不同的膝捞,
//ProcessState.cpp
//一次Binder通信最大可以傳輸?shù)拇笮∈?1MB-4KB*2
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
//映射內(nèi)存
mmap(..., BINDER_VM_SIZE, ...);
回到ServiceManager
,看binder_open()的內(nèi)部實(shí)現(xiàn)binder.c:
//frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize){
struct binder_state *bs;
//分配空間
bs = malloc(sizeof(*bs));
//打開 binder 驅(qū)動愧沟,得到int類型的文件描述符 fd
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
//記錄傳入的 128kb
bs->mapsize = mapsize;
//映射內(nèi)存蔬咬,記錄內(nèi)存映射區(qū)的指針
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
return bs;
}
mmap可以將一個(gè)文件或者其它對象映射進(jìn)內(nèi)存,函數(shù)原型:
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
各參數(shù)如下:
- start:映射區(qū)的開始地址沐寺,傳 NULL 表示由系統(tǒng)決定映射區(qū)的起始地址
- length:映射區(qū)的長度林艘,傳 128kb
- prot:期望的內(nèi)存保護(hù)標(biāo)志,傳 PROT_READ 只讀
- flags:指定映射對象的類型混坞,映射選項(xiàng)和映射頁是否可以共享狐援。傳 MAP_PRIVATE 建立一個(gè)寫入時(shí)拷貝的私有映射钢坦,內(nèi)存區(qū)域的寫入不會影響到原文件
- fd:有效的文件描述符,一般是由open()函數(shù)返回
- offset:被映射對象內(nèi)容的起點(diǎn)啥酱,傳 0
- return:成功執(zhí)行時(shí)爹凹,mmap()返回被映射區(qū)的指針
mmap會根據(jù)入?yún)inder驅(qū)動文件的一部分映射到內(nèi)存空間,然后返回該內(nèi)存空間的指針镶殷。
最后binder_open()返回的bs結(jié)構(gòu)體如下:
//frameworks/native/cmds/servicemanager/binder.c
struct binder_state{
// binder 驅(qū)動文件描述符
int fd;
//由 mmap 得到的內(nèi)存映射區(qū)的指針
void *mapped;
// 128kb
size_t mapsize;
};
2 成為系統(tǒng)唯一的上下文
ServiceManager
讓自己成為整個(gè)系統(tǒng)唯一的上下文管理器禾酱,這樣其他進(jìn)程就能找到ServiceManager
來注冊服務(wù)了,
//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
調(diào)用绘趋,向binder驅(qū)動發(fā)送一個(gè)指令“我ServiceManager
已成為全局上下文管理器”颤陶。
binder驅(qū)動層代碼暫不跟進(jìn),我們只需知道:
一般情況下陷遮,應(yīng)用層的每個(gè)binder實(shí)體都會在binder驅(qū)動層對應(yīng)一個(gè)binder_node節(jié)點(diǎn)指郁,然而
ServiceManager
的binder_context_mgr_node比較特殊,它沒有對應(yīng)的應(yīng)用層binder實(shí)體拷呆。在整個(gè)系統(tǒng)里闲坎,它是如此特殊,以至于系統(tǒng)規(guī)定茬斧,任何應(yīng)用都必須使用句柄0
來跨進(jìn)程地訪問它腰懂。-- 引用自 博客 - 紅茶一杯話Binder
3 進(jìn)入binder循環(huán)
進(jìn)入binder循環(huán),等待系統(tǒng)服務(wù)的注冊和查找請求项秉,
//frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
//readbuf 用于跟 binder 驅(qū)動互傳數(shù)據(jù)
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
//指令:binder 開始循環(huán)
readbuf[0] = BC_ENTER_LOOPER;
//向 binder 發(fā)送該指令
//內(nèi)部會執(zhí)行 ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) { //進(jìn)入循環(huán)
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
//向 binder 發(fā)送 讀寫指令
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
//解析從 binder 讀來的數(shù)據(jù)绣溜,交給傳入的函數(shù) svcmgr_handler 處理
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}
其中binder_parse
解析邏輯如下:
//frameworks/native/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func){
int r = 1;
//計(jì)算能讀完最后一條指令的偏移
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
//不斷從 readbuf 讀取 binder 回傳的指令
uint32_t cmd = *(uint32_t *) ptr;
//每讀完一條,進(jìn)行偏移
ptr += sizeof(uint32_t);
switch(cmd) { //處理各種指令
case BR_TRANSACTION_COMPLETE:
break;
case BR_TRANSACTION:
//轉(zhuǎn)交給傳入的 svcmgr_handler 函數(shù)處理
res = func(bs, txn, &msg, &reply);
//...
break;
case BR_REPLY:
//...
break;
//...
}
}
return r;
}
然后看到傳入的處理函數(shù)svcmgr_handler
娄蔼,
//frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply){
//...省略數(shù)據(jù)包裝和解析的邏輯
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//查找系統(tǒng)服務(wù)
handle = do_find_service(...);
bio_put_ref(reply, handle);
case SVC_MGR_ADD_SERVICE:
//添加系統(tǒng)服務(wù)
do_add_service(...);
}
}
svcmgr_handler
函數(shù)會根據(jù)不同的語義碼code來執(zhí)行相應(yīng)邏輯怖喻,如查找系統(tǒng)服務(wù)的do_find_service
、添加系統(tǒng)服務(wù)的do_add_service
岁诉。
至此锚沸,可以看出ServiceManager
的binder啟動流程:
4 系統(tǒng)服務(wù)的注冊和獲取
下面簡要分析一下系統(tǒng)服務(wù)的注冊和獲取,在「一圖摸清Android系統(tǒng)服務(wù)」一文已對上層邏輯進(jìn)行介紹涕癣,這里直接看do_add_service
和do_find_service
兩個(gè)方法哗蜈。
1.添加系統(tǒng)服務(wù)do_add_service
。
struct svcinfo *svclist 以鏈表的形式記錄了所有的系統(tǒng)服務(wù)坠韩,其結(jié)構(gòu)體如下:
//frameworks/native/cmds/servicemanager/service_manager.c
struct svcinfo{
//下一個(gè)服務(wù)
struct svcinfo *next;
//服務(wù)的 binder 句柄值
uint32_t handle;
struct binder_death death;
int allow_isolated;
size_t len;
//服務(wù)注冊時(shí)取的名字
uint16_t name[0];
};
然后看到do_add_service
距潘,
//frameworks/native/cmds/servicemanager/service_manager.c
struct svcinfo *svclist; //鏈表
int do_add_service(struct binder_state *bs,
const uint16_t *s, size_t len,
uint32_t handle, uid_t uid, int allow_isolated,
pid_t spid){
if (!svc_can_register(s, len, spid, uid)) {
//判斷是否可以注冊系統(tǒng)服務(wù)
//只有 root 進(jìn)程、SystemServer 進(jìn)程只搁、有在 allowed[] 數(shù)組中聲明的進(jìn)程可以
return -1;
}
//分配空間給新的節(jié)點(diǎn)
struct svcinfo *si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
//記錄服務(wù)的 binder 句柄值
si->handle = handle;
si->len = len;
//記錄服務(wù)注冊時(shí)取的名字
memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
//追加一個(gè)結(jié)束符
si->name[len] = '\0';
//...還有各種賦值
//新節(jié)點(diǎn)的 next 指向鏈表
si->next = svclist;
//鏈表的頭插法
svclist = si;
}
如下音比,
至于系統(tǒng)服務(wù)int類型的binder句柄值handle怎么來的,是由binder驅(qū)動層為我們分配氢惋,然后包裝成特定的數(shù)據(jù)結(jié)構(gòu)回傳給我們的洞翩。
可見ServiceManager
用鏈表svclist
管理各系統(tǒng)服務(wù)的binder句柄稽犁,結(jié)構(gòu)體是svcinfo
;
而對應(yīng)到binder驅(qū)動層菱农,則是用鏈表binder_procs
管理的,結(jié)構(gòu)體是binder_proc
柿估,在/drivers/android/binder.c中循未,
//drivers/android/binder.c
//鏈表頭結(jié)點(diǎn)
static HLIST_HEAD(binder_procs);
//結(jié)構(gòu)體
struct binder_proc {
//鏈表普通節(jié)點(diǎn),由他的 next 和 pprev 串起鏈表
struct hlist_node proc_node;
//4棵紅黑樹秫舌,rb = red black
//記錄執(zhí)行傳輸動作的線程信息 binder_thread
struct rb_root threads;
//記錄 binder 實(shí)體 binder_node
struct rb_root nodes;
//記錄 binder 代理 binder_ref
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
};
這里用了HLIST_HEAD和hlist_node來串起鏈表的妖,binder驅(qū)動層的代碼暫不展開,感興趣可以閱讀「紅茶一杯話Binder傳輸機(jī)制篇」足陨。
2.查找系統(tǒng)服務(wù)do_find_service
嫂粟。
//frameworks/native/cmds/servicemanager/service_manager.c
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid){
//遍歷鏈表找到節(jié)點(diǎn)
struct svcinfo *si = find_svc(s, len);
//返回 binder 句柄值
return si->handle;
}
struct svcinfo *find_svc(const uint16_t *s16, size_t len){
struct svcinfo *si;
//遍歷鏈表找到節(jié)點(diǎn)
for (si = svclist; si; si = si->next) {
if ((len == si->len) &&
!memcmp(s16, si->name, len * sizeof(uint16_t))) {
return si;
}
}
return NULL;
}
綜上,查找系統(tǒng)服務(wù)的do_find_service
和添加系統(tǒng)服務(wù)的do_add_service
如下圖:
總結(jié)
ServiceManager
作為管理系統(tǒng)服務(wù)的進(jìn)程墨缘,經(jīng)過打開binder驅(qū)動星虹、注冊成為系統(tǒng)唯一的上下文、進(jìn)入binder循環(huán)3個(gè)核心步驟镊讼,便開始支持系統(tǒng)服務(wù)的注冊和獲取宽涌。系統(tǒng)服務(wù)的注冊和獲取過程基于binder機(jī)制實(shí)現(xiàn)IPC通信,binder的本質(zhì)就是一個(gè)外設(shè)蝶棋,以文件形式通過ioctl
系統(tǒng)調(diào)用來操作卸亮。
留下2個(gè)疑問繼續(xù)探討:
- binder句柄的
遠(yuǎn)程轉(zhuǎn)本地
- one way異步模式和他的串行調(diào)用(async_todo)、同步模式的并行調(diào)用
系列文章:
- 圖解 | Android系統(tǒng)的啟動
- 圖解 | 一圖摸清Android系統(tǒng)服務(wù)
- 圖解 | 一圖摸清Android應(yīng)用進(jìn)程的啟動
- 圖解 | Binder淺析(一)
補(bǔ)充
-
系統(tǒng)服務(wù)由
ServiceManager
進(jìn)程管理玩裙,但用戶自定義的Service組件兼贸,bindService時(shí)的onServiceConnected
回調(diào)拿到的IBinder句柄,是由SystemServer
進(jìn)程的AMS
管理的吃溅,后面再開篇分析了溶诞。這兩個(gè)進(jìn)程容易搞混,再貼出來鞏固一下...
image
參考資料
- 書籍 - Android系統(tǒng)源代碼情景分析
- 視頻 - 哈工大操作系統(tǒng)IO與顯示器
- 百科 - ioctl
- 博客 - 紅茶一杯話Binder