mmap文件返回指向内存中不可访问位置的指针

时间:2013-12-23 19:38:15

标签: c linux 64-bit mmap read-write

我有这个程序应该以读写模式映射文件并能够编辑其内容。此处写的文件大约为40-50 GB,因此我需要mmap64。问题是,虽然mmap64没有返回错误,但它返回的地址无法访问。

#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <unistd.h>

typedef unsigned long long u64;


void access_test(u64 p, u64 sz)
{
    u64 i;
    char tmp;
    for (i=0; i<sz; i++) {
        tmp = *(char*)(p+i);
    }
}

int main(int argc, char *argv[])
{
    int fd;
    long long int sz, p;
    struct stat buf;

    fd = open(argv[1], O_RDWR, 0x0666);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    fstat64(fd, &buf);
    sz = buf.st_size;
    printf("File size: 0x%016llx\n", sz);

    p = mmap64 (0, buf.st_size, PROT_READ | PROT_WRITE , MAP_SHARED, fd, 0);
    if (p == -1) {
        perror ("mmap");
        return 1;
    }

    access_test(p,sz);

    if (close (fd) == -1) {
        perror ("close");
        return 1;
    }


    if (munmap ((void*)p, buf.st_size) == -1) {
        perror ("munmap");
        return 1;
    }

    return 0;
}

结果是一个小文件:

$ ./testmmap minicom.log

File size: 0x0000000000000023
[1]    8282 segmentation fault (core dumped)  ./testmmap minicom.log

同样适合大人物。

1 个答案:

答案 0 :(得分:4)

编译时始终启用警告

以下是启用警告的结果:

$ gcc mmp.c  -Wall -g
mmp.c: In function ‘access_test’:
mmp.c:18:10: warning: variable ‘tmp’ set but not used [-Wunused-but-set-variable]
     char tmp;
          ^
mmp.c: In function ‘main’:
mmp.c:36:5: warning: implicit declaration of function ‘fstat64’ [-Wimplicit-function-declaration]
     fstat64(fd, &buf);
     ^
mmp.c:40:5: warning: implicit declaration of function ‘mmap64’ [-Wimplicit-function-declaration]
     p = mmap64 (0, buf.st_size, PROT_READ | PROT_WRITE , MAP_SHARED, fd, 0);

这里的最后两个警告非常重要。他们说没有mmap64的原型。因此C给你一个默认原型,这是错误的,至少对于mmap64()调用(因为原型将返回一个int,它不能代表64位Linux主机上的指针)

fstat64()的参数也是struct stat64 BTW,这是另一个问题。

使特定的64位函数可用

如果要使fstat64()/mmap64()函数可用,则需要使用_LARGEFILE和LARGEFILE64_SOURCE #define编译代码,请参阅信息here,因此您应该将其编译为例如:

  gcc -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE mmp.c  -Wall -g

或使用#define _FILE_OFFSET_BITS = 64

但是没有必要这样做。编译时,只需调用普通fstat()mmap()以及#define _FILE_OFFSET_BITS=64即可。 e.g:

   gcc -D_FILE_OFFSET_BITS=64 mmp.c  -Wall -g

这将启用对大文件的支持,例如如果需要,将mmap()调用转换为mmap64()(例如,如果你在32位主机上)。

如果你试图mmap()一个50 GB的文件,你无论如何都需要在64位主机上,而在64位Linux主机上则不需要任何这个--mmap()和fstat( )处理大文件而无需任何操作。

使用指针

下一个问题是您将mmap()的返回值赋给整数。这可能会起作用,但代码看起来确实很奇怪。如果您想将该事物视为char *,请将其指定给char *。不要使用将指针转换为64位整数类型来玩弄技巧。

E.g。你的访问功能应该是:

void access_test(char *p, u64 sz)
{
    u64 i;
    char tmp;
    for (i=0; i<sz; i++) {
       tmp = p[i];
    }
}

p应在main()中声明为char *p;,如果您打算将数据视为二进制数据,则使用uint8_t *p;