使用POSIX共享内存时使用信号量出现分段错误

4
我正在编写一些简单的代码,以了解信号量和POSIX共享内存。但是我遇到了一些问题。
我的想法是,一个程序(服务器)打开共享内存并向其写入一个结构体(其中包含一个信号量和一个数组)。然后它等待输入,并在输入后增加该信号量。
同时,客户端打开共享内存,在信号量上等待,在服务器增加信号量后读取结构。
看起来服务器工作正常,但是我在客户端的sem_wait函数处遇到了分段错误,甚至在服务器增加信号量之前就发生了。我想不出什么错了。
服务器代码:
#define _XOPEN_SOURCE 500

#include <stdio.h>

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>
#include <sys/types.h>

#include <semaphore.h>

#include <stdbool.h>

#define ARRAY_MAX 1024

typedef struct {
    sem_t inDataReady;
    float array[ARRAY_MAX];
    unsigned arrayLen;
} OsInputData;

int main() {

    int shm_fd;
    OsInputData *shm_ptr;

    if((shm_fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666)) == -1) {
        printf("shm_open failure\n");
        return 1;
    }

    if(ftruncate(shm_fd, sizeof(OsInputData)) == -1) {
        printf("ftruncate failure\n");
        return 1;
    }

    if((shm_ptr = (OsInputData*)mmap(0, sizeof(OsInputData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
        printf("mmap failure\n");
        return 1;
    }

    sem_init(&(shm_ptr->inDataReady), true, 0);

    shm_ptr->array[0] = 3.0;
    shm_ptr->array[1] = 1.0;
    shm_ptr->array[2] = 2.0;
    shm_ptr->array[3] = 5.0;
    shm_ptr->array[4] = 4.0;

    shm_ptr->arrayLen = 5;

    getchar();
    sem_post(&(shm_ptr->inDataReady));

    sem_destroy(&(shm_ptr->inDataReady));

    munmap(shm_ptr, sizeof(OsInputData));
    close(shm_fd);

    return 0;
}

客户端代码:

#define _XOPEN_SOURCE 500

#include <stdio.h>

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>
#include <sys/types.h>

#include <semaphore.h>

#include <stdbool.h>

#define ARRAY_MAX 1024

typedef struct {
    sem_t inDataReady;
    float array[ARRAY_MAX];
    unsigned arrayLen;
} OsInputData;

int main() {

    int shm_fd;
    OsInputData *shm_ptr;

    if((shm_fd = shm_open("/my_shm", O_RDONLY, 0666)) == -1) {
        printf("shm_open failure\n");
        return 1;
    }

    if((shm_ptr = (OsInputData*)mmap(0, sizeof(OsInputData), PROT_READ, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
        printf("mmap failure\n");
        return 1;
    }

    sem_wait(&(shm_ptr->inDataReady));

    printf("%u\n", shm_ptr->arrayLen);

    munmap(shm_ptr, sizeof(OsInputData));
    close(shm_fd);

    return 0;
}
2个回答

4

实际结果取决于您的系统,但通常情况下,您的程序包含一个错误。您不能销毁可以被其他进程/线程访问的信号量。仅仅因为您已经执行了sem_post并不意味着您的系统已经切换到等待它的进程。当您销毁时,另一个人可能仍在使用它。

在这种情况下,SIGSEGV是一种好事。很少有程序员检查sem_wait的返回值,这会导致程序认为它们已同步,但实际上并没有。


如果是这种情况,我该如何解决呢?虽然我不确定是否是这种情况,因为1)getchar应该会阻止服务器继续运行(并销毁信号量),而客户端段错误发生在此之前;2)注释掉sem_destroy行对解决问题没有任何帮助。我还在sem_initsem_postsem_wait函数中添加了错误检查,但它们没有返回错误。 - Luka Aleksić
同步对象应遵循与RAII(资源获取即初始化)类似的模式,该模式跟踪对对象的未完成引用数量。要销毁该对象,必须首先使其无法访问,然后当其引用计数达到零时,才能安全地销毁诸如同步变量之类的东西。未来会感谢你的。 - mevets

1
原来我在客户端中打开共享内存时需要同时具有读写权限,并相应地在中更新保护。这是一个相当愚蠢的错误,因为很明显客户端还需要写入权限才能实际修改信号量。因此,在客户端代码中进行以下更改即可解决问题。
...
shm_fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0755)
...
shm_ptr = (OsInputData*)mmap(0, sizeof(OsInputData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0)
...

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