1

我编写了一个简单的 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) 
4

4 回答 4

3

这里有一个大问题:

    addr+=jbuffer_size;

您按字节增加,而您只想按字节增加addr它。sizeof(int) * jbuffer_sizejbuffer_size

我的猜测是sizeof(int)你的系统上的 4,因此你在循环的大约 25% 处崩溃,因为你addr在每次迭代中增加了 4 倍太多。

于 2012-11-07T17:08:34.780 回答
1

中的条件while(ret_val == buffer || read_length<jbuffer_size)是错误的。ret_val == buffer将始终为真,如果read_length<jbuffer_size在到达循环时为真,它将始终保持read_length为真,因为只会减少(嗯,直到它下溢 INT_MIN)。

于 2012-11-07T14:58:53.563 回答
1

这个循环永远不会终止,因为ret_val总是等于buffer

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;
}

memcpy总是返回它的第一个参数,所以ret_val永远不会改变。

于 2012-11-07T14:55:22.613 回答
1

while循环是无限的:

while(ret_val == buffer || read_length<jbuffer_size) {
    ret_val = memcpy(buffer, addr,jbuffer_size);
    addr+=jbuffer_size;
    read_length -= jbuffer_size;
}

一如既往memcpy()地返回目的地缓冲区,所以ret_val == buffer总是如此true(因此作为终止条件的一部分是无用的)。这意味着在循环的每次迭代中都会addr增加jbuffer_size字节并传递给 memcpy(),从而导致访问无效内存。

于 2012-11-07T14:56:04.163 回答