Android native進程間通信實例-binder結合共享內存


  在android源碼的驅動目錄下,一般會有共享內存的相關實現源碼,目錄是:kernel\drivers\staging\android\ashmem.c。但是本篇文章不是講解android共享內存的功能實現原理,而是講怎么運用它。

  1. 

  在linux中,不同進程間擁有自己獨立的內存空間,32位操作系統中好像一個進程能用的內存大小是4G吧。而且一般不同進程間不能夠互相使用各自內存的數據。

  當然不同進程間共享數據方法很多,比如之前說的進程間通信binder,socket等等,不過android出了一個共享內存的概念,為的是不同進程間能夠共同操作同一塊內存數據,比如進程1001往一塊共享內存addr里面寫數據“hello world”,進程1009往這塊共享內存addr讀取出“hello world”,也能夠往這塊共享內存addr寫數據“hello china”。這就是共同享用了一塊內存的基本概念了(說白了就是同耕一塊田)。講的夠仔細了吧,如果不清楚評論區見。

  注意:好像binder傳輸的數據實現也是類似於共享內存,讀者可以自行去了解。

  

  2.

  先說一下等會寫程序的思路:

  首先想想代碼編譯出兩個可執行文件后如何操作,打開兩個終端,都進入設備adb shell,第一個終端執行進程a,第二個終端執行進程b。在進程a輸入一串數據后,在進程b中可以讀出這段數據(也能夠改寫這段數據,讀者可以自行添加這部分功能)。

  然后再想想實現的方式,

  進程a:1. 創建共享內存,設置共享內存大小,這時會得到一個fd。2. 獲取共享內存地址。3. 先讀取地址數據,然后往地址寫入數據。4. 把fd通過binder發送給需要使用的進程。

  進程b:1. 通過binder讀取到fd。2. 用fd獲取共享內存地址。3. 讀取共享內存數據,然后往地址寫入數據。

  注意:linux一切皆文件,所以文件描述符fd很重要。  

  

  3. 

  3.1

  捋清思路后,就可以開始寫代碼了(android.mk的編寫可以參考之前的文章),進程a,命名為mysharememory_a代碼如下:

  

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <linux/ashmem.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stddef.h>
#include <linux/ipc.h>
#include <linux/shm.h>

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>

#define DEVASHMEM "/dev/ashmem"
#define SHNAME "hellomemory"
#define MAXBUFSIZE 1024
#define TRANSFDCODE 1000
#define WRITEDATACODE 1001

using namespace android;

int  main(int argc, char *argv[])
{
    int fd = open(DEVASHMEM, O_RDWR);
    if(fd < 0)
    {
        return -1;
    }

    int ret = ioctl(fd, ASHMEM_SET_NAME, SHNAME);
    if(ret < 0){
        close(fd);
        return -1;
    }

    char *get_sh_addr_write = NULL;    
    ret = ioctl(fd, ASHMEM_SET_SIZE, MAXBUFSIZE);
    if(ret < 0){
        close(fd);
        return -1;
    }

    get_sh_addr_write = (char*)mmap(NULL, MAXBUFSIZE , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(NULL == get_sh_addr_write)
    {
        return -1;
    }
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->checkService(String16("mybindertag"));
    Parcel data, reply;

    data.writeDupFileDescriptor(fd);
    binder->transact(TRANSFDCODE, data, &reply);
    
    char input_data[MAXBUFSIZE] = {0};
    while(1)
    {
        printf("read share memory buf is %s\n", get_sh_addr_write);
        printf("please input data to buf :");
        scanf("%s", input_data);
        getchar();

        strcpy(get_sh_addr_write,input_data);
        binder->transact(WRITEDATACODE, data, &reply);
    }
    return ret;
}

  3.2

   mysharememory_b代碼如下:

  

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <linux/ashmem.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stddef.h>
#include <linux/ipc.h>
#include <linux/shm.h>

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>

#define DEVASHMEM "/dev/ashmem"
#define SHNAME "hellomemory"
#define MAXBUFSIZE 1024
#define TRANSFDCODE 1000
#define WRITEDATACODE 1001

using namespace android;

int g_sh_fd = 0;
class MyBinderService : public BBinder{
            status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
            {
                int ret;
                char *get_sh_addr_read = NULL;
                int get_sh_size;
            
                printf("songsong!! **** onTransact ***** code = %d \n",code);
                switch(code)
                {
                    case TRANSFDCODE:
                        g_sh_fd = data.readFileDescriptor();
                        break;

                    case WRITEDATACODE:
                        get_sh_size = ioctl(g_sh_fd, ASHMEM_GET_SIZE,NULL);
                        if(get_sh_size > 0)
                        { 
                            get_sh_addr_read = (char*)mmap(NULL, get_sh_size, PROT_READ | PROT_WRITE, MAP_SHARED, g_sh_fd, 0);
                        }
                        else
                        {
                            printf("mmap failed %d\n", get_sh_size);
                            return -1;     
                        }
                        printf("what is in the share memory: %s\n", get_sh_addr_read);
                        break;
                    default:

                        break;
                }
                return NO_ERROR;
            }
};

int  main(int argc, char *argv[])
{
    defaultServiceManager()->addService(String16("mybindertag"), new MyBinderService());

    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;
}

 

  3.3

  回收關閉部分代碼可選擇添加在mysharememory_b中,如下:

  

    int ret;
    ret = munmap((void*)get_sh_addr_read, get_sh_size);
    if(ret == -1)
    {
        return -1;
    }
    
    ret = close(g_sh_fd);
    if(ret == -1)
    {
        return -1;
    }

 

  

 

  3.4 

  演示截圖:

  

 

 

   4. 為了騙取評論,我不再解釋代碼,心累。不過您可以把代碼直接拷貝去編譯執行,再通過調試去理解代碼的精髓,也是沒問題的。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM