在共享内存中复制到struct时出现“总线错误”

时间:2017-04-25 11:45:42

标签: c struct

我的“主要”功能中有这段代码。

...
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 *,并在strcpy的原始部分放置一个char *但我仍然得到相同的错误。 printf打印正确的“消息”,这不应该是问题...

任何帮助都是相关的。

提前致谢!

1 个答案:

答案 0 :(得分:2)

  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. */
    }
    

    请注意,我假设msg结构中的dados成员是一个char数组。如果它是指向char的指针,它将无法工作。 (它将指向一些随机位置。即使您将其设置为指向此进程中的共享内存对象 ,它也会指向其他进程中的错误地址,因为映射基本上是在随机地址。)

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