You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

消费者进程可获取POSIX信号量值但调用sem_trywait触发段错误

问题描述

在多进程项目中,sender.c(生产者进程)可通过sem_wait和sem_post修改共享内存中的无名POSIX信号量值,但receiver.c(消费者进程)仅能获取该信号量数值,调用sem_trywait时会触发段错误。

怀疑问题源于参数传递(传引用vs传值)或信号量释放/销毁方式不当,sender和receiver均通过静态链接获取buffer_node结构体。已在x64 Ubuntu和ARM树莓派Raspbian测试,替换sem_trywait为sem_wait问题仍存在,希望尽量避免改用命名信号量。

相关代码

头文件(cbuffapi.h)

#define _CBUFFAPI_H_

#include <stdlib.h>     
#include <signal.h>     
#include "stdio.h"
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <assert.h>
#include <sys/stat.h>      
#include <fcntl.h>   

#define BUFFER_SIZE    5
#define SAMPLES_AMOUNT 240


struct buffer_node {
    unsigned long int buffered_samples[SAMPLES_AMOUNT];
    sem_t semaphore;
    int sem_val;
};

struct cyclic_buffer {
    struct buffer_node buffer_elements[BUFFER_SIZE];
    unsigned int head;
    unsigned int tail;
    unsigned int full;
    unsigned int max;
};

/*cyclic_buffer* init_buffer();
//void buffer_reset(cyclic_buffer* cb);
//void buffer_free(cyclic_buffer* cb);
//void buffer_put(cyclic_buffer* cb, unsigned int index);
//unsigned int is_buffer_full(cyclic_buffer* cb);*/
int get_sem_val(sem_t sem);

#endif

Sender进程(sender.c)

#include <stdlib.h>     
#include <signal.h>     
#include "stdio.h"
#include <semaphore.h>
#include "cbuffapi.h"
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <assert.h>
#include <sys/stat.h>      
#include <fcntl.h>   

#define NAME           "/testmem"

struct buffer_node * shm_buffer;

void Handler(int signo)
{
    //System Exit
    printf("\r\n END \r\n");
    sem_close(&(shm_buffer->semaphore));
    munmap(0, sizeof(struct buffer_node));
    shm_unlink(NAME);
    exit(0);
}

int main()
{
    signal(SIGINT, Handler);
    int shm_cyclic_buffer_fd = shm_open(NAME, O_CREAT|O_RDWR|O_EXCL, 0666);

    if(shm_cyclic_buffer_fd < 0)
    {
        perror("shm_open()");
        return 1;
    }

    ftruncate(shm_cyclic_buffer_fd, sizeof(struct buffer_node));
    shm_buffer = (struct buffer_node*)mmap(0, sizeof(struct buffer_node), PROT_WRITE|PROT_READ, MAP_SHARED, shm_cyclic_buffer_fd, 0);
    shm_buffer->sem_val = 67;
    sem_init(&(shm_buffer->semaphore), 1, 4);
    int test_sem_val = 15;

    while(1)
    {
        if(sem_trywait(&(shm_buffer->semaphore))==0)
        {
            sem_getvalue(&(shm_buffer->semaphore), &test_sem_val);
            printf("%d\n", test_sem_val);
            shm_buffer->sem_val = shm_buffer->sem_val + 1;
            printf("%d\n", shm_buffer->sem_val);
            sleep(5);
            sem_post(&(shm_buffer->semaphore));
            sem_getvalue(&(shm_buffer->semaphore), &test_sem_val);
            printf("%d\n", test_sem_val);
        }
        else if(sem_trywait(&(shm_buffer->semaphore))== -1)
        {   
            printf("variable reading - overwriting impossible\n");
        }

        sleep(0.5);
    }
    return 0;
}

Receiver进程(receiver.c)

#include <stdlib.h>     
#include <signal.h>     
#include "stdio.h"
#include <semaphore.h>
#include "cbuffapi.h"
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <assert.h>
#include <sys/stat.h>      
#include <fcntl.h>   

#define NAME           "/testmem"

int shared_memory_fd;
void Handler(int signo)
{
    //System Exit
    printf("\r\n END \r\n");
    munmap(0, sizeof(struct buffer_node));
    close(shared_memory_fd);
    shm_unlink(NAME);
    exit(0);
}

int main()
{
    signal(SIGINT, Handler);
    struct buffer_node* shm_buffer;

    int shared_memory_fd = shm_open(NAME, O_RDONLY, 0666);

    if(shared_memory_fd < 0)
    {
        perror("Impossible to get shared memory file descriptor");
        return 1;
    }
    printf("Shared memory ID ok\n");
    //memory mapping
    shm_buffer=(struct buffer_node*)mmap(0, sizeof(struct buffer_node), PROT_READ, MAP_SHARED, shared_memory_fd, 0);

    if(shm_buffer == (void *) -1)
    {
        perror("Cannot attach to shared memory");
        return 2;
    }
    printf("map ok");
    printf("shared memory ID ok\n");
    int test_sem_val = 1;

    while(1)
    {
        sem_getvalue(&(shm_buffer->semaphore), &test_sem_val);
        printf("%d\n", test_sem_val);
        printf("%d\n", shm_buffer->sem_val);
        sleep(5);
        if(sem_trywait(&(shm_buffer->semaphore)) == 0)
        {   
            printf("%d\n", shm_buffer->sem_val);
            sleep(1);
            sem_post(&(shm_buffer->semaphore));
        }
        else
        {
            printf("variable modified\n");
            sleep(1);
        }
    }
    return 0;
}
问题分析与解决
  • 核心问题1:共享内存映射权限不足
    Receiver进程仅用PROT_READ权限映射共享内存,但sem_trywait和sem_post会修改信号量内部状态,需要写入共享内存,权限不足直接触发段错误。修复方式是给Receiver的映射加上PROT_WRITE权限:

    shm_buffer=(struct buffer_node*)mmap(0, sizeof(struct buffer_node), PROT_READ|PROT_WRITE, MAP_SHARED, shared_memory_fd, 0);
    
  • 核心问题2:信号量操作重复调用
    Sender进程中连续两次调用sem_trywait,第一次失败后再次尝试会浪费资源,应改为调用一次并保存结果:

    int ret = sem_trywait(&(shm_buffer->semaphore));
    if(ret == 0) {
        // 成功处理逻辑
    } else if(ret == -1) {
        // 失败处理逻辑
    }
    
  • 信号量与共享内存销毁错误

    • Sender进程误用sem_close,无名信号量应使用sem_destroy,且需确保所有进程不再使用时再调用,否则会引发未定义行为。
    • Receiver进程不应调用shm_unlink,共享内存的unlink操作应由创建它的Sender进程负责,Receiver只需关闭文件描述符并解除映射。
    • 信号处理函数中munmap的第一个参数应为映射起始地址shm_buffer,而非0,否则会导致未定义行为。
  • 其他规范优化

    • 检查mmap返回值时,建议使用标准宏MAP_FAILED替代(void*)-1,兼容性更好。
    • 确保sem_init的第二个参数设为1(标识为跨进程共享的信号量),当前代码已正确设置。

内容的提问来源于stack exchange,提问作者V8Interceptor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 18:24:58