Binder系列第一篇:《從getSystemService()開始,開擼Binder通訊機(jī)制》http://www.reibang.com/p/1050ce12bc1e
Binder系列第二篇:《能用【白話文】來分析Binder通訊機(jī)制哄孤?》http://www.reibang.com/p/fe816777f2cf
Binder系列第三篇:《Binder機(jī)制之一次響應(yīng)的故事》http://www.reibang.com/p/4fba927dce05
CoorChice在上次的文章 《從getSystemService()開始耕驰,開擼Binder通訊機(jī)制:http://www.reibang.com/p/1050ce12bc1e》 中留了一些關(guān)于Binder的坑,也許大家看的時(shí)候有些云里霧里的录豺,這篇文章朦肘,CoorChice就開始填這些坑了。并開始逐步的深入Binder核心機(jī)制双饥,讓你對Android中最重要的部分有所了解媒抠。
好了,咱們發(fā)車了咏花!
由open_driver()開始
在
《從getSystemService()開始趴生,開擼Binder通訊機(jī)制:http://www.reibang.com/p/1050ce12bc1e》這篇文章中,相信大家應(yīng)該看到在/frameworks/native/libs/binder/ProcessState.cpp
文件中昏翰,有這樣一段代碼苍匆。
static int open_driver()
{
//打開"/dev/binder"Binder驅(qū)動(dòng)文件,并獲得其描述符
int fd = open("/dev/binder", O_RDWR);
...
//獲取Binder驅(qū)動(dòng)程序的版本號
status_t result = ioctl(fd, BINDER_VERSION, &vers);
...
size_t maxThreads = 15;
//告知驅(qū)動(dòng)程序最多可以啟動(dòng)15條線程處理事物
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
...
return fd;
}
它在ProcessState創(chuàng)建的時(shí)候會被調(diào)用棚菊。它肩負(fù)了一項(xiàng)重要的使命浸踩,就是在該進(jìn)程中打開/dev/binder
設(shè)備文件,然后獲得該設(shè)備文件的描述符统求〖焱耄可以看到,打開設(shè)備文件是通過open()
函數(shù)實(shí)現(xiàn)的码邻,它是怎么實(shí)現(xiàn)的呢折剃?
用戶空間函數(shù)與Binder驅(qū)動(dòng)函數(shù)
首先打開/drivers/staging/android/binder.c
文件,然后找到下面這個(gè)結(jié)構(gòu)體:
//這個(gè)結(jié)構(gòu)體中定義了文件操作符與Binder驅(qū)動(dòng)的對應(yīng)函數(shù)關(guān)聯(lián)
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
//用戶空間的mmap()操作像屋,會引起B(yǎng)inder驅(qū)動(dòng)的binder_mmap()函數(shù)的調(diào)用
.mmap = binder_mmap,
//用戶空間的open()操作怕犁,會引起B(yǎng)inder驅(qū)動(dòng)的binder_open()函數(shù)的調(diào)用
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
這個(gè)結(jié)構(gòu)體會作為下面這個(gè)結(jié)構(gòu)體的一個(gè)成員,在Binder驅(qū)動(dòng)注冊的時(shí)候被和Linux定義的文件操作符關(guān)聯(lián)。
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
//定義設(shè)備節(jié)點(diǎn)文件名奏甫。這里Binder驅(qū)動(dòng)設(shè)備的文件路徑即為/dev/binder
.name = "binder",
//關(guān)聯(lián)Linux文件操作符
.fops = &binder_fops
};
這樣戈轿,在Binder驅(qū)動(dòng)設(shè)備注冊完成后,在用戶空間調(diào)用poll()
扶檐、open()
等函數(shù)的時(shí)候凶杖,Binder驅(qū)動(dòng)的對應(yīng)函數(shù)就會被調(diào)用胁艰。
open()函數(shù)的真面目
現(xiàn)在款筑,我們知道了,當(dāng)我們在用戶空間調(diào)用open()
函數(shù)時(shí)腾么,Binder驅(qū)動(dòng)層的binder_open()
函數(shù)會被隨之調(diào)用奈梳。我們看看binder_open()
函數(shù)做了些什么?
//用戶空間調(diào)用open()實(shí)際調(diào)用的是這里
//參數(shù)為打開設(shè)備文件后傳遞過來的
static int binder_open(struct inode *nodp, struct file *filp)
{
//binder_proc儲存進(jìn)程信息的結(jié)構(gòu)體
//注意解虱,這個(gè)進(jìn)程結(jié)構(gòu)體是存在于Binder內(nèi)核空間中的
struct binder_proc *proc;
//將當(dāng)前進(jìn)程的信息儲存到binder_proc中
...
//鎖定同步
binder_lock(__func__);
...
//將該進(jìn)程上下文信息proc保存到Binder驅(qū)動(dòng)的進(jìn)程樹中
//以便查找使用
hlist_add_head(&proc->proc_node, &binder_procs);
// 設(shè)置進(jìn)程id
proc->pid = current->group_leader->pid;
...
// 將進(jìn)程信息結(jié)構(gòu)體賦值給文件私有數(shù)據(jù)
filp->private_data = proc;
//釋放鎖
binder_unlock(__func__);
...
//在/proc/binder/proc下創(chuàng)建名為進(jìn)程id的文件攘须,便于查看進(jìn)程的通訊
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
return 0;
}
這個(gè)函數(shù)主要的作用是為打開了/dev/binder
設(shè)備文件的進(jìn)程生成一個(gè)專屬的進(jìn)程信息體,然后保存到驅(qū)動(dòng)中殴泰。這樣于宙,該進(jìn)程就能和Binder驅(qū)動(dòng)互動(dòng)了。
可能有的細(xì)心的同學(xué)會發(fā)現(xiàn)悍汛,open()
函數(shù)會返回設(shè)備表述符捞魁,而binder_open()
函數(shù)看起來只會返回0啊离咐?CoorChice在前面說過谱俭,binder_open()
只是和open()
產(chǎn)生了關(guān)聯(lián),但實(shí)際打開設(shè)備文件的操作還是Linux再進(jìn)行宵蛀。想必你也可以看到昆著,binder_open()
函數(shù)的參數(shù)是在設(shè)備文件打開后才可能獲取的。所以术陶,這個(gè)設(shè)備描述符應(yīng)該是由Linux來分配給進(jìn)程的凑懂。
下面,接著看看在open_driver()
中出現(xiàn)的另一個(gè)函數(shù)ioctl()
梧宫。
ioctl()函數(shù)的真面目
如果你理解了上面的open()
函數(shù)征候,那么自然就知道,用戶空間的ioctl()
函數(shù)會引起B(yǎng)inder驅(qū)動(dòng)層的binder_ioctl()
函數(shù)的調(diào)用祟敛。我們就看看驅(qū)動(dòng)層的這個(gè)函數(shù)做了什么疤坝?這是一個(gè)十分重要的函數(shù)啊馆铁!
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
// 從file結(jié)構(gòu)體中取出進(jìn)程信息
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
//表明arg是一個(gè)用戶空間地址
void __user *ubuf = (void __user *)arg;
...
//取出線程信息
thread = binder_get_thread(proc);
...
switch (cmd) {
...
}
...
}
同樣跑揉,用戶層調(diào)用ioctl(fd, cmd, arg)
函數(shù),會先由Linux內(nèi)核根據(jù)設(shè)備描述符fd獲得對應(yīng)的設(shè)備文件體file,然后調(diào)用Binder驅(qū)動(dòng)的binder_ioctl()
函數(shù)历谍。
這個(gè)函數(shù)比較重要现拒,CoorChice再一步一步的解析一下。
獲得進(jìn)程信息體
首先Binder驅(qū)動(dòng)根據(jù)傳入的文件體獲得其中的進(jìn)程信息望侈。
struct binder_proc *proc = filp->private_data;
還記得在binder_open()
中生成的那個(gè)進(jìn)程信息結(jié)構(gòu)體嗎印蔬?
來自用戶空間的參數(shù)
void __user *ubuf = (void __user *)arg;
首先我們需要知道,這個(gè)arg是從用戶空間傳遞過來的地址脱衙。比如ioctl(fd, BINDER_VERSION, &vers)
傳遞過來了一個(gè)地址侥猬,指向用來儲存Binder版本號的空間。
在Binder驅(qū)動(dòng)層捐韩,需要對這個(gè)地址進(jìn)行轉(zhuǎn)換一下退唠,用__user
給它做上標(biāo)記,表明它指向的是用戶空間的地址荤胁。那么瞧预,這個(gè)arg指向的空間與Binder驅(qū)動(dòng)的內(nèi)存空間的數(shù)據(jù)傳遞就需要通過copy_from_user()
或者copy_to_user()
來進(jìn)行了。
不同的cmd對應(yīng)不同的操作
switch (cmd) {
...
}
switch中定義了幾個(gè)命令仅政,分別對應(yīng)不同的操作垢油,CoorChice不在這全部說了,后面遇到再說圆丹。
我們先看看在open_driver()
中出現(xiàn)的兩個(gè)cmd就行了滩愁。
- BINDER_VERSION
這個(gè)命令用于獲取Binder驅(qū)動(dòng)版本號。
//獲取Binder驅(qū)動(dòng)的版本號
case BINDER_VERSION: {
//表示用戶空間的binder版本信息
struct binder_version __user *ver = ubuf;
...
//把版本號賦值給binder_version的protocol_version成員
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
...
}
break;
}
注意运褪,上面不是直接賦值惊楼,而是使用了put_user()
函數(shù)。因?yàn)檫@個(gè)值是需要寫到用戶空間去的秸讹。
- BINDER_SET_MAX_THREADS
設(shè)置進(jìn)程可用于Binder通訊的最大線程數(shù)量檀咙。
//設(shè)置用戶進(jìn)程最大線程數(shù)
case BINDER_SET_MAX_THREADS:
//使用copy_from_user()函數(shù),將用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間
//這里就是把線程數(shù)拷貝給進(jìn)程結(jié)構(gòu)體的max_threads
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
...
}
break;
注意璃诀,上面使用了copy_from_user()
函數(shù)弧可,把用戶空間的值,寫到了驅(qū)動(dòng)層的進(jìn)程信息體的成員max_threads劣欢。
好了棕诵,上次open_driver()
這個(gè)坑算是補(bǔ)上了。
接下來看看ProcessState::getStrongProxyForHandle()
函數(shù)留下的坑吧凿将。
接著getStrongProxyForHandle()說
注意啦校套,從這里開始是山路十八彎,抓好扶好了澳恋帧笛匙!
先來看一張流程圖侨把。
不夠高清?點(diǎn)這個(gè)鏈接下載吧妹孙!http://ogemdlrap.bkt.clouddn.com/Binder%E8%BF%9B%E9%98%B6%E5%AE%8C%E6%95%B4.png秋柄。So Sweet!
圖中相同顏色的流程線表示同一個(gè)流程蠢正,上面標(biāo)有數(shù)字骇笔,你需要按照數(shù)字順序來看,因?yàn)檫@真的是一個(gè)復(fù)雜無比的流程!
另外,同一種顏色的雙向箭頭線指向的是同一個(gè)變量或者值相同的變量夺脾。同理,相同顏色的帶字空心箭頭指向的也是同一個(gè)變量或者相同的值旭旭。
每個(gè)函數(shù)框上部的框表示在我們這個(gè)流程中谎脯,傳入函數(shù)的參數(shù)葱跋。
溫習(xí)一下getStrongProxyForHandle()中的坑
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
...
//嘗試獲取handle對應(yīng)的handle_entry對象,沒有的話會創(chuàng)建一個(gè)
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
// 上面的判斷確保了同一個(gè)handle不會重復(fù)創(chuàng)建新的BpBinder
if (handle == 0) {
Parcel data;
//在handle對應(yīng)的BpBinder第一次創(chuàng)建時(shí)
//會執(zhí)行一次虛擬的事務(wù)請求源梭,以確保ServiceManager已經(jīng)注冊
status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0);
if (status == DEAD_OBJECT)
//如果ServiceManager沒有注冊娱俺,直接返回
return NULL;
}
//創(chuàng)建一個(gè)BpBinder
//handle為0時(shí)創(chuàng)建的是ServiceManager對應(yīng)的BpBinder
b = new BpBinder(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b; //待會兒返回b
}
...
}
return result;
}
上次CoorChice在getStrongProxyForHandle()函數(shù)中是把下面這段代碼省略了的,為了方便大家關(guān)注流程废麻。
if (handle == 0) {
Parcel data;
//在handle對應(yīng)的BpBinder第一次創(chuàng)建時(shí)
//會執(zhí)行一次虛擬的事務(wù)請求荠卷,以確保ServiceManager已經(jīng)注冊
status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0);
if (status == DEAD_OBJECT)
//如果ServiceManager沒有注冊,直接返回
return NULL;
}
由于我們發(fā)起了獲取ServiceManager的Binder的請求烛愧,所以handle是0的油宜。還記得嗎?應(yīng)用進(jìn)程在首次獲攘恕(或者說創(chuàng)建)ServiceManager的Binder前慎冤,會先和ServiceManager進(jìn)行一次無意義的通訊(可以看到這次通訊的code為PING_TRANSACTION),以確保系統(tǒng)的ServiceManager已經(jīng)注冊沧卢。既然是在這第一次見到Binder通訊蚁堤,那么我們就索性從這開始來探索Binder通訊機(jī)制的核心流程吧。
IPCThreadState的創(chuàng)建
IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0)
這句代碼首先會獲取IPCThreadState單例但狭。這是我在圖中省略了的披诗。
IPCThreadState* IPCThreadState::self()
{
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
//先檢查有沒有,以確保一個(gè)線程只有一個(gè)IPCThreadState
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState; //沒有就new一個(gè)IPCThreadState
}
...
}
很明顯立磁,這段代碼確保了進(jìn)程中每一線程都只會有一個(gè)對應(yīng)IPCThreadState呈队。
接下來看看IPCThreadState的構(gòu)造函數(shù)。
IPCThreadState::IPCThreadState()
//保存所在進(jìn)程
: mProcess(ProcessState::self()),
mMyThreadId(androidGetTid()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
//用于接收Binder驅(qū)動(dòng)的數(shù)據(jù)唱歧,設(shè)置其大小為256
mIn.setDataCapacity(256);
//用于向Binder驅(qū)動(dòng)發(fā)送數(shù)據(jù)宪摧,同樣設(shè)置其大小為256
mOut.setDataCapacity(256);
}
CoorChice注釋的地方比較重要哦,想要看懂后面的流程,上面3個(gè)注釋的記住哦绍刮!
好了温圆,我們的IPCThreadState算是創(chuàng)建出來了。事實(shí)上IPCThreadState主要就是封裝了和Binder通訊的邏輯孩革,當(dāng)我們需要進(jìn)行通訊時(shí)岁歉,就需要通過它來完成。
下面就來看看通訊是怎么開始的膝蜈。
第一步 IPCThreadState::transact()發(fā)起通訊
你可以先在圖中找到對應(yīng)的流程線锅移。transact()完整代碼的話你可以看圖中的,或者在/frameworks/native/libs/binder/IPCThreadState.cpp
看源碼饱搏。由于流程復(fù)雜非剃,CoorChice就以小片段來說明。
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
flags |= TF_ACCEPT_FDS; //添加TF_ACCEPT_FDS
...
if (err == NO_ERROR) {
...
//將需要發(fā)送的數(shù)據(jù)寫入mOut中
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
...
}
首先推沸,在傳入的flags參數(shù)中添加一個(gè)TF_ACCEPT_FDS
標(biāo)志备绽,表示返回?cái)?shù)據(jù)中可以包含文件描述符。以下是幾個(gè)標(biāo)志位的意義:
enum transaction_flags {
TF_ONE_WAY = 0x01, /*異步的單向調(diào)用鬓催,沒有返回值*/
TF_ROOT_OBJECT = 0x04, /*里面的數(shù)據(jù)是一個(gè)組件的根對象*/
TF_STATUS_CODE = 0x08, /*數(shù)據(jù)包含的是一個(gè)32bit的狀態(tài)碼*/
TF_ACCEPT_FDS = 0x10, /*允許返回對象中肺素,包含文件描述符*/
}
接著,會調(diào)用writeTransactionData()函數(shù)宇驾,把需要發(fā)送的數(shù)據(jù)準(zhǔn)備好倍靡。注意這里的命令是BC_TRANSACTION
哦。如果你隨時(shí)對照著圖查看參數(shù)的話课舍,這個(gè)流程將會變的容易理解一些塌西。
第二步 writeTransactionData()準(zhǔn)備發(fā)送數(shù)據(jù)
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
//儲存通訊事務(wù)數(shù)據(jù)的結(jié)構(gòu)
binder_transaction_data tr;
tr.target.ptr = 0; //binder_node的地址
tr.target.handle = handle; //用于查找目標(biāo)進(jìn)程Binder的handle,對應(yīng)binder_ref
tr.code = code; //表示事務(wù)類型
tr.flags = binderFlags;
tr.cookie= 0;
...
//Parcel mOut筝尾,與之相反的有Parcel mIn
//寫入本次通訊的cmd指令
mOut.writeInt32(cmd);
//把本次通訊事務(wù)數(shù)據(jù)寫入mOut中
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
如你所見捡需,這個(gè)函數(shù)主要?jiǎng)?chuàng)建了一個(gè)用于儲存通訊事務(wù)數(shù)據(jù)的binder_transaction_data
結(jié)構(gòu)t,并把需要發(fā)送的事務(wù)數(shù)據(jù)放到其中忿等,然后再把這個(gè)tr寫入IPCThreadState的mOut中栖忠。這樣一來,后面就可以從mOut中取出這個(gè)通訊事務(wù)數(shù)據(jù)結(jié)構(gòu)了贸街。它非常重要庵寞,你一定要記住它是什么?以及從那來的薛匪?
此外捐川,還需要把本次通訊的命令也寫入mOut中,這樣后面才能獲取到發(fā)送方的命令逸尖,然后執(zhí)行相應(yīng)的操作古沥。
第三步 waitForResponse()等待響應(yīng)
第二步完成后瘸右,我們再次回到IPCThreadState::transact()函數(shù)中。
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
...
flags |= TF_ACCEPT_FDS; //添加TF_ACCEPT_FDS
...
//等待響應(yīng)
if ((flags & TF_ONE_WAY) == 0) { //檢查本次通訊是否有TF_ONE_WAY標(biāo)志岩齿,即沒有響應(yīng)
//reply是否為空
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
...
}
...
return err;
}
一般通訊都需要響應(yīng)太颤,所以我們就只看有響應(yīng)的情況了,即flags中不包含TF_ONE_WAY
標(biāo)記盹沈。調(diào)用waitForResponse()函數(shù)時(shí)龄章,如果沒有reply,會創(chuàng)建一個(gè)fakeReplay乞封。我們回顧一下:
transact(0, IBinder::PING_TRANSACTION, data, NULL, 0)
看做裙,我們上面?zhèn)魅氲膔eplay是一個(gè)NULL,所以這里是會創(chuàng)建一個(gè)fakeReplay的肃晚。
緊接著锚贱,我們就進(jìn)入到IPCThreadState::waitForResponse()
中了」卮可以看下圖中的流程線哦拧廊,對應(yīng)紅色編號3的線。
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
...
while (1) {
//真正和Binder驅(qū)動(dòng)交互的是talkWithDriver()函數(shù)
if ((err=talkWithDriver()) < NO_ERROR) break;
...
}
...
}
這個(gè)方法中悍缠,一開始就有些隱蔽的調(diào)用了一個(gè)十分重要的方法IPCThreadState::talkWithDriver()
卦绣,從名字也能看出來耐量,真正和Binder驅(qū)動(dòng)talk的邏輯是在這個(gè)函數(shù)中的飞蚓。這個(gè)地方給差評!
順著代碼廊蜒,我們進(jìn)入talkWithDriver()看看用戶空間是如何和Binder驅(qū)動(dòng)talk的趴拧。
第四步 talkWithDriver()和Binder talk!
注意山叮,需要說明一下著榴,IPCThreadState::talkWithDriver()
這個(gè)函數(shù)的參數(shù)默認(rèn)為true!一定要記住屁倔,不然后面就看不懂了脑又!
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
...
//讀寫結(jié)構(gòu)體,它是用戶空間和內(nèi)核空間的信使
binder_write_read bwr;
...
//配置發(fā)送信息
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
...
//獲取接收信息
if(doReceive && needRead){
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
...
//設(shè)置消耗為0
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
...
//通過ioctl操作與內(nèi)核進(jìn)行讀寫
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
...
} while (err == -EINTR);
...
}
這個(gè)函數(shù)中,有一個(gè)重要結(jié)構(gòu)被定義锐借,就是binder_write_read
问麸。它能夠儲存一些必要的發(fā)送和接收的通訊信息,它就像用戶空間和內(nèi)核空間之間的一個(gè)信使一樣钞翔,在兩端傳遞信息严卖。
在這個(gè)函數(shù)中,首先會把用戶空間要傳遞/讀取信息放到bwr中布轿,然后通過一句關(guān)鍵的代碼ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
與Binder驅(qū)動(dòng)talk哮笆。ioctl()函數(shù)CoorChice已經(jīng)在上一篇中說了来颤,它最終會調(diào)用到Binder內(nèi)核的binder_ioctl()
函數(shù),至于為什么稠肘?你可以再看看上一篇文章回顧下福铅。
注意這里我們給ioctl()函數(shù)傳遞的參數(shù)。
- 第一個(gè)參數(shù)项阴,是從本進(jìn)程中取出上面篇中打開并保存Binder設(shè)備文件描述符本讥,通過它可以獲取到之前生成的file文件結(jié)構(gòu),然后傳給
binder_ioctl()
函數(shù)鲁冯。沒印象的同學(xué)先看看上篇回顧下這里拷沸。 - 第二個(gè)參數(shù),是命令薯演,它決定了待會到內(nèi)核空間中要執(zhí)行那段邏輯撞芍。
- 第三個(gè)參數(shù),我們把剛剛定義的信使bwr的內(nèi)存地址傳到內(nèi)核空間去跨扮。
這些參數(shù)是理解后面步驟的關(guān)鍵序无,不要忘了哦!現(xiàn)在衡创,進(jìn)入到老盆友binder_ioctl()
函數(shù)中帝嗡,看看收到用戶空間的消息后,它干了什么璃氢?
第五步 在binder_ioctl()中處理消息
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
// 從file結(jié)構(gòu)體中取出進(jìn)程信息
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
//表明arg是一個(gè)用戶空間地址
//__user標(biāo)記該指針為用戶空間指針哟玷,在當(dāng)前空間內(nèi)無意義
void __user *ubuf = (void __user *)arg;
...
//鎖定同步
binder_lock(__func__);
//取出線程信息
thread = binder_get_thread(proc);
...
switch (cmd) {
//讀寫數(shù)據(jù)
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
...
}
...
}
...
//解鎖
binder_unlock(__func__);
...
}
這個(gè)函數(shù)看過《從getSystemService()開始,開擼Binder通訊機(jī)制:http://www.reibang.com/p/1050ce12bc1e》的同學(xué)應(yīng)該不會陌生一也。首先會根據(jù)文件描述符獲得的file結(jié)構(gòu)巢寡,獲取到調(diào)用ioctl()函數(shù)的進(jìn)程的進(jìn)程信息,從而再獲得進(jìn)程的線程椰苟。然后將arg參數(shù)地址轉(zhuǎn)換成有用戶空間標(biāo)記的指針抑月。接著,在switch中根據(jù)cmd參數(shù)判斷需要執(zhí)行什么操作舆蝴。這些步驟和上篇文章中是一樣的谦絮。不同的是,我們這次的cmd命令是BINDER_WRITE_READ
洁仗,表示要進(jìn)行讀寫操作层皱。可以看到京痢,接下來的讀寫邏輯是在binder_ioctl_write_read()
函數(shù)中的奶甘。
嗯,接下來祭椰,我們即將進(jìn)入第6步臭家,看看Binder驅(qū)動(dòng)中的這段讀寫通訊邏輯是怎樣的疲陕?
第六步 binder_ioctl_write_read() talking
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
//獲取發(fā)送進(jìn)程信息
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
//來自用戶空間的參數(shù)地址
void __user *ubuf = (void __user *)arg;
//讀寫信息結(jié)構(gòu)體
struct binder_write_read bwr;
...
//拷貝用戶空間的通訊信息bwr到內(nèi)核的bwr
if (copy_from_user(&bwr, ubuf, sizeof(bwr)))
...
if (bwr.write_size > 0) {
//寫數(shù)據(jù)
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
...
}
咱們先看上面這個(gè)片段。
首先自然是取出用戶空間的進(jìn)程信息钉赁,然后轉(zhuǎn)換獲得用戶空間的參數(shù)地址(對應(yīng)本次通訊中為bwr的地址)蹄殃,這些都跟在binder_ioctl()
中做的差不多。
接下來你踩,你可以看到一個(gè)binder_write_read
結(jié)構(gòu)的申明struct binder_write_read bwr
诅岩,緊跟著通過copy_from_user(&bwr, ubuf, sizeof(bwr))
把用戶空間的bwr拷貝到了當(dāng)前內(nèi)核空間的bwr。現(xiàn)在带膜,Binder內(nèi)核空間的bwr就獲取到了來自用戶空間的通訊信息了吩谦。
獲取到來自用戶空間的信息后,先調(diào)用binder_thread_write()
函數(shù)來處理膝藕,我看看是如何進(jìn)行處理的式廷。
第七步binder_thread_write()處理寫入
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
uint32_t cmd;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed; //起始地址
void __user *end = buffer + size; //結(jié)束地址
while (ptr < end && thread->return_error == BR_OK) {
//從用戶空間獲取cmd命令
if (get_user(cmd, (uint32_t __user *)ptr)) -EFAULT;
ptr += sizeof(uint32_t);
switch (cmd) {
case BC_TRANSACTION:
case BC_REPLY: {
//用來儲存通訊信息的結(jié)構(gòu)體
struct binder_transaction_data tr;
//拷貝用戶空間的binder_transaction_data
if (copy_from_user(&tr, ptr, sizeof(tr))) return -EFAULT;
ptr += sizeof(tr);
//處理通訊
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
break;
}
...
}
*consumed = ptr - buffer;
}
return 0;
}
一開始就是對一些變量進(jìn)行賦值。
首先芭挽,binder_buffer
是啥滑废?哪來的?快到到傳參的地方方看看bwr.write_buffer
袜爪,它是寫的buffer蠕趁。那么它里面裝了啥?這就得回到第4步中找了辛馆,因?yàn)閎wr是在那個(gè)地方定義和初始化的俺陋。bwr.write_buffer = (uintptr_t)mOut.data()
,嗯怀各,它指向了mOut中的數(shù)據(jù)倔韭。那么問題又來了?mOut中的數(shù)據(jù)是啥瓢对?...
看,這就是為什么CoorChice一直在強(qiáng)調(diào)胰苏,前面的一些參數(shù)和變量一定要記姿队肌!不然到后面就會云里霧里的硕并!不過還好法焰,有了CoorChcie上面那張圖,你隨時(shí)可以快速的找到答案倔毙。我們回到第二步writeTransactionData()埃仪,就是通訊事務(wù)結(jié)構(gòu)定義的那個(gè)地方∩略撸看到?jīng)]卵蛉,mOut中儲存的就是一個(gè)通訊事務(wù)結(jié)構(gòu)颁股。
現(xiàn)在答案就明了了,buffer指向了用戶空間的通訊事務(wù)數(shù)據(jù)傻丝。
另外兩個(gè)參數(shù)甘有,ptr此刻和buffer的值是一樣的,因?yàn)閏onsumed為0葡缰,所以它現(xiàn)在也相當(dāng)于是用戶空間的通訊事務(wù)數(shù)據(jù)tr的指針亏掀;而end可以明顯的看出,它指向了tr的末尾泛释。
通過get_user(cmd, (uint32_t __user *)ptr)
函數(shù)滤愕,我們可以將用戶空間的tr的cmd拷貝到內(nèi)核空間。get_user()
和put_user()
這對函數(shù)就是干這個(gè)的怜校,拷貝一些簡單的變量该互。回到第2步writeTransactionData()
中韭畸,看看參數(shù)宇智。沒錯(cuò),cmd為BC_TRANSACTION
胰丁。所以随橘,進(jìn)到switch中,對應(yīng)執(zhí)行的就是case BC_TRANSACTION
锦庸。
可以看到BC_TRANSACTION
事務(wù)命令和BC_REPLAY
響應(yīng)命令机蔗,執(zhí)行的是相同的邏輯。
//用來儲存通訊信息的結(jié)構(gòu)體
struct binder_transaction_data tr;
//拷貝用戶空間的binder_transaction_data
if (copy_from_user(&tr, ptr, sizeof(tr))) return -EFAU
ptr += sizeof(tr);
//處理通訊
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
先定義了一個(gè)內(nèi)核空間的通訊事務(wù)數(shù)據(jù)tr甘萧,然后把用戶空間的通訊事務(wù)數(shù)據(jù)拷貝到內(nèi)核中tr萝嘁。此時(shí),ptr指針移動(dòng)sizeof(tr)
個(gè)單位扬卷,現(xiàn)在ptr應(yīng)該和end的值是一樣的了牙言。然后,調(diào)用binder_transaction()
來處理事務(wù)怪得。
第八步 binder_transaction()來處理事務(wù)
順著流程線8看過去咱枉,WTF!這是一個(gè)復(fù)雜無比的函數(shù)徒恋!很多蚕断!很長!
函數(shù)一開始定義了一堆變量入挣,我們先不管亿乳,用到時(shí)再說。先看第一個(gè)使用到的參數(shù)replay
径筏。由于上一步中的cmd為BC_TRANSACTION
葛假,所以很明顯障陶,走的是false,所以我們直接看false里的邏輯桐款。
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply){
...
if (reply) {
...
} else {
if (tr->target.handle) {
//如果參數(shù)事物信息中的進(jìn)程的句柄不為0咸这,即不是系統(tǒng)ServiceManager進(jìn)程
//定義binder引用
struct binder_ref *ref;
//根據(jù)參數(shù)binder進(jìn)程和句柄handle來查找對應(yīng)的binder
ref = binder_get_ref(proc, tr->target.handle);
...
//設(shè)置目標(biāo)binder實(shí)體為上面找到的參數(shù)進(jìn)程的binder引用的binder實(shí)體
target_node = ref->node;
} else {
//如果參數(shù)事物信息中的進(jìn)程的句柄為0,即是系統(tǒng)ServiceManager進(jìn)程
//設(shè)置通訊目標(biāo)進(jìn)程的Binder實(shí)體為ServiceManager對應(yīng)的Binder
target_node = binder_context_mgr_node;
}
//設(shè)置通訊目標(biāo)進(jìn)程為target_node對應(yīng)的進(jìn)程
target_proc = target_node->proc;
...
}
一開始先判斷tr->target.handle
為不為0魔眨。還記得上一篇說的嗎媳维?handle為0表示的是ServiceManager,如果不為0表示的其它Service遏暴。那么這里為不為0呢侄刽?看圖!
我們順著指向tr的綠線一直找朋凉,可以看到州丹。在用戶空間通訊事務(wù)數(shù)據(jù)被定義的地方,也就是第2步IPCThreadState::writeTransactionData()
中杂彭,給tr->target_handle
賦值了墓毒,往上看發(fā)現(xiàn),這個(gè)值來自IPCThreadState::transact()
函數(shù)的參數(shù)handle亲怠。那么回到我們一開始調(diào)用這個(gè)函數(shù)的地方IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0)
所计。哦,handle為0团秽。所以這里的target就是ServiceManager主胧。那么直接把binder_context_mgr_node
(它表示ServiceManager的Binder,在ServiceManager注冊的時(shí)候被緩存到了Binder內(nèi)核中)賦值給target_node
习勤,記住了哦踪栋!后面這些都會用到⊥急希總之夷都,我們就是需要先獲取到一個(gè)目標(biāo)進(jìn)程。
接下來吴旋,通過target_node
损肛,也就是ServiceManager進(jìn)程的Binder(其它情況就是對應(yīng)進(jìn)程的Binder),我們可以獲取到目標(biāo)進(jìn)程信息荣瑟,然后賦值給target_proc
。記住了哦摩泪!
繼續(xù)下一段代碼笆焰。
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply){
...
//判斷目標(biāo)線程是否為空
if (target_thread) {
...
//目標(biāo)線程的todo隊(duì)列
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
...
} else {
//獲得通訊目標(biāo)進(jìn)程的任務(wù)隊(duì)列
target_list = &target_proc->todo;
//獲取通訊目標(biāo)進(jìn)程的等待對象
target_wait = &target_proc->wait;
}
...
}
首先看target_thread
是否為空,由于我們沒有走if(replay)的TRUE邏輯见坑,所以這里target_thread
是為空的嚷掠。那么捏检,從目標(biāo)進(jìn)程信息target_proc
分別去除todo任務(wù)隊(duì)列和wait對象,賦值給target_list
和target_wait
不皆。同樣需要記坠岢恰!
繼續(xù)下一段代碼霹娄。
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply){
struct binder_transaction *t; //表示一個(gè)binder通訊事務(wù)
struct binder_work *tcomplete; //表示一項(xiàng)work
...
struct list_head *target_list; //通訊目標(biāo)進(jìn)程的事務(wù)隊(duì)列
wait_queue_head_t *target_wait; //通訊目標(biāo)進(jìn)程的等待對象
...
//為本次通訊事務(wù)t申請空間
t = kzalloc(sizeof(*t), GFP_KERNEL);
...
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
...
if (!reply && !(tr->flags & TF_ONE_WAY))
//采用非one way通訊方式能犯,即需要等待服務(wù)端返回結(jié)果的通訊方式
//設(shè)置本次通訊事務(wù)t的發(fā)送線程為用戶空間的線程
t->from = thread;
else
t->from = NULL;
...
//設(shè)置本次通訊事務(wù)的接收進(jìn)程為目標(biāo)進(jìn)程
t->to_proc = target_proc;
//設(shè)置本次通訊事務(wù)的接收線程為目標(biāo)線程
t->to_thread = target_thread;
//設(shè)置本次通訊事務(wù)的命令為用戶空間傳來的命令
t->code = tr->code;
//設(shè)置本次通訊事務(wù)的命令為用戶空間傳來的flags
t->flags = tr->flags;
...
//開始配置本次通訊的buffer
//在目標(biāo)進(jìn)程中分配進(jìn)行本次通訊的buffer的空間
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
t->buffer->allow_user_free = 0; //通訊buffer允許釋放
t->buffer->transaction = t; //把本次通訊存入buffer中
//設(shè)置本次通訊的buffer的目標(biāo)Binder實(shí)體為target_node
//如前面一樣,通過這個(gè)buffer可以找到對應(yīng)的進(jìn)程
t->buffer->target_node = target_node;
...
offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
//將用戶空間發(fā)送來的數(shù)據(jù)拷貝到本次通訊的buffer的data中
copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size);
...
//將用戶空間發(fā)送來的偏移量offsets拷貝給起始o(jì)ffp
copy_from_user(offp, (const void __user *)(uintptr_t)tr->data.ptr.offsets, tr->offsets_size);
...
//計(jì)算結(jié)尾off_end
off_end = (void *)offp + tr->offsets_size;
...
//判斷是否是BC_REPLY
if (reply) {
...
binder_pop_transaction(target_thread, in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
//如果沒有ONE_WAY標(biāo)記犬耻,即需要等待響應(yīng)
t->need_reply = 1; //1標(biāo)示這是一個(gè)同步事務(wù)踩晶,需要等待對方回復(fù)。0表示這是一個(gè)異步事務(wù)枕磁,不用等對方回復(fù)
//設(shè)置本次通訊事務(wù)的from_parent為發(fā)送方進(jìn)程的事務(wù)
t->from_parent = thread->transaction_stack;
//設(shè)置發(fā)送方進(jìn)程的事務(wù)棧為本次通訊事務(wù)
thread->transaction_stack = t;
}
...
//將本次通訊事務(wù)的work類型設(shè)置為BINDER_WORK_TRANSACTION
t->work.type = BINDER_WORK_TRANSACTION;
//將本次通訊事務(wù)的work添加到目標(biāo)進(jìn)程的事務(wù)列表中
list_add_tail(&t->work.entry, target_list);
//設(shè)置work類型為BINDER_WORK_TRANSACTION_COMPLETE
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
//將BINDER_WORK_TRANSACTION_COMPLETE類型的work添加到發(fā)送方的事務(wù)列表中
list_add_tail(&tcomplete->entry, &thread->todo);
if (target_wait)
//喚醒目標(biāo)進(jìn)程渡蜻,開始執(zhí)行目標(biāo)進(jìn)程的事務(wù)棧
wake_up_interruptible(target_wait);
return;
}
在開始分析之前,大家先吧這段代碼開始的幾個(gè)變量定義記住计济,不然后面會很迷茫的茸苇!
這段代碼很多!很長沦寂!CoorChice已經(jīng)盡量的刪去一些沒那么重要的和我不知道是干啥的了Q堋!
其實(shí)這么多代碼凑队,主要使用在給binder事務(wù)t的成員賦值了则果。我們簡單看幾個(gè)我認(rèn)為重要的賦值。
首先為事務(wù)t和work tcomplete申請了內(nèi)存漩氨。然后設(shè)置事務(wù)t的from線程(也就是發(fā)送方線程)的值西壮,如果不是BC_REPLAY
事務(wù),并且通訊標(biāo)記沒有TF_ONE_WAY
(即本次通訊需要有響應(yīng))叫惊,那么把參數(shù)thread賦值給t->from
款青。前面說過,我們本次通訊是BC_TRANSACTION
事務(wù)霍狰,所以事務(wù)t就需要儲存發(fā)送方的線程信息抡草,以便后面給發(fā)送方響應(yīng)使用。
參數(shù)thread是那來的呢蔗坯?順著往回找康震,在第5步binder_ioctl()
中,我們從用戶空間調(diào)用ioctl()函數(shù)的進(jìn)程(即發(fā)送方進(jìn)程)的進(jìn)程信息中獲取到了thread宾濒。
接著設(shè)置事務(wù)t的目標(biāo)進(jìn)程t->to_proc
和目標(biāo)進(jìn)程的線程t->to_thread
為前面處理好的target_proc
和target_thread
(本次通訊腿短,target_thread
為空哦)。
然后把通訊事務(wù)數(shù)據(jù)tr中的code和flags賦值給事務(wù)t的code和flags。code和flags是什么呢橘忱?我們回到用戶空間赴魁,定義通訊事務(wù)數(shù)據(jù),即第2步IPCThreadState::writeTransaction()中可以看到钝诚,code和flags均是傳進(jìn)來的參數(shù)颖御。而的發(fā)源地是通訊的起始點(diǎn)IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0)
,即code = IBinder::PING_TRANSACTION
凝颇, flags = 0潘拱。記住了哦!后面還會用祈噪。
然后開始設(shè)置事務(wù)t的buffer信息泽铛。首先通過binder_alloc_buf()
函數(shù),在目標(biāo)進(jìn)程target_proc
中為t->buffer
申請了內(nèi)存辑鲤,即t->buffer
指向了目標(biāo)進(jìn)程空間中的一段內(nèi)存盔腔。然后配置一下t->buffer
的信息,這些信息后面也會用到月褥。記住了哦弛随!
接著通過copy_from_user()
函數(shù),把用戶空間的需要發(fā)送的數(shù)據(jù)拷貝到t->buffer
的data中宁赤。
再往下到了if(replay)舀透,本次通訊會走false邏輯。于是决左,事務(wù)t會把發(fā)送方的事務(wù)棧transaction_stack
儲存在from_parent
中愕够,而發(fā)送方把自己的事務(wù)棧設(shè)置以成t開始。這些都需要記住佛猛,不然再往后你就會越來越迷糊惑芭!
最重要的部分來了!
//將本次通訊事務(wù)的work類型設(shè)置為BINDER_WORK_TRANSACTION
t->work.type = BINDER_WORK_TRANSACTION;
//將本次通訊事務(wù)的work添加到目標(biāo)進(jìn)程的事務(wù)列表中
list_add_tail(&t->work.entry, target_list);
//設(shè)置work類型為BINDER_WORK_TRANSACTION_COMPLETE
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
將BINDER_WORK_TRANSACTION_COMPLETE類型的work添加到發(fā)送方的事務(wù)列表中
list_add_tail(&tcomplete->entry, &thread->todo);
if (target_wait)
//喚醒目標(biāo)進(jìn)程继找,開始執(zhí)行目標(biāo)進(jìn)程的事務(wù)棧
wake_up_interruptible(target_wait);
return;
先把事務(wù)t的work.type類型設(shè)置為BINDER_WORK_TRANSACTION
類型遂跟,這決定了該事務(wù)后面走的流程,然后把事務(wù)t的任務(wù)添加到目標(biāo)進(jìn)程的任務(wù)棧target_list
中婴渡。接著把work tcomplete的類型設(shè)置為BINDER_WORK_TRANSACTION_COMPLETE
幻锁,用于告訴發(fā)送方,和Binder驅(qū)動(dòng)的一次talk完成了边臼,同樣哄尔,需要把這個(gè)項(xiàng)任務(wù)添加到發(fā)送方的任務(wù)列表里。
最后柠并,通過wake_up_interruptible(target_wait)
函數(shù)喚醒休眠中的目標(biāo)進(jìn)程究飞,讓它開始處理任務(wù)棧中的任務(wù)置谦,也就是剛剛我們添加到target_list
中的任務(wù)堂鲤。接著return結(jié)束該函數(shù)亿傅。
結(jié)束這個(gè)函數(shù)你以為就忘啦?Native瘟栖!接著往下看葵擎。
第9步 binder_thread_read()讀取數(shù)據(jù)
上一個(gè)函數(shù)結(jié)束后回到第7步binder_thread_write()
函數(shù)中,retrun 0;
半哟,binder_thread_write()
函數(shù)結(jié)束酬滤。然后回到第6步binder_ioctl_write_read()
函數(shù)中繼續(xù)執(zhí)行。
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
...
if (bwr.read_size > 0) {
//讀數(shù)據(jù)
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
...
}
Binder驅(qū)動(dòng)會調(diào)用binder_thread_read()
函數(shù)寓涨,為發(fā)送進(jìn)程讀取數(shù)據(jù)盯串。我們看看是怎么讀取的。
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer,
size_t size,
binder_size_t *consumed,
int non_block)
{
...
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
if (!list_empty(&thread->todo)) {
//獲取線程的work隊(duì)列
w = list_first_entry(&thread->todo, struct binder_work, entry);
} else if (!list_empty(&proc->todo) && wait_for_proc_work) {
//獲取從進(jìn)程獲取work隊(duì)列
w = list_first_entry(&proc->todo, struct binder_work, entry);
} else {
//沒有數(shù)據(jù),則返回retry
if (ptr - buffer == 4 &&
!(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
goto retry;
break;
}
...
}
我們先看這個(gè)片段戒良,前面一堆代碼掠過了体捏。首先,需要看看能不能從發(fā)送進(jìn)程的線程thread的任務(wù)棧中取出任務(wù)來糯崎,回顧第8步binder_transaction()
中几缭,我們在最后往發(fā)送進(jìn)程的線程thread的任務(wù)棧中添加了一個(gè)BINDER_WORK_TRANSACTION_COMPLETE
類型的work。所以這里是能取到任務(wù)的沃呢,就直接執(zhí)行下一步了年栓。
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer,
size_t size,
binder_size_t *consumed,
int non_block)
{
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed; //
void __user *end = buffer + size; //用戶空間結(jié)束
...
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
...
switch (w->type) {
...
case BINDER_WORK_TRANSACTION_COMPLETE:
//設(shè)置cmd為BR_TRANSACTION_COMPLETE
cmd = BR_TRANSACTION_COMPLETE;
//將BR_TRANSACTION_COMPLETE寫入用戶進(jìn)程空間的mIn中
put_user(cmd, (uint32_t __user *)ptr);
//從事務(wù)隊(duì)列中刪除本次work
list_del(&w->entry);
//釋放
kfree(w);
break;
...
}
}
...
}
由于這個(gè)流程中薄霜,我們知道work的type為BINDER_WORK_TRANSACTION_COMPLETE
類型某抓,所以就先只看這種情況了。在這段代碼中惰瓜,cmd = BR_TRANSACTION_COMPLETE
很重要否副,要記住鸵熟!接著把cmd拷貝到用戶空間的發(fā)送進(jìn)程副编,然后刪除任務(wù),釋放內(nèi)存流强。
一次和Binder驅(qū)動(dòng)的通訊完成痹届!
上面代碼執(zhí)行完后,binder_thread_read()
函數(shù)差不多就結(jié)束了打月,接著又會回到binder_ioctl_write_read()
函數(shù)队腐。
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
...
//將內(nèi)核的信使bwr拷貝到用戶空間
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
...
}
上面函數(shù)最后會把內(nèi)核中的信使拷貝到用戶空間。
然后奏篙,我們直接的再次的回到第3步的函數(shù)IPCThreadState::waitForResponse()
中柴淘。
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
...
while (1) {
//真正和Binder驅(qū)動(dòng)交互的是talkWithDriver()函數(shù)
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
...
if (mIn.dataAvail() == 0) continue;
//取出在內(nèi)核中寫進(jìn)去的cmd命令
cmd = mIn.readInt32();
...
switch (cmd) {
//表示和內(nèi)核的一次通訊完成
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
...
}
}
...
}
經(jīng)過剛剛的讀取迫淹,這次mIn中可是有數(shù)據(jù)了哦!我們從mIn中取出cmd命令为严。這是什么命令呢敛熬?就是剛剛寫到用戶空間的BR_TRANSACTION_COMPLETE
。在這段邏輯中第股,由于之前我們傳入了一個(gè)fakeReplay進(jìn)來应民,所以程序走bredk,然后繼續(xù)循環(huán)夕吻,執(zhí)行下一次talkWithDriver()
函數(shù)诲锹。到此,我們和Binder內(nèi)核的一次通訊算是完成了涉馅。
但是我們發(fā)起的這次通訊還沒有得到回應(yīng)哦归园!猜猜看回應(yīng)的流程是怎樣的呀?
文章太長了稚矿,回應(yīng)流程放到下一篇了庸诱。
總結(jié)
- 抽出空余時(shí)間寫文章分享需要?jiǎng)恿Γ€請各位看官動(dòng)動(dòng)小手點(diǎn)個(gè)贊盐捷,給我點(diǎn)鼓勵(lì)??
- 我一直在不定期的創(chuàng)作新的干貨偶翅,想要上車只需進(jìn)到我的【個(gè)人主頁】點(diǎn)個(gè)關(guān)注就好了哦。發(fā)車嘍~
本篇CoorChice填了上篇文章中的一些坑碉渡,并借此跑通了一遍客戶端和Binder驅(qū)動(dòng)通訊的流程聚谁。這是個(gè)很復(fù)雜的過程,大家看著圖走一遍滞诺,再思考思考形导。回過頭來一想习霹,其實(shí)也沒那么難了朵耕。
俗話說會者不難, 難者不會
,大概就是這樣吧淋叶。
功力有限阎曹,有錯(cuò)還請指出一起交流交流。
看到這里的童鞋快獎(jiǎng)勵(lì)自己一口辣條吧煞檩!
想要看CoorChice的更多文章处嫌,請點(diǎn)個(gè)關(guān)注哦!