C 从mmap ed内存的有效读取会在负载下产生SIGBUS。为什么?

C 从mmap ed内存的有效读取会在负载下产生SIGBUS。为什么?,c,linux,openmp,mmap,sigbus,C,Linux,Openmp,Mmap,Sigbus,我有一个程序,可以将缓冲区复制到文件中,mmap将其返回,然后检查其内容。多个线程可以在同一个文件上工作。偶尔,我在阅读时会收到SIGBUS信号,但只是在负载下 映射是MAP_PRIVATE和MAP_POPULATE。通过SIGBUS的崩溃发生在mmap成功之后,我不理解,因为使用了MAP_填充 下面是一个完整的示例(使用OpenMP在/tmp/buf_*下创建文件,并用零填充),以创建更多的加载和并发写入: // Program to check for unexpected SIGBUS /

我有一个程序,可以将缓冲区复制到文件中,mmap将其返回,然后检查其内容。多个线程可以在同一个文件上工作。偶尔,我在阅读时会收到SIGBUS信号,但只是在负载下

映射是MAP_PRIVATE和MAP_POPULATE。通过SIGBUS的崩溃发生在mmap成功之后,我不理解,因为使用了MAP_填充

下面是一个完整的示例(使用OpenMP在/tmp/buf_*下创建文件,并用零填充),以创建更多的加载和并发写入:

// Program to check for unexpected SIGBUS
// gcc -std=c99 -fopenmp -g -O3 -o mmap_manymany mmap_manymany.c
#include <assert.h>
#include <errno.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>

#define NBUFS 64
const char bufs[NBUFS][65536] = {{0}};
const char zeros[65536] = {0};

int main()
{
  int count = 0;
  while ( 1 )
  {
    void *mappings[ 1000 ] = {NULL};

#pragma omp parallel for
    for ( int i = 0; i < 1000; ++i )
    {
      // Prepare filename
      int bufIdx = i % NBUFS;
      char path[ 128 ] = { 0 };
      sprintf( path, "/tmp/buf_%0d", bufIdx );

      // Write full buffer
      int outFd = -1;
#pragma omp critical
      {
        remove( path );
        outFd = open( path, O_EXCL | O_CREAT | O_WRONLY | O_TRUNC, 0644 );
      }
      assert( outFd != -1 );
      ssize_t size = write( outFd, bufs[bufIdx], 65536 );
      assert( size == 65536 );
      close( outFd );

      // Map it to memory
      int inFd = open( path, O_RDONLY );
      if ( inFd == -1 )
        continue; // Deleted by other thread. Nevermind

      mappings[i] = mmap( NULL, 65536, PROT_READ, MAP_PRIVATE | MAP_POPULATE, inFd, 0 );
      assert( mappings[i] != MAP_FAILED );
      close( inFd );

      // Read data immediately. Creates occasional SIGBUS but only under load.
      int v = memcmp( mappings[i], zeros, 65536 );
      assert( v == 0 );
    }

    // Clean up
    for ( int i = 0; i < 1000; ++i )
      munmap( mappings[ i ], 65536 );
    printf( "count: %d\n", ++count );
  }
}
//检查意外SIGBUS的程序
//gcc-std=c99-fopenmp-g-O3-o mmap_manymany mmap_manymany.c
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义NBUFS 64
const char bufs[NBUFS][65536]={{0};
常量字符零[65536]={0};
int main()
{
整数计数=0;
而(1)
{
void*映射[1000]={NULL};
#pragma-omp并行
对于(int i=0;i<1000;++i)
{
//准备文件名
int bufIdx=i%NBUFS;
字符路径[128]={0};
sprintf(路径,“/tmp/buf_u0d”,bufIdx);
//写满缓冲区
int outpd=-1;
#pragma-omp-critical
{
移除(路径);
outpd=开放(路径,O|u EXCL | O|u CREAT | O|u WRONLY | O|u TRUNC,0644);
}
断言(outpd!=-1);
ssize_t size=write(outpd,bufs[bufIdx],65536);
断言(大小=65536);
关闭(出口);
//将其映射到内存
int inFd=打开(仅路径);
如果(inFd==-1)
继续;//已被其他线程删除。无需终止
映射[i]=mmap(NULL,65536,PROT_READ,MAP_PRIVATE,MAP_POPULATE,inFd,0);
断言(映射[i]!=MAP\u失败);
关闭(inFd);
//立即读取数据。创建偶尔的SIGBUS,但仅在负载下。
int v=memcmp(映射[i],零,65536);
断言(v==0);
}
//清理
对于(int i=0;i<1000;++i)
munmap(映射[i],65536);
printf(“计数:%d\n”++计数);
}
}

没有为我触发assert,但使用SIGBUS的程序总是在几秒钟后崩溃。

对于当前程序,线程0可能会创建
/tmp/buf_0
,写入并关闭它。然后线程1删除并创建
/tmp/buf_0
,但在线程1写入它之前,线程0打开、映射并读取
/tmp/buf_0
,因此尝试访问尚未包含64 kiB数据的文件。您将获得一个
SIGBUS


为了避免这个问题,只需为每个线程创建唯一的文件/和
bufs
,方法是使用
omp\u get\u thread\u num()
而不是
bufIdx

可能与未应答相关,但我没有使用共享映射。崩溃发生在哪里?在
mmap
调用之后,可能会有一些语句和表达式。请使用调试器缩小范围。你试过没有OpenMP吗?那么它能工作吗?它直接在memcmp调用中崩溃。内核从我在perf中发现的do_page_fault发送SIGBUS,如下所示:sudo perf record-g-e signal:signal_generate./mmap_many如果没有OpenMP,它是不会发生的。是的!这个解释很有道理。我不能以不同的方式命名我的文件,但我可以使用
O_RDWR
打开它们,并直接在我的
mmap
中使用该文件描述符。这避免了您描述的竞争。我仍然不清楚:示例代码在
memcmp
中失败。为什么不在
mmap
内,因为
MAP\u POPULATE
?出于某种原因,Linux忽略了填充内存页时的错误。。。