【发布时间】:2012-10-27 13:35:22
【问题描述】:
我编写了一个简单的 Android 原生函数,它获取文件名和更多参数,并通过映射 (mmap) 它的内存来读取文件。
因为是mmap,所以我真的不需要调用“read()”,所以我只是从mmap()返回的地址中memcpy()。
但是,我在某个地方得到了 SIGSEGV,可能是因为我试图访问我不允许的内存。但是我不明白为什么,我已经要求映射所有文件的内存!
我附上了我的代码和我得到的错误:
编辑
我修复了终止循环,但在读取 25001984 个字节后仍然得到 SIGSEGV。 该函数适用于这些参数: jn_bytes = 100,000,000 jbuffer_size = 8192 jshared=jpopulate=jadvice=0
void Java_com_def_benchmark_Benchmark_testMmapRead(JNIEnv* env, jobject javaThis,
jstring jfile_name, unsigned int jn_bytes, unsigned int jbuffer_size, jboolean jshared, jboolean jpopulate, jint jadvice) {
const char *file_name = env->GetStringUTFChars(jfile_name, 0);
/* *** start count *** */
int fd = open(file_name, O_RDONLY);
//get the size of the file
size_t length = lseek(fd, 0L, SEEK_END);
lseek(fd, 0L, SEEK_SET);
length = length>jn_bytes?jn_bytes:length;
// man 2 mmap: MAP_POPULATE is only supported for private mappings since Linux 2.6.23
int flags = 0;
if (jshared) flags |= MAP_SHARED; else flags |= MAP_PRIVATE;
if(jpopulate) flags |= MAP_POPULATE;
//int flags = MAP_PRIVATE;
int * addr = reinterpret_cast<int *>(mmap(NULL, length , PROT_READ, flags , fd, 0));
if (addr == MAP_FAILED) {
__android_log_write(ANDROID_LOG_ERROR, "NDK_FOO_TAG", strerror(errno));
return;
}
int * initaddr = addr;
if(jadvice > 0)
madvise(addr,length,jadvice==1?(MADV_SEQUENTIAL|MADV_WILLNEED):(MADV_DONTNEED));
close(fd);
char buffer[jbuffer_size];
void *ret_val = buffer;
int read_length = length;
while(ret_val == buffer || read_length<jbuffer_size) {
/*****GETTING SIGSEGV SOMWHERE HERE IN THE WHILE************/
ret_val = memcpy(buffer, addr,jbuffer_size);
addr+=jbuffer_size;
read_length -= jbuffer_size;
}
munmap(initaddr,length);
/* stop count */
env->ReleaseStringUTFChars(jfile_name, file_name);
}
和错误日志:
15736^done
(gdb)
15737 info signal SIGSEGV
&"info signal SIGSEGV\n"
~"Signal Stop\tPrint\tPass to program\tDescription\n"
~"SIGSEGV Yes\tYes\tYes\t\tSegmentation fault\n"
15737^done
(gdb)
15738-stack-list-arguments 0 0 0
15738^done,stack-args=[frame={level="0",args=[]}]
(gdb)
15739-stack-list-locals 0
15739^done,locals=[]
(gdb)
【问题讨论】:
-
文件有多大?系统可以在这么大的堆栈上创建 VLA 吗?使用
malloc()而不是char buffer[jbuffer_size];,但不要忘记也释放它。 -
我要求 mmap 的长度为 100MB,我不明白为什么数组分配方法(堆栈/堆)会解决这个问题,因为几个第一个 memcpy 工作得很好(在调试时)但是之后(当我删除断点并让它运行时)我得到了 SIGSEGV。意味着我到达了一些我不应该访问的地址?
-
在我工作的机器上,我可以通过 VLA 分配的内容肯定有上限,而且我认为我不会冒险尝试 100 MiB 范围内的 VLA — 至少,我会确保它可靠地工作。动态内存分配(通过
malloc()等)是另一回事,尽管即使在那里你也必须担心系统不会报告过度使用内存的错误,直到你去访问他们所说的可用但不是什么时候时候到了。 -
现在我使用关于 read_length 的报告运行它,所以我看到我最后一次获得有关 read_length 的日志时始终为 74998016,这意味着如果我将它从 100000000(这是原始长度)减少) 我得到 25001984 (~ 24 MB) 这意味着我已经阅读了 ~ 24 MB 直到 SIGSEGV。它有帮助吗?我在 Android 2.3 上运行。关于你提到的 VLA,如果 ~100MB 太多了 - mmap 应该为我的请求返回一些错误,不是吗?如果您谈论我的缓冲区 - size=8196,为什么 memcpy 第一次成功?
-
VLA 是
buffer,与mmap()无关。我不是安卓开发者;我无法回答在任何版本的 Android 上哪些功能有效或无效。如果我在调试这个问题,我会去测试 VLA 是否在大尺寸下工作。例如,我可以分配一个 1 MiB、2 MiB、4 MiB、... 128 MiB VLA,然后安全地使用它(memset()可以测试)。如果所有这些都很好,那么我对你的问题的猜测是错误的,你可以忽略我。如果其中任何一个失败了,那么也许我的问题是正确的。然后你会怎么做取决于你发现了什么。
标签: android c++ segmentation-fault mmap memcpy