1. 程式人生 > >Binder系列5—註冊服務(addService)

Binder系列5—註冊服務(addService)

framework/native/libs/binder/
  - Binder.cpp
  - BpBinder.cpp
  - IPCThreadState.cpp
  - ProcessState.cpp
  - IServiceManager.cpp
  - IInterface.cpp
  - Parcel.cpp

frameworks/native/include/binder/
  - IInterface.h (包括BnInterface, BpInterface)

一.概述

1.1 media服務註冊

media入口函式是main_mediaserver.cpp中的main()方法,程式碼如下:

int main(int argc __unused, char** argv)
{
    ...
    InitializeIcuOrDie();
    //獲得ProcessState例項物件【見小節2.1】
    sp<ProcessState> proc(ProcessState::self());
    //獲取BpServiceManager物件
    sp<IServiceManager> sm = defaultServiceManager();
    AudioFlinger::instantiate();
    //註冊多媒體服務  【見小節3.1
】 MediaPlayerService::instantiate(); ResourceManagerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); SoundTriggerHwService::instantiate(); RadioService::instantiate(); registerExtensions(); //啟動Binder執行緒池 ProcessState::self()->startThreadPool(); //
當前執行緒加入到執行緒池 IPCThreadState::self()->joinThreadPool(); }

過程說明:

  1. 獲取ServiceManager: 講解了defaultServiceManager()返回的是BpServiceManager物件, 用於跟servicemanager程序通訊;
  2. 理解Binder執行緒池的管理, 講解了startThreadPool和joinThreadPool過程.

本文的重點就是講解Native層服務註冊的過程.

1.2 類圖

在Native層的服務以media服務為例,來說一說服務註冊過程,先來看看media的整個的類關係圖。 點選檢視大圖

add_media_player_service

圖解:

  • 藍色代表的是註冊MediaPlayerService服務所涉及的類
  • 綠色代表的是Binder架構中與Binder驅動通訊過程中的最為核心的兩個類;
  • 紫色代表的是註冊服務和獲取服務的公共介面/父類;

1.3 時序圖

先通過一幅圖來說說,media服務啟動過程是如何向servicemanager註冊服務的。

點選檢視大圖

addService

二. ProcessState

2.1 ProcessState::self

[-> ProcessState.cpp]

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }

    //例項化ProcessState 【見小節2.2】
    gProcess = new ProcessState;
    return gProcess;
}

獲得ProcessState物件: 這也是單例模式,從而保證每一個程序只有一個ProcessState物件。其中gProcessgProcessMutex是儲存在Static.cpp類的全域性變數。

2.2 ProcessState初始化

[-> ProcessState.cpp]

ProcessState::ProcessState()
    : mDriverFD(open_driver()) // 開啟Binder驅動【見小節2.3】
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        //採用記憶體對映函式mmap,給binder分配一塊虛擬地址空間【見小節2.4】
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            close(mDriverFD); //沒有足夠空間分配給/dev/binder,則關閉驅動
            mDriverFD = -1;
        }
    }
}
  • ProcessState的單例模式的惟一性,因此一個程序只打開binder裝置一次,其中ProcessState的成員變數mDriverFD記錄binder驅動的fd,用於訪問binder裝置。
  • BINDER_VM_SIZE = (1*1024*1024) - (4096 *2), binder分配的預設記憶體大小為1M-8k。
  • DEFAULT_MAX_BINDER_THREADS = 15,binder預設的最大可併發訪問的執行緒數為16。

2.3 open_driver

[-> ProcessState.cpp]

static int open_driver()
{
    // 開啟/dev/binder裝置,建立與核心的Binder驅動的互動通道
    int fd = open("/dev/binder", O_RDWR);
    if (fd >= 0) {
        fcntl(fd, F_SETFD, FD_CLOEXEC);
        int vers = 0;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
            close(fd);
            fd = -1;
        }
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;

        // 通過ioctl設定binder驅動,能支援的最大執行緒數
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        if (result == -1) {
            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
        }
    } else {
        ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
    }
    return fd;
}

