【问题标题】:Getting "bus error" when copying to struct in shared memory复制到共享内存中的结构时出现“总线错误”
【发布时间】:2017-09-22 09:58:01
【问题描述】:

我的“main”函数中有这段代码。

...
int data_size1 = sizeof(dados);

int fdDados = shm_open("/dados", O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if (fdDados<0){
    perror("shm_open failed");
    exit(1);
}

dados* shared_data_dados=(dados*) mmap(NULL, data_size1, PROT_READ|PROT_WRITE, MAP_SHARED, fdDados, 0); 
time_t rawtime;
struct tm * timeinfo;
time (&rawtime);
timeinfo = localtime (&rawtime);
char auxMsg[91];
sprintf(auxMsg, "Pid=%d, time=%s", getpid(), asctime(timeinfo));
printf(auxMsg);
strcpy((shared_data_dados->msg), auxMsg);
...

它包括以下.h:

typedef struct {
    char msg[101];
} dados;

问题是,当我对结构执行 strcpy 时,它给了我一个总线错误。我尝试将结构更改为 char*,并将 char* 放在 strcpy 的原始部分,但我仍然遇到相同的错误。 printf 打印正确的“消息”,所以这不应该是问题......

感谢任何帮助。

提前致谢!

【问题讨论】:

  • 仅仅因为 printf 有效,并不意味着您没有跑出缓冲区的末尾 - print strlen(auxMsg) 也是如此。尝试将 shmopen 的文件名更改为“./dados”之类的名称。 shmopen 创建一个文件描述符 - 如果您无权访问 /,它可能无法工作
  • 我已经尝试了这两个建议,但都没有奏效。 strlen 的 printf 按预期打印“40”。

标签: c struct


【解决方案1】:
  1. 您需要调用ftruncate(fdDados, SIZE) 来定义共享内存对象的大小(SIZE),如man 3 shm_open 手册页中所述。

    默认情况下,共享内存对象的大小为零字节。因为您的映射不受共享内存对象的支持,所以对映射的所有访问都会导致生成SIGBUS 信号。

    这与您对文件进行内存映射,然后尝试访问文件末尾之后的映射时的情况完全相同。
     

  2. mmap()调用中,第二个参数,映射的长度,应该是页面大小的倍数。

    您可以通过调用sysconf(_SC_PAGESIZE)获取页面大小。不要依赖魔术常量或预处理器宏。
     

  3. 由于上面的(1)和(2),最好通过将您需要的共享内存量四舍五入到页面大小的下一个倍数来计算SIZE。例如:

    static size_t full_pages(const size_t size)
    {
        size_t page = sysconf(_SC_PAGESIZE);
        if (size < page)
            return page;
        else
        if (size % page)
            return size + page - (size % page);
        else
            return size;
    }
    

    在您的代码中,

    int     shared_fd, len;
    size_t  shared_size;
    dados  *shared_dados;
    
    shared_size = full_pages(sizeof *shared_dados);
    shared_fd = shm_open("/dados", O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    if (shared_fd == -1) {
        fprintf(stderr, "Cannot create shared object '/dados': %s.\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    if (ftruncate(shared_fd, (off_t)shared_size) == -1) {
        fprintf(stderr, "Cannot resize shared object '/dados': %s.\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    shared_dados = mmap(NULL, shared_size, PROT_READ | PROT_WRITE,
                        MAP_SHARED, shared_fd, 0);
    if (shared_dados == MAP_FAILED) {
        fprintf(stderr, "Cannot map shared object '/dados': %s.\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    if (close(shared_fd) == -1) {
        fprintf(stderr, "Error closing shared object '/dados': %s.\n", strerror(errno));
        exit(EXIT_FAILURE);
    } else
        shared_fd = -1;
    
    /* TODO: LOCKING! */
    
    memset(shared_dados->msg, '\0', sizeof shared_dados->msg);
    len = snprintf(shared_dados->msg, sizeof shared_dados->msg, "pid=%ld", (long)getpid());
    if (len < 0 || len >= (int)sizeof shared_dados->msg) {
        /* The string we tried to print was too long to
           fit into the shared_dados->msg field. Oops. */
    }
    

    请注意,我假设dados 结构中的msg 成员是一个字符数组。如果它是指向 char 的指针,它将不起作用。 (它会指向某个随机位置。即使你将它设置为指向共享内存对象内在这个进程中,它也会在其他进程中指向一个错误的地址,因为映射本质上是在随机地址。)

    还要注意TODO: LOCKING! 评论。为确保其他进程只会看到完整的消息,您应该使用例如访问 msg 成员的互斥锁。

【讨论】:

  • 非常感谢!这正是问题所在,缺少 ftruncate。
猜你喜欢
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多