检查mmap的地址是否正确

5
我正在编写一个高负载守护进程,应在FreeBSD 8.0和Linux上运行。 守护进程的主要目的是通过请求其标识符的文件来传输文件。通过对数据库发出请求,将标识符转换为本地文件名/文件大小。然后我使用连续的mmap()调用来传递带有send()的文件块。
但是有时候数据库中的文件大小与文件系统中的文件大小不匹配(realsize < size in db)。在这种情况下,我已经发送了所有真实数据块,并且当映射下一个数据块时 - mmap没有返回错误,只是常规地址(我也检查了errno变量,它在mmap之后等于零)。当守护程序尝试发送此块时,它会收到Segmentation Fault。(此行为保证在FreeBSD 8.0 amd64上发生)
我在打开文件之前使用安全检查来确保文件大小stat()调用。 然而,现实生活向我展示segfault仍然可能在罕见的情况下引发。
因此,我的问题是是否有一种方法可以在引用指针之前检查指针是否可访问?当我在gdb中打开core时,gdb会说给定的地址超出范围。可能还有其他解决方案可以提出。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define FILENAME        "./datafile"

int main()
{
    unsigned long i, j;

    srand(time(NULL));
    unsigned long pagesize = sysconf(_SC_PAGESIZE);

    unsigned long basesize = 4 * pagesize;
    unsigned long cropsize = 2 * pagesize;

    // create 4*pagesize sized file
    int f = creat(FILENAME, 0644);
    for (i = 0; i < basesize; i++) {
        unsigned char c = (unsigned char)rand();
        if (write(f, &c, 1) < 1) { perror("write"); break; }
    }
    close(f);

    f = open(FILENAME, O_RDONLY);

    // walk trough file
    unsigned char xor = 0;
    unsigned long offset = 0;
    for (j = 0; j < 4; j++) {
        // trunc file to 2*pagesize
        if (j == 2) truncate(FILENAME, cropsize);

        char *data = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE, f, offset);
        if (data == MAP_FAILED) { perror("mmap"); break; }
        printf("mmap: %lu@%lu for %i\n", pagesize, offset, f);

        for (i = 0; i < pagesize; i++) xor ^= data[i];

        offset += pagesize;
    }

    close(f);

    return 0;
}
1个回答

2
当然,我无法从这里证明,但我强烈怀疑你的代码中只是一个簿记错误。如果您调用mmap并传入大小,并且成功,则不应收到SIGSEGV信号。
我建议您使用valgrind进行调查。
在许多Linux系统上,/proc/PID/maps将显示哪些区域映射了什么访问权限。

我在主贴中放置了代码示例,说明了问题。这段代码模拟了文件的创建,并在使用mmap进行处理时调整文件大小。在Linux系统上,第三步出现总线错误(Bus Error),在FreeBSD上则是段错误(SegFault)。 - reddot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接