Android native程式間通訊例項-binder結合共享記憶體

啊源股發表於2019-08-01

  在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. 為了騙取評論,我不再解釋程式碼,心累。不過您可以把程式碼直接拷貝去編譯執行,再通過除錯去理解程式碼的精髓,也是沒問題的。

  

相關文章