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

C语言多线程读写共享内存整数异常:读值为-1或段错误

问题分析与修复方案

看起来你在多线程共享内存读写的逻辑里踩了几个关键的坑,导致段错误和读取值异常的问题,咱们一步步拆解清楚:

核心问题梳理

  • 共享内存创建缺失O_CREAT参数:原多线程代码中shm_open没加O_CREAT,如果是第一次运行,共享内存对象不存在的话,fd会返回-1,后续mmap会失败拿到无效地址,这是段错误的主要诱因之一。
  • 混淆字符串写入与二进制整数写入:你用sprintf把整数转成ASCII字符串写入共享内存,但读取时却试图把它当成二进制整数解析(*(int*)memory)——这两种方式的内存布局完全不同:
    • sprintf(memory, "%d", i)会把i转成字符串,比如i=10时,内存里是'1' '0' '\0'(占3字节)
    • 而你真正需要的是直接写入整数的二进制值,就像单线程代码里的memcpy那样
  • 错误的内存读取方式:(int)memory是把共享内存的地址转成整数,不是读取内存里存储的值,这就是你读到-1(无效地址的整数表示)的原因。
  • 退出条件逻辑错误:if((int)memory == 0)是判断共享内存地址是否为NULL,不是判断内存里存储的整数是否为0,导致读线程永远无法退出。

修正后的多线程代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>

struct args {
    void* memptr;
    sem_t* semptr;
};

void *p1(void *vargp) {
    int* memory = (int*)((struct args*)vargp)->memptr; // 直接转成int*,简化读写逻辑
    sem_t* semaphore = ((struct args*)vargp)->semptr;

    for(int i=1; i<=10; i++) {
        sem_wait(semaphore); // 先获取信号量,保证互斥访问
        printf("写入线程: 写入值 %d\n", i);
        *memory = i; // 直接写入二进制整数,效果等同于memcpy
        sem_post(semaphore); // 释放信号量
        sleep(1);
    }

    // 写入结束标记0
    sem_wait(semaphore);
    *memory = 0;
    printf("写入线程: 写入结束标记0\n");
    sem_post(semaphore);
    sleep(0.1);
    return NULL; // 线程函数必须返回void*类型值
}

void *p2(void *vargp) {
    int* memory = (int*)((struct args*)vargp)->memptr;
    sem_t* semaphore = ((struct args*)vargp)->semptr;

    sleep(0.1); // 等待写入线程先启动
    while(1) {
        sem_wait(semaphore);
        int val = *memory; // 读取内存里的二进制整数
        printf("读取线程: 读取到值 %d\n", val);
        if(val == 0){
            sem_post(semaphore);
            break; // 读到结束标记,退出循环
        }
        sem_post(semaphore);
        sleep(1);
    }
    return NULL;
}

const int ByteSize = sizeof(int); // 用sizeof(int)替代硬编码4,适配不同平台
const char* SharedName = "memNameTest";
const char* SemaphoreName = "semNameTest";

int main() {
    // 添加O_CREAT参数,确保共享内存不存在时自动创建
    int fd = shm_open(SharedName, O_CREAT | O_RDWR, 0644);
    if(fd == -1) {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    ftruncate(fd, ByteSize);
    void* memptr = mmap(0, ByteSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(memptr == MAP_FAILED) {
        perror("mmap failed");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // 信号量添加O_CREAT参数,初始值设为1(允许第一个线程先获取)
    sem_t* semptr = sem_open(SemaphoreName, O_CREAT, 0644, 1);
    if(semptr == SEM_FAILED) {
        perror("sem_open failed");
        munmap(memptr, ByteSize);
        close(fd);
        exit(EXIT_FAILURE);
    }

    struct args *Share = (struct args *)malloc(sizeof(struct args));
    Share->memptr = memptr;
    Share->semptr = semptr;

    pthread_t thread1, thread2;
    printf("启动线程...\n");
    pthread_create(&thread1, NULL, p1, (void*)Share);
    pthread_create(&thread2, NULL, p2, (void*)Share);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    printf("线程执行完毕\n");

    // 彻底清理资源,避免残留
    munmap(memptr, ByteSize);
    close(fd);
    sem_close(semptr);
    shm_unlink(SharedName); // 用shm_unlink删除共享内存对象
    sem_unlink(SemaphoreName); // 删除信号量对象
    free(Share); // 释放malloc分配的结构体

    return 0;
}

关键修改点说明

  1. 共享内存创建:给shm_open加上O_CREAT参数,同时增加错误检查,避免无效文件描述符导致后续操作崩溃。
  2. 内存读写优化:把共享内存指针直接转成int*,通过*memory = i直接写入二进制整数,读取时直接取*memory,和单线程的memcpy效果一致且更简洁。
  3. 信号量调整:将信号量初始值设为1(保证任意一个线程能先获取锁),同时添加O_CREAT和错误检查。
  4. 退出条件修正:读取内存里的实际值val == 0来判断是否退出,而非判断共享内存地址。
  5. 资源完善清理:添加shm_unlink和sem_unlink彻底删除共享内存与信号量对象,避免系统残留;同时释放malloc分配的结构体。
  6. 线程函数规范:给线程函数添加return NULL,符合void*返回值的要求。

为什么单线程能正常运行?

单线程代码里用了memcpy(memptr, &i, sizeof(int))直接写入整数的二进制值,读取时用*((int*)memptr)正确解析二进制内容,逻辑完全匹配,所以能正常工作。而多线程里误用了sprintf(字符串写入),导致内存内容和读取方式不匹配,再加上共享内存创建的问题,才出现了段错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 07:03:12