Android幀緩衝區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析(9)

         5. 圖形緩衝區的釋放過程
        前面提到,用戶空間的應用程序用到的圖形緩衝區是由Gralloc模塊中的函數gralloc_free來釋放的,這個函數實現在文件hardware/libhardware/modules/gralloc/gralloc.cpp中,如下所示:
  1. static int gralloc_free(alloc_device_t* dev,  
  2.         buffer_handle_t handle)  
  3. {  
  4.     if (private_handle_t::validate(handle) < 0)  
  5.         return -EINVAL;  
  6.   
  7.     private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(handle);  
  8.     if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {  
  9.         // free this buffer  
  10.         private_module_t* m = reinterpret_cast<private_module_t*>(  
  11.                 dev->common.module);  
  12.         const size_t bufferSize = m->finfo.line_length * m->info.yres;  
  13.         int index = (hnd->base - m->framebuffer->base) / bufferSize;  
  14.         m->bufferMask &= ~(1<<index);  
  15.     } else {  
  16.         gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(  
  17.                 dev->common.module);  
  18.         terminateBuffer(module, const_cast<private_handle_t*>(hnd));  
  19.     }  
  20.   
  21.     close(hnd->fd);  
  22.     delete hnd;  
  23.     return 0;  
  24. }  

        要釋放的圖形緩衝區使用參數handle來描述。前面提到,從Gralloc模塊中分配的圖形緩衝區是使用private_handle_t結構體來描述的,因此,這裏的參數handle應該指向一個private_handle_t結構體,這是通過調用private_handle_t類的靜態成員函數validate來驗證的。private_handle_t類的靜態成員函數validate的實現可以參考前面第1部分的內容。
 
        要釋放的圖形緩衝區有可能是在系統幀緩衝區分配的,也有可能是在內存中分配的,這可以通過檢查它的標誌值flags的PRIV_FLAGS_FRAMEBUFFER位是否等於1來確認。
        如果要釋放的圖形緩衝區是在系統幀緩衝區中分配的,那麼首先要知道這個圖形緩衝區是系統幀緩衝區的第index個位置,接着再將變量m所描述的一個private_module_t結構體的成員變量bufferMask的第index位重置爲0即可。我們只需要將要釋放的圖形緩衝區的開始地址減去系統幀緩衝區的基地址,再除以一個圖形緩衝區的大小,就可以知道要釋放的圖形緩衝區是系統幀緩衝區的第幾個位置。這個過程剛好是在系統幀緩衝區中分配圖形緩衝區的逆操作。
        如果要釋放的圖形緩衝區是內存中分配的,那麼只需要調用另外一個函數terminateBuffer來解除要釋放的圖形緩衝區在當前進程的地址空間中的映射。
        最後,這個函數還會將用來描述要釋放的圖形緩衝區的private_handle_t結構體所佔用的內存釋放掉,並且將要要釋放的圖形緩衝區所在的系統幀緩衝區或者匿名共享內存的文件描述符關閉掉。
       函數terminateBuffer實現在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
  1. int terminateBuffer(gralloc_module_t const* module,  
  2.         private_handle_t* hnd)  
  3. {  
  4.     if (hnd->base) {  
  5.         // this buffer was mapped, unmap it now  
  6.         gralloc_unmap(module, hnd);  
  7.     }  
  8.   
  9.     return 0;  
  10. }  
       它通過調用另外一個函數gralloc_unmap來解除參數hnd所描述的一個圖形緩衝區在當前進程的地址空間中的映射。後面在分析圖形緩衝區的註銷過程時,我們再詳細分析函數gralloc_unmap的實現。
 
       至此,圖形緩衝區的釋放過程就分析完成了,接下來我們繼續分析圖形緩衝區的註冊過程。
       6. 圖形緩衝區的註冊過程
       前面提到,在Android系統中,所有的圖形緩衝區都是由SurfaceFlinger服務分配的,而當一個圖形緩衝區被分配的時候,它會同時被映射到請求分配的進程的地址空間去,即分配的過程同時也包含了註冊的過程。但是對用戶空間的其它的應用程序來說,它們所需要的圖形緩衝區是在由SurfaceFlinger服務分配的,因此,當它們得到SurfaceFlinger服務分配的圖形緩衝區之後,還需要將這塊圖形緩衝區映射到自己的地址空間來,以便可以使用這塊圖形緩衝區。這個映射的過程即爲我們接下來要分析的圖形緩衝區註冊過程。
       前面還提到,註冊圖形緩衝區的操作是由Gralloc模塊中的函數gralloc_register_buffer來實現的,這個函數實現在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
  1. int gralloc_register_buffer(gralloc_module_t const* module,  
  2.         buffer_handle_t handle)  
  3. {  
  4.     if (private_handle_t::validate(handle) < 0)  
  5.         return -EINVAL;  
  6.   
  7.     // if this handle was created in this process, then we keep it as is.  
  8.     int err = 0;  
  9.     private_handle_t* hnd = (private_handle_t*)handle;  
  10.     if (hnd->pid != getpid()) {  
  11.         void *vaddr;  
  12.         err = gralloc_map(module, handle, &vaddr);  
  13.     }  
  14.     return err;  
  15. }  
       這個函數首先驗證參數handle指向的一塊圖形緩衝區的確是由Gralloc模塊分配的,方法是調用private_handle_t類的靜態成員函數validate來驗證,即如果參數handle指向的是一個private_handle_t結構體,那麼它所指向的一塊圖形緩衝區就是由Gralloc模塊分配的。
 
       通過了上面的檢查之後,函數gralloc_register_buffer還需要檢查當前進程是否就是請求Gralloc模塊分配圖形緩衝區hnd的進程。如果是的話,那麼當前進程在請求Gralloc模塊分配圖形緩衝區hnd的時候,就已經將圖形緩衝區hnd映射進自己的地址空間來了,因此,這時候就不需要重複在當前進程中註冊這個圖形緩衝區。
       真正執行註冊圖形緩衝區的操作是由函數gralloc_map來實現的,這個函數也是實現文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示:
  1. static int gralloc_map(gralloc_module_t const* module,  
  2.         buffer_handle_t handle,  
  3.         void** vaddr)  
  4. {  
  5.     private_handle_t* hnd = (private_handle_t*)handle;  
  6.     if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {  
  7.         size_t size = hnd->size;  
  8.         void* mappedAddress = mmap(0, size,  
  9.                 PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);  
  10.         if (mappedAddress == MAP_FAILED) {  
  11.             LOGE("Could not mmap %s", strerror(errno));  
  12.             return -errno;  
  13.         }  
  14.         hnd->base = intptr_t(mappedAddress) + hnd->offset;  
  15.         //LOGD("gralloc_map() succeeded fd=%d, off=%d, size=%d, vaddr=%p",  
  16.         //        hnd->fd, hnd->offset, hnd->size, mappedAddress);  
  17.     }  
  18.     *vaddr = (void*)hnd->base;  
  19.     return 0;  
  20. }  
       由於在系統幀緩衝區中分配的圖形緩衝區只在SurfaceFlinger服務中使用,而SurfaceFlinger服務在初始化系統幀緩衝區的時候,已經將系統幀緩衝區映射到自己所在的進程中來了,因此,函數gralloc_map如果發現要註冊的圖形緩衝區是在系統幀緩衝區分配的時候,那麼就不需要再執行映射圖形緩衝區的操作了。
 
       如果要註冊的圖形緩衝區是在內存中分配的,即它的標誌值flags的PRIV_FLAGS_FRAMEBUFFER位等於1,那麼接下來就需要將它映射到當前進程的地址空間來了。由於要註冊的圖形緩衝區是在文件描述符hnd->fd所描述的一塊匿名共享內存中分配的,因此,我們只需要將文件描述符hnd->fd所描述的一塊匿名共享內存映射到當前進程的地址空間來,就可以將參數hnd所描述的一個圖形緩衝區映射到當前進程的地址空間來。
       由於映射文件描述符hnd->fd得到的是一整塊匿名共享內存在當前進程地址空間的基地址,而要註冊的圖形緩衝區可能只佔據這塊匿名共享內存的某一小部分,因此,我們還需要將要註冊的圖形緩衝區的在被映射的匿名共享內存中的偏移量hnd->offset加上被映射的匿名共享內存的基地址hnd->base,纔可以得到要註冊的圖形緩衝區在當前進程中的訪問地址,這個地址最終又被寫入到hnd->base中去。
       註冊圖形緩衝區的過程就是這麼簡單,接下來我們再分析圖形緩衝區的註銷過程。
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章