【问题标题】:Writing to ashmem / why does android free ashmem?写信给 ashmem / 为什么 android 会释放 ashmem?
【发布时间】:2015-03-06 09:54:54
【问题描述】:

我想在两个 (ndk-) 进程之间共享数据。为此,我使用 source 使用 ashmem。
一个进程持续读取(read_mem),一个进程写入一次(write_mem)。

问题是读取过程没有获取写入器的值。

通过查看阅读器的地图我发现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(按回车键)

看读者的地图:

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-buildcommand 编译为可执行文件
设备:LG Nexus 4 (AOSP Lollypop)
我检查了/dev/ashmem 它存在。
ashmem 取自 here

【问题讨论】:

  • 地图还在,只是在标签中显示“(已删除)”?
  • 我仍然能够读写缓冲区是的。但似乎数据不会被写入。 msync 没有区别。

标签: android android-ndk shared-memory ashmem


【解决方案1】:

Ashmem 不像 Linux 上的常规共享内存那样工作,这是有充分理由的。

首先,我们来解释一下“(deleted)”部分,这是内核中如何实现ashmem的一个实现细节。真正的意思是在/dev/ashmem/目录下创建了一个fileentry,然后removed,但是对应的i -node 仍然存在,因为它至少有一个打开的文件描述符。

您实际上可以创建多个具有相同名称的 ashmem 区域,它们都将显示为“/dev/ashmem/ (deleted)”,但它们中的每一个都对应一个不同的 i-node,并且因此是一个不同的 记忆 区域。如果您在 /dev/ashmem/ 下查看,您会看到该目录仍然是空的。

这就是为什么 ashmem 区域的名称实际上只用于调试。无法通过名称“打开”现有区域。

当最后一个文件描述符关闭时,ashmem i-node 和相应的内存会自动回收。这很有用,因为这意味着如果您的进程因崩溃而死,内核将自动回收内存。常规 SysV 共享内存并非如此(崩溃的进程只会泄漏内存!在 Android 等嵌入式系统上是不可接受的)。

您的测试程序创建了两个不同的同名 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

瞧,希望这会有所帮助

【讨论】:

  • 但是为什么输出中的fds是一样的呢?
  • 文件描述符编号是特定于进程的。默认情况下,命令行可执行文件将打开 3 个文件描述符 (0=stdin/1=stdout/2=stderr),因此第一个 open() 调用可能会返回 3。
  • 抱歉,疯狂的链接器源似乎已被移动,这里是同一源的先前提交的链接,其中包含有效的 SendFd() 和 ReceiveFd() 函数:android.googlesource.com/platform/ndk/+/android-5.0.0_r7/…
  • @Digit 您能否指出在创建 ashmem 区域时文件条目添加和后续删除的确切位置?
  • 赞成,因为它很好地描述了内部,尽管这里有一个错误。没有/dev/ashmem/文件夹,有/dev/ashmem设备文件,/proc/&lt;pid&gt;/maps中有/dev/ashmem/&lt;name&gt; (deleted)条目,只是名称。
猜你喜欢
  • 2017-06-23
  • 2012-04-15
  • 1970-01-01
  • 2016-03-20
  • 2023-04-06
  • 2013-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多