open_driver作用是開啟/dev/binder裝置,設定binder支援的最大執行緒數。關於binder驅動的相應方法,見文章Binder Driver初探

ProcessState採用單例模式,保證每一個程序都只打開一次Binder Driver。

2.4 mmap

//原型
voidmmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) //此處 mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);

引數說明:

  • addr: 代表對映到程序地址空間的起始地址,當值等於0則由核心選擇合適地址,此處為0;
  • size: 代表需要對映的記憶體地址空間的大小,此處為1M-8K;
  • prot: 代表記憶體對映區的讀寫等屬性值,此處為PROT_READ(可讀取);
  • flags: 標誌位,此處為MAP_PRIVATE(私有對映,多程序間不共享內容的改變)和 MAP_NORESERVE(不保留交換空間)
  • fd: 代表mmap所關聯的檔案描述符,此處為mDriverFD;
  • offset:偏移量,此處為0。

mmap()經過系統呼叫,執行binder_mmap過程。

三. 服務註冊

3.1 instantiate

[-> MediaPlayerService.cpp]

void MediaPlayerService::instantiate() {
    //註冊服務【見小節3.2】
    defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService());
}

註冊服務MediaPlayerService:由defaultServiceManager()返回的是BpServiceManager,同時會建立ProcessState物件和BpBinder物件。 故此處等價於呼叫BpServiceManager->addService。其中MediaPlayerService位於libmediaplayerservice庫.

3.2 BpSM.addService

[-> IServiceManager.cpp ::BpServiceManager]

virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated) {
    Parcel data, reply; //Parcel是資料通訊包
    //寫入頭資訊"android.os.IServiceManager"
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());   
    data.writeString16(name);        // name為 "media.player"
    data.writeStrongBinder(service); // MediaPlayerService物件【見小節3.2.1】
    data.writeInt32(allowIsolated ? 1 : 0); // allowIsolated= false
    //remote()指向的是BpBinder物件【見小節3.3】
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}

服務註冊過程:向ServiceManager註冊服務MediaPlayerService,服務名為”media.player”;

3.2.1 writeStrongBinder

[-> parcel.cpp]

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

3.2.2 flatten_binder

[-> parcel.cpp]

status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder(); //本地Binder不為空
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.binder = 0;
            obj.handle = handle;
            obj.cookie = 0;
        } else { //進入該分支
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
        ...
    }
    //【見小節3.2.3】
    return finish_flatten_binder(binder, obj, out);
}

將Binder物件扁平化,轉換成flat_binder_object物件。

  • 對於Binder實體,則cookie記錄Binder實體的指標;
  • 對於Binder代理,則用handle記錄Binder代理的控制代碼;

關於localBinder,程式碼見Binder.cpp。

BBinder* BBinder::localBinder()
{
    return this;
}

BBinder* IBinder::localBinder()
{
    return NULL;
}

3.2.3 finish_flatten_binder

inline static status_t finish_flatten_binder(
    const sp<IBinder>& , const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}

將flat_binder_object寫入out。

3.3 BpBinder::transact

