【问题标题】:Segmentation fault when using semaphores with POSIX shared memory将信号量与 POSIX 共享内存一起使用时出现分段错误
【发布时间】:2026-02-12 08:00:02
【问题描述】:

我正在编写一些简单的代码来自学有关信号量和 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;
}

【问题讨论】:

    标签: c posix semaphore shared-memory


    【解决方案1】:

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

    在这种情况下,SIGSEGV 是一种善意。很少有程序员会检查 sem_wait 的返回值,这可能会导致程序认为它们是同步的,而实际上并非如此。

    【讨论】:

    • 如果是这样,我将如何解决它?虽然我不确定是不是这样,因为 1)getchar 应该阻止服务器继续(并破坏信号量),并且客户端段错误发生在此之前;和 2) 注释掉 sem_destroy 行无济于事。我还在错误检查中包装了sem_initsem_postsem_wait 函数,但它们不会返回错误。
    • 同步对象应该遵循与 RAII 类似的模式(资源获取是初始化),它跟踪对象的未完成引用的数量。要销毁它,它必须首先变得不可发现,然后当它的引用计数达到零时,销毁诸如同步变量之类的东西是安全的。未来会感谢你。
    【解决方案2】:

    事实证明,我必须在客户端中打开具有读写权限的共享内存,并在 mmap 中相应地更新保护。

    这是一个非常愚蠢的错误,因为很明显客户端也需要写入权限才能实际修改信号量。

    所以在客户端代码中,以下更改解决了它

    ...
    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)
    ...
    

    【讨论】: