爲何在Android中使用binder通訊機制?
衆所周知linux中的進程通訊有不少種方式,好比說管道、消息隊列、socket機制等。socket咱們再熟悉不過了,然而其做爲一款通用的接口,通訊開銷大,數據傳輸效率低,主要用在跨網絡間的進程間通訊以及在本地的低速通訊。消息隊列和管道都是採用存儲-轉發模式,效率上面也有點低,由於這種模式的數據傳輸要通過兩次的內存拷貝,先從發送方的緩存區拷貝到內核開闢的緩存區中,而後再從內核拷貝到接受方的緩存區。傳統的ipc沒有任何的安全措施,兩個進程之間沒有辦法鑑別對方的身份,而在Android中,每一個應用在安裝後都會被分配一個uid,因此這個身份也有了保障,也更安全。爲了保障安全和高效率,Android提供了一套全新的ipc通訊機制也就是binder。linux
binder通訊模型
一個進程間通訊能夠簡單理解成爲Client-server模式,binder機制在Android系統中的一個模型以下:緩存
Client得到到server端的proxy對象。
Client經過調用proxy對象的方法向server發送請求。
proxy對象經過binder設備節點,把Client請求信息發送到linux內核空間,由binder驅動獲取併發送到服務進程。
服務進程處理Client請求,經過linux內核的binder驅動把結果返回給proxy對象。
客戶端收到proxy的返回結果。
當client的線程進入到binder驅動後,內核會把client的線程掛起,而後進入服務進程,一直到結果返回,Client線程纔會被喚醒,因此這個過程又相似於「線程遷移」,就是一個線程進入到另一個進程中帶着結果返回。安全
binder組成結構
Binder驅動服務器
binder是內核中的一個字符驅動設備位於/dev/binder。這個設備是Android系統IPC的核心部分,客戶端的服務代理用來經過它向server發送請求,服務器也是經過它把處理結果返回給客戶端的服務代理對象。這部份內容,在Android中經過一個IPCThreadState對象封裝了對Binder驅動的操做。
Service Manager網絡
這個東西主要用來負責管理服務。Android中提供的系統服務都要經過Service Manager註冊本身,將本身添加進服務管理鏈表中,爲客戶端提供服務。而客戶端若是要和特定的系統服務端通信,就須要向Service Manager來查詢和得到所須要服務。能夠看出Service Manager是系統服務對象的管理中心。
服務(Server)併發
須要強調的是這裏服務是指的是System Server,而不是SDK server,向客戶端提供服務。
客戶端socket
通常是指Android系統上面的應用程序。它能夠請求Server中的服務。
代理對象
是指在客戶端應用程序中獲取生成的Server代理(proxy)類對象。從應用程序角度看代理對象和本地對象沒有差異,均可以調用其方法,方法都是同步的,而且返回相應的結果。
從源碼分析MediaPlayerService
和其餘介紹binder機制的博客文章同樣,接下來我也會從MediaPlayerService的角度去介紹bidner(建議你們看下深刻理解Android這本書),源碼在framework\base\Media\MediaServer\Main_mediaserver.cpp中。之因此選擇MPS,是由於這個service牽涉了許多重要的服務,好比說音頻、攝像等。函數
int main(int argc, char** argv)
{
// 打開驅動設備,將設備文件描述符映射到內存,這快內存做爲數據交互的地方
sp<ProcessState> proc(ProcessState::self());源碼分析
sp<IServiceManager> sm = defaultServiceManager();ui
LOGI("ServiceManager: %p", sm.get());
waitBeforeAdding( String16("media.audio_flinger") );
AudioFlinger::instantiate();
waitBeforeAdding( String16("media.player") );
// 註冊MediaPlayerService,
MediaPlayerService::instantiate();
waitBeforeAdding( String16("media.camera") );
CameraService::instantiate();
waitBeforeAdding( String16("media.audio_policy") );
AudioPolicyService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
首先看下ProcessState的構造函數
ProcessState::ProcessState()
: mDriverFD(open_driver())// 開啓binder
, mVMStart(MAP_FAILED) // 內存映射的起始地址
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
......
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
.....
}
上述ProcessState構造函數代碼首先調用open_driver()方法,進入看看先:
static int open_driver()
{
if (gSingleProcess) {
return -1;
}
// 打開binder設備,設備成功打開返回一個設備的文件描述符
int fd = open("/dev/binder", O_RDWR);
if (fd >= 0) {
// 改變已經打開的文件的性質。若是FD_CLOEXEC位是0,執行execve的過程當中,文件保持打開。反之則關閉。
fcntl(fd, F_SETFD, FD_CLOEXEC);
int vers;
#if defined(HAVE_ANDROID_OS)
status_t result = ioctl(fd, BINDER_VERSION, &vers);
#else
status_t result = -1;
errno = EPERM;
#endif
if (result == -1) {
LOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
close(fd);
fd = -1;
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
LOGE("Binder driver protocol does not match user space protocol!");
close(fd);
fd = -1;
}
#if defined(HAVE_ANDROID_OS)
size_t maxThreads = 15;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
if (result == -1) {
LOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
#endif
} else {
LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
}
return fd;
}
open_driver是打開binder設備 /dev/binder文件,打開成功後會返回一個文件描述符,接着調用fcntl函數來改變剛纔打開的設備文件的性質。接着在調用ioctl函數,ioctl是設備驅動程序中對設備的I/O通道進行管理的函數。所謂對I/O通道進行管理,就是對設備的一些特性進行控制,例如串口的傳輸波特率、馬達的轉速等等,BINDER_SET_MAX_THREADS 就是用戶程序對設備的控制命令,maxThreads則是補充參數,這句話的意思就是告訴binder驅動,這個文件描述符支持的最大線程數。在構造函數中使用mmap函數將該文件描述符映射到內存中,以便使用這塊內存來接受數據。構造函數中作了兩件比較重要的事情:
打開了/dev/binder設備文件,獲取了一個與設備文件相關聯的文件描述符,這也就至關於得到了一個與內核的binder驅動有了交互的通道。
使用mmap函數將該文件描述符映射到內存中,以便使用這塊內存來接受數據。
接着再回到ProcessState::self()中來看看:
sp<ProcessState> ProcessState::self()
{
if (gProcess != NULL) return gProcess;
AutoMutex _l(gProcessMutex);
if (gProcess == NULL) gProcess = new ProcessState;
return gProcess;
}
這個self僅僅是返回了ProcessState對象,ProcessState是以單例模式設計的,主要用來維護當前進程和binder設備通訊時的一個狀態。
有了和binder驅動的交互通道後,接下來作了什麼呢?咱們看下如下這段代碼:
sp<IServiceManager> sm = defaultServiceManager();
defaultServiceManager的業務實如今IServiceManager.cpp中,原型以下:
sp<IServiceManager> defaultServiceManager(){
if (gDefaultServiceManager != NULL)return gDefaultServiceManager;
AutoMutex _l(gDefaultServiceManagerLock);
if (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL));
}
return gDefaultServiceManager;
}
最重要的就是這句代碼了:
gDefaultServiceManager = interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL))
1
再回到ProcessState中去看看getContextObject的實現
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
if (supportsProcesses()) {
return getStrongProxyForHandle(0);
} else {
return getContextObject(String16("default"), caller);
}
}
supportsProcesses()就是說當前的設備是否支持進程,這毫無疑問,真實的設備確定是支持的,因此接下來確定是要走getStrongProxyForHandle(0)方法的。
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
b = new BpBinder(handle); // 建立一個BpBinder
e->binder = b;// 給資源項填充這個binder
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
// This little bit of nastyness is to allow us to add a primary
// reference to the remote proxy when this team doesn't have one
// but another team is sending the handle to us.
result.force_set(b);
e->refs->decWeak(this);
}
}
// 返回了new BpBinder(0); /
return result;
}
lookupHandleLocked函數用來查找對應的資源,一開始確定是沒有的,因此走b==null的分支,在這個分支裏面使用handler==0做爲參數新建了一個BpBinder,而後把這個binder賦值給了handle_entry的binder屬性,最後把這個BpBinder對象返回,也就是說返回的是new BpBinder(0)。我以爲這裏面應該是把handle_entry資源項和當前進程掛鉤的,要否則這裏爲啥要把BpBinder和這個資源項綁定在一塊兒呢。
在這裏有兩個比較重要的東西,就是BpBinder和BBinder,他們兩個是成雙成對的出現,BpBinder是客戶端和服務端的代理類,而BBinder也就是BpBinder對立的一端,表明客戶端要交互的目的端。換種說法講就是BpBinder若是是客戶端,那麼BBinder則是服務端。
接下來gDefaultServiceManager = interface_cast(ProcessState::self()->getContextObject(NULL))就應該轉變爲以下的代碼了:
gDefaultServiceManager = interface_cast<IServiceManager>(BpBinder)
1
這個interface_cast方法描述以下:
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
interface_cast是一個模板方法,INTERFACE表明的就是IserviceManager,裏面調用的是IserviceManager.asInterface方法,而在這個方法裏面,則是利用了BpBinder對象構建了一個BpManagerService對象,咱們知道BpBinder和BBinder二者是一個相互通訊的關係,那麼爲何這裏又來了個BpManagerService,BpManagerService實現了IServiceManager中的業務函數,而且BpManagerService中有個mRemote對象指向了BpBinder對象。就這樣,BpManagerService實現了業務函數而且也有了和服務端進行通訊的表明。接下來就分析一下服務的註冊了。
在MediaPlayerService.cpp中:
void MediaPlayerService::instantiate() {
// defaultServiceManager實際上返回的是BpManagerService,是IServiceManager的後代
defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService());
}
defaultServiceManager()返回的就是BpManagerService,其實現了IServiceManager的業務方法,因此咱們要去BpManagerService中看看addService的實現:
virtual status_t addService(const String16& name, const sp<IBinder>& service)
{
Parcel data, reply; // 數據包
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
// 將數據打包後,把通訊的工做交給了BpServiceManager
// 這個remote其實就也就是mRemote,也就是BpBinder對象
// 數據打包後,傳給了BpBinder對象的transact函數,也就意味着把通訊的工做交給了BpBinder對象來完成
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
// 因此addService函數其實就是一個業務層的函數,工做很簡單,就是把數據打包後,再交給通訊層去處理
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
Parcel咱們能夠理解成一個在進程間傳送的數據包,data是咱們要發送的數據包,而reply則是服務返回過來的數據包,數據打包後,交由remote()函數的transact方法執行,remote()方法返回的就是BpManagerService中的mRemote對象,這個對象指向的是以前的BpBinder對象,因此與服務通訊的工做就交給了BpBinder對象來完成,下面前往BpBinder中去看看這個方法的實現:
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
1
2
3
4
5
6
7
8
9
10
11
BpBinder其實沒有什麼能夠作的實際工做,也就是個表象,這個真實工做又交給了IPCThreadState的transact,IPCThreadState纔是在進程中真正幹活的夥計,前往IPCThreadState.cpp中看看:
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
......
if (err == NO_ERROR) {
LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
// handle值爲0,表明通訊的目的端
// BC_TRANSACTION參數: 應用程序向binder設備發送消息的消息碼。
// 而binder設備嚮應用程序回覆消息碼以BR_開頭。
// 先發數據,而後等待結果返回
// 其實在這個方法裏面只是把數據寫入到mOut中,並無發出去
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
........
if ((flags & TF_ONE_WAY) == 0) {
............
#endif
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
#if 0
.........
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}
writeTransactionData看樣子就只是把數據寫入到一個地方了,原型以下:
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
//handle是0,表示目的端
tr.target.handle = handle;
// 消息碼
tr.code = code;
tr.flags = binderFlags;
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
tr.data_size = sizeof(status_t);
tr.data.ptr.buffer = statusBuffer;
tr.offsets_size = 0;
tr.data.ptr.offsets = NULL;
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
這個方法只是直接把命令直接寫到out中去,並無把這個消息發出去,因此繼續往下看waitForResponse方法,承載了發送和接受數據的工做確定就在這裏面了:
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing waitForResponse Command: "
<< getReturnString(cmd) << endl;
}
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
case BR_ACQUIRE_RESULT:
{
LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
const int32_t result = mIn.readInt32();
if (!acquireResult) continue;
*acquireResult = result ? NO_ERROR : INVALID_OPERATION;
}
goto finish;
case BR_REPLY:
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t),
freeBuffer, this);
} else {
err = *static_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
}
} else {
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
continue;
}
}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
waitForResponse承載了發送和接受數據,那麼發送給binder驅動,這個過程是怎麼通訊的呢?看下talkWithDriver函數:
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened");
// binder_write_read是與binder驅動交換數據的結構
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
// 請求命令的填充
bwr.write_size = outAvail;
bwr.write_buffer = (long unsigned int)mOut.data();
// This is what we'll read.
if (doReceive && needRead) {
// 接受數據緩衝區信息的填充,若是之後收到數據了,就直接填在mIn中
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (long unsigned int)mIn.data();
} else {
bwr.read_size = 0;
}
IF_LOG_COMMANDS() {
TextOutput::Bundle _b(alog);
if (outAvail != 0) {
alog << "Sending commands to driver: " << indent;
const void* cmds = (const void*)bwr.write_buffer;
const void* end = ((const uint8_t*)cmds)+bwr.write_size;
alog << HexDump(cmds, bwr.write_size) << endl;
while (cmds < end) cmds = printCommand(alog, cmds);
alog << dedent;
}
alog << "Size of receive buffer: " << bwr.read_size
<< ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
}
// Return immediately if there is nothing to do.
// 若是沒有任何的數據交換,則立馬返回
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
IF_LOG_COMMANDS() {
alog << "About to read/write, write size = " << mOut.dataSize() << endl;
}
#if defined(HAVE_ANDROID_OS)
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
IF_LOG_COMMANDS() {
alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
}
} while (err == -EINTR);
IF_LOG_COMMANDS() {
alog << "Our err: " << (void*)err << ", write consumed: "
<< bwr.write_consumed << " (of " << mOut.dataSize()
<< "), read consumed: " << bwr.read_consumed << endl;
}
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < (ssize_t)mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
IF_LOG_COMMANDS() {
TextOutput::Bundle _b(alog);
alog << "Remaining data size: " << mOut.dataSize() << endl;
alog << "Received commands from driver: " << indent;
const void* cmds = mIn.data();
const void* end = mIn.data() + mIn.dataSize();
alog << HexDump(cmds, mIn.dataSize()) << endl;
while (cmds < end) cmds = printReturnCommand(alog, cmds);
alog << dedent;
}
return NO_ERROR;
}
return err;
}
看來這裏和binder驅動設備進行通訊並非使用read/write方式,而是ioctl方式。這樣就完成了binder通訊了。
就分析到這裏吧。有空再深刻研究