[-> BpBinder.cpp]

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    if (mAlive) {
        // code=ADD_SERVICE_TRANSACTION【見小節3.4】
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

Binder代理類呼叫transact()方法,真正工作還是交給IPCThreadState來進行transact工作。先來 看看IPCThreadState::self的過程。

3.3.1 IPCThreadState::self

[-> IPCThreadState.cpp]

IPCThreadState* IPCThreadState::self()
{
    if (gHaveTLS) {
restart:
        const pthread_key_t k = gTLS;
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
        if (st) return st;
        return new IPCThreadState;  //初始IPCThreadState 【見小節3.3.2】
    }

    if (gShutdown) return NULL;

    pthread_mutex_lock(&gTLSMutex);
    if (!gHaveTLS) { //首次進入gHaveTLS為false
        if (pthread_key_create(&gTLS, threadDestructor) != 0) { //建立執行緒的TLS
            pthread_mutex_unlock(&gTLSMutex);
            return NULL;
        }
        gHaveTLS = true;
    }
    pthread_mutex_unlock(&gTLSMutex);
    goto restart;
}

TLS是指Thread local storage(執行緒本地儲存空間),每個執行緒都擁有自己的TLS,並且是私有空間,執行緒之間不會共享。通過pthread_getspecific/pthread_setspecific函式可以獲取/設定這些空間中的內容。從執行緒本地儲存空間中獲得儲存在其中的IPCThreadState物件。

3.3.2 IPCThreadState初始化

[-> IPCThreadState.cpp]

IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()),
      mMyThreadId(gettid()),
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0)
{
    pthread_setspecific(gTLS, this);
    clearCaller();
    mIn.setDataCapacity(256);
    mOut.setDataCapacity(256);
}

每個執行緒都有一個IPCThreadState,每個IPCThreadState中都有一個mIn、一個mOut。成員變數mProcess儲存了ProcessState變數(每個程序只有一個)。

  • mIn 用來接收來自Binder裝置的資料,預設大小為256位元組;
  • mOut用來儲存發往Binder裝置的資料,預設大小為256位元組。

3.4 IPC::transact

[-> IPCThreadState.cpp]


            
           

相關推薦

Binder系列5註冊服務(addService)

framework/native/libs/binder/ - Binder.cpp - BpBinder.cpp - IPCThreadState.cpp - ProcessState.cpp - IServiceManager.cpp - IInt

Docker Swarm系列——5.Swarm服務檔案

在這篇文章中,大家將會了解如何通過docker-compose.yml檔案的形式部署服務。從docker1.13版本開始,新的docker compose v3引入適用於Docker Swarm模式的stack deploy服務部署方式,所有我們就直接使用新的

Binder系列6—獲取服務(getService)

一、 獲取服務在Native層的服務註冊,我們選擇以media為例來展開講解,先來看看media的類關係圖。1.1 類圖點選檢視大圖圖解:藍色: 代表獲取MediaPlayerService服務相關的類;綠色: 代表Binder架構中與Binder驅動通訊過程中的最為核心的兩

【thinkphp5+swoole賽事直播教程系列5】HTTP服務(下)http_server.php

