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

修改共享内存中结构体成员值时程序停滞的技术求助

问题:共享内存中结构体字符串字段操作异常

我用共享内存存储一个结构体数组,每个结构体对应一个游戏房间(lobby)的信息。每个房间由fork创建的独立子进程负责,子进程会把自身房间的数据写入数组,父进程需要查看所有房间的代码。

共享内存初始化代码

int fd = shm_open("tabLobby", O_CREAT | O_RDWR, S_IRWXU);  
// Set the size of the shared memory object
int pageSize = sysconf(_SC_PAGE_SIZE);
CHECK(ftruncate(fd, pageSize), "__ftruncate__");
// Map the tabEtats object into the virtual address space of the calling process
tabLobby = mmap(0, pageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

进程创建与数据获取代码

int pidLobby;
tabLobby[nbLobby].pidLobby = 0;
CHECK(pidLobby = fork(), "fork()");
if(pidLobby == 0) {
    // Child
    serveurLobby(nbLobby);
    exit(EXIT_SUCCESS);
} else {
    // Father
    while(tabLobby[nbLobby].pidLobby == 0) {
        sleep(1);
    }
    // Show code and port
    printf(GREEN "Création du Lobby %s (Port %d)\n" RESET, tabLobby[nbLobby].code, tabLobby[nbLobby].port);
    // Writing response data
    send_t sendData;
    sendData.args[0] = tabLobby[nbLobby].ip;
    sendData.args[1] = tabLobby[nbLobby].code;
    char* portChar = malloc(sizeof(char) * 5);
    sprintf(portChar, "%d", tabLobby[nbLobby].port);
    sendData.args[2] = portChar;
}

子进程处理代码

void serveurLobby(int idLobby) {
    // Emplacement du lobby dans le tableau
    tabLobby[idLobby].ip = ip;
    tabLobby[idLobby].port = port;
    char* code = malloc(sizeof(char) * 6);
    // This functions generates a random code and puts it into code
    generateLobbyCode(code);
    strcpy(tabLobby[idLobby].code, code);
    printf("Lobby code : %s\n", tabLobby[idLobby].code);
    tabLobby[idLobby].pidLobby = getpid();
}

异常现象

  • 直接执行tabLobby[idLobby].code = "TEST"时,程序正常运行
  • 执行tabLobby[idLobby].code[0] = 'a'、strcpy/strdup等修改操作时,程序无报错但停滞
  • 用tabLobby[idLobby].code = code替代字符串拷贝函数,函数结束后数据丢失

解决方法

核心问题是你的结构体中code字段定义成了char*指针,而非固定大小的字符数组,这是共享内存场景下的典型错误:

  1. 执行tabLobby[idLobby].code = "TEST"时,指针指向的是进程地址空间中的常量字符串,父子进程地址空间可能巧合映射了同一段常量区,所以能读到,但这是不可靠的行为。
  2. 用strcpy或直接修改code[0]时,指针指向的要么是非法内存(导致程序停滞),要么是子进程私有堆内存(父进程无法访问)。
  3. tabLobby[idLobby].code = code是把子程序malloc的堆内存地址赋值给指针,该地址仅在子进程有效,父进程访问时属于无效内存,数据自然丢失。

修复步骤:

  1. 修改结构体定义:把char* code改成固定大小的字符数组,比如char code[6];(匹配你生成的5位代码加结束符的需求)。确保结构体所有字段都存储在共享内存中,而非指向外部内存区域。

    注意:如果ip字段也是char*,同样要改成固定大小数组,比如char ip[INET_ADDRSTRLEN];,否则会遇到相同问题。

  2. 调整子进程代码:直接使用共享内存中的数组存储代码,无需额外malloc:

    void serveurLobby(int idLobby) {
        tabLobby[idLobby].ip = ip; // 若ip是指针需替换为数组拷贝,比如strcpy(tabLobby[idLobby].ip, ip);
        tabLobby[idLobby].port = port;
        // 直接将生成的代码写入共享内存数组
        generateLobbyCode(tabLobby[idLobby].code);
        printf("Lobby code : %s\n", tabLobby[idLobby].code);
        tabLobby[idLobby].pidLobby = getpid();
    }
    
  3. 父进程直接读取数组:无需担心指针有效性问题,直接访问tabLobby[nbLobby].code即可。

额外注意事项:

  • 共享内存操作需保证原子性,当前用pidLobby作为同步标志可行,若有复杂并发操作,建议配合信号量或互斥锁避免数据竞争。
  • 确保ftruncate设置的共享内存大小足够容纳整个结构体数组,避免越界访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:34:50