在 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. 为了骗取评论, 我不再解释代码, 心累. 不过您可以把代码直接拷贝去编译执行, 再通过调试去理解代码的精髓, 也是没问题的.
来源: https://www.cnblogs.com/songsongman/p/11249974.html