<?php $http = new swoole_http_server("0.0.0.0", 8811); $http->set( [ 'enable_static_handler' => true, 'docu

ElasticSearch系列(5) 叢集不間斷服務的節點版本升級步驟

During a rolling upgrade, primary shards assigned to a node with the higher version will never have their replicas assigned to a node with the lower versi

系列5-springCloud微服務--zuul代理實現

1.zuul代理服務配置步驟1:新建zuul服務模組,修改pom檔案,新增如下依賴:<dependency> <groupId>org.springframework.cloud</groupId> <artifactId&g

Swift新手教程系列5-函數+selector在swift中的使用方法

move 入口 http event use div 程序 解決 lin 原創blog。轉載請註明出處 近期在用swift寫代碼,盡管遇到一些問題,可是代碼量確實減了不少。 swfit新手教程系列會隨著我使用swfit中的積累,不斷地去修正更新 之前的教程 swift單

IntelliJ 、Pycharm、webstorm 2017 註冊碼及註冊服務

fhe 出現 usr wnlb 沒有 bag bbc 應該 targe jetbrains 家的東西都非常好看,但是價格貴的令人發指,所以我搭建了一個 Pycharm激活服務器,可以用來激活 Pycharm,IntelliJ IDEA,WebStorm。避免頻繁更換激活碼的

問題解決系列: 後臺服務流量控制- 控制訪問別的服務的速度

發送 template 個人 exce 保護 rms 這一 ole 每分鐘 互聯網的後臺提倡大系統小做,微服務化。所以後臺服務之間相互依賴,我依賴別人的,別人也依賴我的,這很正常。但是後臺服務講穩定性。只有一切可控,才能談穩定性。為了不沖垮下遊的服務,我們有兩種做法:一種是

ACdream區域賽指導賽之手速賽系列(5) 題解

hello cor hang repeated exit p s -o inpu ans A - Problem A Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/

C++遊戲系列5:不止有一件武器

location ref 初始 .text isalive urn lan dead then 很多其它見:C++遊戲系列文件夾 知識點:對象數組作為數據成員 改進:每一個角色所持有的武器不僅僅一件,故持有的武器,用了對象數組來表示,當然,也能

企業案例:系統開機啟動項優化 除了下面5服務以外都關閉 crond   sshd     network rsyslog sysstat

企業案例:系統開機啟動項優化 除了下面5個服務以外都關閉 crond sshd network rsyslog sysstat第一步:把要執行命令的樣子顯示出來[[email protected]/* */ ~]# chkconfig|egrep "sshd|crond|rsys

20170825L08-05老男孩linux實戰運維培訓-Lamp系列之-Apache服務生產實戰應用指南02

apache這一節說Apache的安裝目錄文件具體介紹了一些重要文件的配置tree -L 1 /usr/local/apache[[email protected] extra]# tree -L 1 /usr/local/apache/usr/local/apache├── apache ->

20170830L08-06老男孩linux實戰運維培訓-Lamp系列之-Apache服務生產實戰應用指南03

apache還是說的apache的設置這一次說的是虛擬主機主要配置文件httpd.confhttpd-vhhsots.confhttpd.conf主要控制目錄的訪問httpd-vhosts.conf控制域名的轉換,要別名,日誌的路徑對於實驗中的訪問主機中要設置 hosts文件<Directory "/v

5.samba服務

smaba五、samba服務器1、概述Samba最先在linux和windows兩個平臺之間搭建起來的一座橋梁,它實現了linux系統和windows系統之間的通信,比如拷貝文件、實現不同操作系統之間的資源共享等,我們可以將其架設成一個功能強大的文件服務器,也可以將其架設成打印服務器提供給本地和遠程聯機打印。

[js高手之路]深入淺出webpack教程系列5-插件使用之html-webpack-plugin配置(中)

logs 定義數據 ash con ack move lan ref min 上文我們講到了options的配置和獲取數據的方式,本文,我們繼續深入options的配置 一、html-webpack-plugin插件中的options除了自己定義了一些基本配置外,我們是可以

linux註冊服務教程

.cn spl 界面 http 優先 方式 .com strong 完成 該說明是項目完成很久之後,整理資料時的偶然發現,當時所操作的linux為中標麒麟,需要對項目進行開機自啟,對llinux還不熟悉,找不到linux中的服務自啟設置。輾轉多次才找到了解決方案。記錄以供參

SpringCloud系列研究---Eureka服務發現

url next register default spring one static demo auto :創建項目工程 新建project 這裏選擇gradle 直接next 繼續next 最後點擊finish 二:創建Eureka服務中心 選擇第一步中創

windows 安裝redis並註冊服務

service evel img 開啟 logs level exe -c start windows下載地址 https://github.com/MSOpenTech/redis/releases 啟動:redis-server redis.

Openstack 安裝部署指南翻譯系列 之 Horizon服務安裝(Dashboad)

openstack 翻譯 horizon安裝1.1.1.1. Horizon服務安裝(Dashboad)本節介紹如何在控制器節點上安裝和配置儀表板。儀表板所需的唯一核心服務是身份服務。您可以使用儀表板與其他服務(如鏡像服務,計算和網絡)結合使用。您還可以在具有獨立服務(如對象存儲)的環境中使用儀表板。註意:本