乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      紅狼博客 ? Android源碼分析:大塊內(nèi)存的跨進(jìn)程共享

       老匹夫 2014-02-22

      我們知道,傳統(tǒng)的IPC方式傳遞大塊內(nèi)存時,一般使用共享內(nèi)存的方式。在Android Binder IPC中,有著自己獨特的跨進(jìn)程傳遞方式。它也同樣,避免了內(nèi)存拷貝的方式,可以讓內(nèi)存基址和偏移在進(jìn)程間不斷而且方便的傳遞。Android傳遞大內(nèi)存塊的方式稍有不同,這些大內(nèi)存塊往往位于特定的設(shè)備文件中,如pmemashmem匿名共享內(nèi)存:Anonymous Shared Memory,當(dāng)然,也支持共享使用其它特定設(shè)備的緩沖區(qū)。pmemashmem就是AndroidLinux內(nèi)核中虛擬出的兩個設(shè)備,前者是物理連續(xù)的大塊內(nèi)存區(qū)域,通常大小在8MB~16MB之間,不同的硬件平臺或產(chǎn)品對其定義不同,它也位于系統(tǒng)的RAM中,不由內(nèi)核的mm管理,而是劃歸給虛擬的設(shè)備pmem來管理,用戶空間通過設(shè)備文件與其交互,一般用于Camera;后者原理與前者相同,也是通過設(shè)備文件使用,但它可能物理上不連續(xù),大小也通常小些,作為普通的共享之用。在使用之前,必須調(diào)用mmap將設(shè)備緩沖區(qū)映射到系統(tǒng)的進(jìn)程內(nèi)存空間。

       

      具體實現(xiàn)則是通過libbinder.so庫中的IMemoryIMemoryHeap、MemoryHeapBase、MemoryHeapPmem等類實現(xiàn)的。將某個設(shè)備(如pmemashmem)管理的內(nèi)存映射(mmap)到系統(tǒng)的進(jìn)程內(nèi)存空間,這塊內(nèi)存稱為內(nèi)存堆(MemeoryHeap

      IMemoryHeap定義了獲取內(nèi)存堆信息的接口,這個內(nèi)存堆的信息有:HeapID(亦即設(shè)備文件描述符)、經(jīng)過mmap映射到進(jìn)程空間的基址、內(nèi)存堆大小以及訪問標(biāo)志??梢允褂孟旅嫠膫€API來獲得它們的值:

      virtual int getHeapID() const = 0;

      virtual void* getBase() const = 0;

      virtual size_t getSize() const = 0;

      virtual uint32_t getFlags() const = 0;

      Client側(cè)調(diào)用接口類實際上調(diào)用到子類BpMemoryHeap對象。經(jīng)Binder跨進(jìn)程后,請求由server側(cè)的子類MemoryHeapBaseMemoryHeapPmem完成。類的繼承關(guān)系圖如下:

       

       

       

      IMemoryHeap接口中定義的四個純虛函數(shù)由子類BpMemoryHeap中重載實現(xiàn),它們均是直接返回對應(yīng)的成員的值(見圖中的BpMemeoryHeap,上面有四個私有成員變量,下面有四個共有成員函數(shù))。不過在返回之前,都調(diào)用了映射函數(shù)assertMapped確保已經(jīng)映射:

       

      void BpMemoryHeap::assertMapped() const

      {

      if (mHeapId == -1) {//在還沒有映射的情況下才執(zhí)行下面代碼

      sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());//asBinder實際調(diào)用的是remote(),也就是得到BpBinder對象

      sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));//通過bindercache緩存中找到對應(yīng)的BpMemoryHeap對象,然后調(diào)用下面一行進(jìn)行真正的映射

      heap->assertReallyMapped();//進(jìn)行真正的映射

      if (heap->mBase != MAP_FAILED) {//沒有出錯則繼續(xù)

      Mutex::Autolock _l(mLock);

      if (mHeapId == -1) {//還沒有更新basesizeheapID等信息,則更新它們

      mBase = heap->mBase;

      mSize = heap->mSize;

      android_atomic_write( dup( heap->mHeapId ), &mHeapId );

      }

      } else {

      // something went wrong

      free_heap(binder);

      }

      }

      }

      見代碼注釋,只有在還沒有映射的情況下才進(jìn)行映射,判斷的依據(jù)是文件描述符是否為-1。首先獲取對應(yīng)的BpBinder對象,然后在全局的cache中查詢BpMemoryHeap對象,接著調(diào)用BpMemoryHeap對象的assertReallyMapped進(jìn)行真正的映射。

      全局緩存cache(即類HeapCache)中維護(hù)著一個鍵值表,它是從Bpinder對象的弱指針到heap_info_t結(jié)構(gòu)體的映射:

      struct heap_info_t {

      sp<IMemoryHeap> heap;

      int32_t count; //使用計數(shù)

      };

       

      KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;

       

      HeapCachefind_heap函數(shù)首先查詢其鍵值表,若找到則直接返回BpMemoryHeap對象強(qiáng)指針,并將使用計數(shù)加1;若沒找到,則創(chuàng)建一個BpMemoryHeap對象,并將其添加到表項中:

      heap_info_t info;

      info.heap = interface_cast<IMemoryHeap>(binder); //binder創(chuàng)建一個BpMemoryHeap對象

      info.count = 1;

      //LOGD(“adding binder=%p, heap=%p, count=%d”,

      // binder.get(), info.heap.get(), info.count);

      mHeapCache.add(binder, info);

      return info.heap;

       

      也就是說,在使用IMemoryHeap也就是創(chuàng)建其子類對象時,不是直接使用interface_cast<IMemoryHeap>(binder)進(jìn)行創(chuàng)建,而是首先在cache中查詢;若沒有查到,才進(jìn)行創(chuàng)建。也就是確保內(nèi)存堆代理BpMemoryHeap對象在一個進(jìn)程中的唯一性。當(dāng)多次使用到某個內(nèi)存堆代理時,有使用計數(shù)維護(hù)引用次數(shù)。這樣做的目的是確保只對設(shè)備文件進(jìn)行一次mmap映射。從assertMapped調(diào)用assertReallyMapped就可以看出來:

      void BpMemoryHeap::assertReallyMapped() const

      {

      if (mHeapId == -1) {

      Parcel data, reply;

      data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());

      status_t err = remote()->transact(HEAP_ID, data, &reply);

      int parcel_fd = reply.readFileDescriptor();//從對方獲取設(shè)備文件描述符,通常文件描述符不能跨進(jìn)程,但AndroidBinder IPCLinux內(nèi)核中做了特殊處理,使它們共享內(nèi)核中的file節(jié)點,因此可以跨進(jìn)程。

      ssize_t size = reply.readInt32();//得到設(shè)備內(nèi)存大小

      uint32_t flags = reply.readInt32();//訪問標(biāo)志

       

      LOGE_IF(err, “binder=%p transaction failed fd=%d, size=%ld, err=%d (%s)”,

      asBinder().get(), parcel_fd, size, err, strerror(-err));

       

      int fd = dup( parcel_fd ); //復(fù)制一個文件描述符

      LOGE_IF(fd==-1, “cannot dup fd=%d, size=%ld, err=%d (%s)”,

      parcel_fd, size, err, strerror(errno));

       

      int access = PROT_READ;

      if (!(flags & READ_ONLY)) {

      access |= PROT_WRITE;

      }//若沒有只寫明確規(guī)定是只讀,則加上寫標(biāo)志

       

      Mutex::Autolock _l(mLock);

      if (mHeapId == -1) {//再次確保還沒有映射

      mRealHeap = true;

      mBase = mmap(0, size, access, MAP_SHARED, fd, 0);//映射到內(nèi)存空間

      if (mBase == MAP_FAILED) {//出錯,則給出log信息

      LOGE(“cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)”,

      asBinder().get(), size, fd, strerror(errno));

      close(fd);

      } else {//成功,更新其余三個成員信息

      mSize = size;

      mFlags = flags;

      android_atomic_write(fd, &mHeapId);

      }

      }

      }

      }

      可見,它從對方得到相關(guān)信息后,在本進(jìn)程空間重新進(jìn)行mmap映射。

       

      server側(cè),類MemoryHeapBase負(fù)責(zé)處理來自Client側(cè)的請求,它有多個構(gòu)造函數(shù),當(dāng)沒有指定設(shè)備文件名稱或設(shè)備文件描述符時,它就默認(rèn)使用ashmem上的內(nèi)存,否則使用指定的設(shè)備文件的內(nèi)存。在這些構(gòu)造函數(shù)中,首先檢查并確保size是頁對齊的,然后調(diào)用打開設(shè)備文件(若還沒有文件描述符fd的話),再調(diào)用mapfd成員函數(shù)對設(shè)備文件進(jìn)行mmap映射。

      若使用的是pmem,則由MemoryHeapBase的子類MemoryHeapPmem來處理。當(dāng)然,平臺開發(fā)商也可以編寫其它子類,來實現(xiàn)對其它內(nèi)存設(shè)備的支持。

      libbinder庫中,還有類MemoryDealer這個類用來進(jìn)行內(nèi)存分配管理,它包含一個內(nèi)存分配器SimpleBestFitAllocator,所分配的內(nèi)存塊為Allocation(繼承自MemoryBase),但是當(dāng)前還沒有使用它們。

       

      一個內(nèi)存堆上,往往可以分為多個區(qū)域以供調(diào)用者使用,如Camera預(yù)覽中的圖像的一楨。IMemory就代表中內(nèi)存堆中的這塊內(nèi)存區(qū)域??梢杂扇齻€變量來確定它:在哪個MemoryHeap、在堆中的偏移量offset和大小size。我們可以通過getMemory函數(shù)獲得該內(nèi)存區(qū)域的這三樣信息:

      sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const

      偏移量offset和大小size通過修改實參傳回,而MemoryHeap則通過函數(shù)返回。為方便起見,IMemory還提供了三個方便使用的輔助函數(shù):通過fastPointerpointer直接得到指向內(nèi)存塊的指針,通過size函數(shù)可以得到內(nèi)存塊大小。

       

      不管是內(nèi)存堆MemoryHeapBase還是其子類,以及其里面的內(nèi)存區(qū)域,均由使用者創(chuàng)建。如如CameraService依賴于Camera硬件來實現(xiàn)各項功能,這就是平臺廠商實現(xiàn)的CameraHardeInterface的子類(如有的平臺使用的是V4L2,對應(yīng)的子類是CameraHardwareV4L2)。在Camera硬件實現(xiàn)中,一般都需要創(chuàng)建MemoryHeapBaseMemoryBase對象,即server側(cè)對象。Camera的使用者(如應(yīng)用程序)在Client側(cè)通過調(diào)用,將得到接口對象強(qiáng)指針,實際指向前述BpXXX(即IMemoryHeapIMemory的子類:BpMemoryHeapBpMemory)對象。Client側(cè)在在自己的進(jìn)程里進(jìn)行內(nèi)存映射后,通過對應(yīng)接口API就可以得到heapID,base基址和偏移量offset,于是就可以對數(shù)據(jù)(如預(yù)覽、拍照和錄像的數(shù)據(jù))進(jìn)行自己的處理了。因此,在serverclient側(cè),它們基址多半不同,但都映射到同一設(shè)備的內(nèi)存上,如pmem中的一塊內(nèi)存。這樣,就實現(xiàn)了大塊內(nèi)存的共享。也就是說,跨進(jìn)程傳遞的過程,實際是傳遞設(shè)備文件描述符、基址和偏移量等信息的過程。

       

       

      本文鏈接地址: http://www./?p=902

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多