在Android中使用共享内存
Charles Chan @ 2022-01-09 #SHM @Android
Contents:
如果去网络上搜索如何在Android系统中使用共享内存,绝大部分文章都会提到下面的做法。
#define __ASHMEMIOC 0x77
#define ASHMEM_NAME_LEN 256
#define ASHMEM_SET_NAME (_IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN]))
#define ASHMEM_SET_SIZE (_IOW(__ASHMEMIOC, 3, size_t))
int ashm_open(const char *_name, size_t _size)
{
int fd = open("/dev/ashmem", O_RDWR);
if(fd < 0) {
a::cerr << "could not open /dev/ashmem";
}
else {
ioctl(fd, ASHMEM_SET_NAME, _name);
ioctl(fd, ASHMEM_SET_SIZE, _size);
}
return fd;
}
这个函数基本上可以起到Linux中的shm_open
的作用。区别是需要给出共享内存的预期大小。
但是,实际上,事情远没有这么简单。在Android系统中,那个ASHMEM_SET_NAME
的作用并不等同于Linux系统中shm_open
的第一个参数。
在Android系统中,即便在不同的进程中使用同样的ASHMEM_SET_NAME
,打开的也不是相同的共享内存空间。两者根本无法互通。
如果要互通,就需要第一个调用ashm_open
的进程将fd传给其他进程。然后其他进程使用这个fd来直接操作。也就是用于直接调用mmap
。
将fd从一个进程共享给另一个进程的方法可能不止一个。下面这个是我使用的方案。也就是利用Domain Socket。
具体创建Domain Socket的过程就不展开了。主要讲收发:
发送fd的方法如下:
static int send_fd(int _sock, int _fd)
{
struct iovec iov;
char buffer[1];
buffer[0] = 0;
iov.iov_base = buffer;
iov.iov_len = 1;
struct msghdr msg;
struct cmsghdr* cmsg;
char cms[CMSG_SPACE(sizeof(int))];
::memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = reinterpret_cast<caddr_t>(cms);
msg.msg_controllen = CMSG_LEN(sizeof(int));
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
::memcpy(CMSG_DATA(cmsg), &_fd, sizeof(int));
int ret = sendmsg(_sock, &msg, 0);
if (ret > 0 && ret != iov.iov_len) {
errno = EIO;
ret = -1;
}
return ret;
}
接收fd的进程类似如下:
char buffer[1];
struct iovec iov;
iov.iov_base = buffer;
iov.iov_len = 1;
struct msghdr msg;
struct cmsghdr* cmsg;
char cms[CMSG_SPACE(sizeof(int))];
::memset(&msg, 0, sizeof msg);
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = reinterpret_cast<caddr_t>(cms);
msg.msg_controllen = sizeof(cms);
int ret = recvmsg(sk_fd, &msg, 0);
if (ret < 0)
continue;
if (ret == 0) {
errno = EIO;
continue;
}
cmsg = CMSG_FIRSTHDR(&msg);
::memcpy(&shm_fd, CMSG_DATA(cmsg), sizeof(int));
shm_fd
就是获得的fd。