在android源碼的驅動目錄下,一般會有共用記憶體的相關實現源碼,目錄是:kernel\drivers\staging\android\ashmem.c。但是本篇文章不是講解android共用記憶體的功能實現原理,而是講怎麼運用它。 1. 在linux中,不同進程間擁有自己獨立的記憶體空間,32位操作系 ...
在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. 為了騙取評論,我不再解釋代碼,心累。不過您可以把代碼直接拷貝去編譯執行,再通過調試去理解代碼的精髓,也是沒問題的。