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

标签 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

大的也是一样。

最佳答案

编译时始终启用警告

这是启用警告后的结果:

$ 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没有原型(prototype)。因此,C 为您提供了一个默认原型(prototype),但它是错误的,至少对于 mmap64() 调用而言(因为原型(prototype)将返回一个 int,它不能表示 64 位 Linux 主机上的指针)

fstat64() 的参数也是一个 struct stat64 顺便说一句,这是另一个问题。

使特定的 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即可。例如:

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

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

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

使用指针

下一个问题是您要将 mmap() 的返回值分配给一个整数。这可能碰巧有效,但代码确实因此看起来很奇怪。如果您想将事物视为 char *,请将其分配给 char *。不要把指针强制转换为 64 位整数类型。

例如你的访问函数应该是:

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;数据作为二进制数据。

关于c - mmap 文件返回指向内存中不可访问位置的指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20750066/

相关文章:

linux - 64 位 Linux 上的缓冲区溢出

c - 在 *.txt 文件中搜索并更新 1 行 - C 语言

c - C语言的socket编程问题

android - NDK 构建失败 cocos2d-x

Ubuntu : dpkg --add-architecture i386 throwing error --add-architecture unknown option

windows - 在 Windows 64 位上将 libusb 与 Delphi 一起使用?

c - echo()函数在这个程序中起什么作用?

c - printf:没有为格式字符串传递足够的参数

linux - sqlplus 不工作

c - Linux可加载模块精确定时