我想在两个(ndk-)进程之间共享数据。为此,我使用source使用ashmem。
一个进程正在连续读取(read_mem
),一个进程正在写入一次(write_mem
)。
问题在于读取过程未获取写入器的值。
和
通过查看读者的 map ,我发现android删除了ashmem_create_region
之后的共享内存文件。
read_mem.c
// read_mem.c
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include "ashmem.h"
#define SHM_NAME "test_mem"
int main(int argc, char **argv) {
int shID = ashmem_create_region(SHM_NAME, 2);
if (shID < 0)
{
perror("ashmem_create_region failed\n");
return 1;
}
// right here /dev/ashmem/test_mem is deleted
printf("ashmem_create_region: %d\n", shID);
char *sh_buffer = (char*)mmap(NULL, 2, PROT_READ | PROT_WRITE, MAP_SHARED, shID, 0);
if (sh_buffer == (char*)-1)
{
perror("mmap failed");
return 1;
}
printf("PID=%d", getpid());
do
{
printf("VALUE = 0x%x\n", sh_buffer[0]);
}
while (getchar());
return 0;
}
write_mem.c
// write_mem.c
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include "ashmem.h"
#define SHM_NAME "test_mem"
int main(int argc, char **argv) {
int shID = ashmem_create_region(SHM_NAME, 2);
if (shID < 0)
{
perror("ashmem_create_region failed\n");
return 1;
}
printf("ashmem_create_region: %d\n", shID);
char *sh_buffer = (char*)mmap(NULL, 2, PROT_READ | PROT_WRITE, MAP_SHARED, shID, 0);
if (sh_buffer == (char*)-1)
{
perror("mmap failed");
return 1;
}
printf("PID=%d\n", getpid());
int ch = getchar();
sh_buffer[0] = ch;
printf("Written 0x%x\n", ch);
munmap(sh_buffer, 2);
close(shID);
return 0;
}
这是输出:
读
130|shell@mako:/data/local/tmp $ ./read_mem
ashmem_create_region: 3
PID=29655
VALUE = 0x0
写作
shell@mako:/data/local/tmp $ ./write_mem
ashmem_create_region: 3
PID=29691
A
Written 0x41
再次读取
VALUE = 0x0
(按回车键)观看读者的 map :
shell@mako:/ $ cat /proc/29655/maps | grep test_mem
b6ef5000-b6ef6000 rw-s 00000000 00:04 116213 /dev/ashmem/test_mem (deleted)
如您所见,
test_mem
已删除,而 read_mem
仍然有效。其他资讯
使用android
ndk-build
命令将这两个文件编译为可执行文件装置:LG Nexus 4(AOSP Lollypop)
我检查了
/dev/ashmem
是否存在。摘自here的烟灰缸
最佳答案
Ashmem不能像Linux上的常规共享内存那样工作,这是有充分理由的。
首先,让我们尝试解释“(已删除)”部分,这是有关如何在内核中实现ashmem的实现细节。真正的含义是在/dev/ashmem/目录中创建了一个文件条目,然后将其删除,但是由于至少有一个打开的文件描述符,所以相应的 inode 仍然存在。
实际上,您可以创建几个具有相同名称的ashmem区域,它们都将显示为“/dev/ashmem/(已删除)”,但是每个区域都对应于一个不同的i节点,因此一个不同的i节点内存区域。并且,如果您在/dev/ashmem/下查看,该目录仍然为空。
这就是为什么ashmem区域的名称实际上仅用于调试的原因。无法通过名称“打开”现有区域。
当关闭最后一个文件描述符时,会自动回收一个ashmem inode 和相应的内存。这很有用,因为这意味着如果您的进程由于崩溃而死,内核将自动回收内存。常规SysV共享内存不是这种情况(崩溃进程只会泄漏内存!在诸如Android之类的嵌入式系统上,这是 Not Acceptable )。
您的测试程序会创建两个不同的具有相同名称的Ashmem区域,这就是为什么它们无法按您认为的那样工作的原因。相反,您需要的是:
1)在其中一个过程中创建一个Ashmem区域。
2)将一个新的文件描述符传递给从第一个进程到第二个进程的区域。
一种方法是派生第一个进程来创建第二个进程(这将自动复制文件描述符),但这在Android上通常不是一个好主意。
更好的选择是使用sendmsg()和recvmsg()通过两个进程之间的Unix域套接字发送文件描述符。这通常很棘手,但是作为示例,请看以下为NDK编写的源文件中的SendFd()和ReceiveFd()函数:
https://android.googlesource.com/platform/ndk/+/android-5.0.0_r7/sources/android/crazy_linker/tests/test_util.h
瞧,希望这对您有所帮助