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

共享内存中结构体数组跨进程共享失效的问题排查求助

共享内存中结构体数组异常问题排查

问题描述

我开发了一个用于在两个进程间初始化并填充结构体数组的库,但功能无法正常工作——不过共享int数组可正常运行。我怀疑问题出在Block结构体中的Transaction指针上,是否是它导致共享内存无数据、甚至引发进程退出?

补充信息:我强制size为0仅插入并打印一个元素,print函数输出Blocco n°0(说明Masterbook[i].id有值)后就卡住了,修改char*后问题仍存在。

相关代码

结构体定义

struct Transaction {
    char timestamp[24];
    int sender; /* pid user sent */
    int receiver;
    int reward;
    int money;
};

struct Block
{
    int id;
    struct Transaction* tr1;
    struct Transaction* tr2;
    struct Transaction* reward;
};

masterbook.c

struct Block* MasterBook;
int sizeMaster = -1;

void initMasterBook(){
    int shmid,sizeid;
    shmid = shmget(SH_KEY_MASTERBOOK,MAX_MASTER*sizeof(struct Block),IPC_CREAT | 0666);
    if(shmid == -1) {
        perror("shmget error");
        exit(EXIT_FAILURE);
    }
    MasterBook = (struct Block*)shmat(shmid,NULL,0);
    if ( MasterBook == (void*)-1 ) {
        perror ( "Error in shmat: " );
        exit(EXIT_FAILURE);
    }
}

void freeMasterBook(){
    shmdt((void *) MasterBook); 
}

int addBlock(struct Block block){
    if(sizeMaster >= MAX_MASTER-1) return -1;
    ++sizeMaster;
    block.id = sizeMaster;
    MasterBook[sizeMaster] = block;
    return 0;
}

int printMasterBook(){
    if(sizeMaster < 0) {
        printf("\nMasterbook vuoto");
        return -1;
    }
    int i = 0;
    for ( i; i <= sizeMaster; i++)
    {
        printf("\nBlocco n° : %d",MasterBook[i].id);
        
        if(MasterBook[i].tr1 != NULL){
            printf("\n\tTransazione n° 1 : ");
            struct Transaction *temp = MasterBook[i].tr1;
            printf("\n\t\tReceiver: %d",temp->receiver);
            printf("\n\t\tSender: %d",temp->sender);
            printf("\n\t\tReward: %d",temp->reward);
            printf("\n\t\tTimestamp: %s",temp->timestamp);
        }else printf("\nThere's no transaction 1 set!");

        if(MasterBook[i].tr2 != NULL){
            printf("\n\tTransazione n° 2 : ");
            struct Transaction *temp2 = MasterBook[i].tr2;
            printf("\n\t\tReceiver: %d",temp2->receiver);
            printf("\n\t\tSender: %d",temp2->sender);
            printf("\n\t\tReward: %d",temp2->reward);
            printf("\n\t\tTimestamp: %s",temp2->timestamp);
        }else printf("\nThere's no transaction 2 set!");

        if(MasterBook[i].reward != NULL){
            printf("\n\tTransazione Reward : ");
            struct Transaction *temp3 = MasterBook[i].reward;
            printf("\n\t\tReceiver: %d",temp3->receiver);
            printf("\n\t\tSender: %d",temp3->sender);
            printf("\n\t\tReward: %d",temp3->reward);
            printf("\n\t\tTimestamp: %s",temp3->timestamp);
        }else printf("\nThere's no transaction reward set!");
    
        printf("finish print");
    }
    return 0;
}

master.c

initMasterBook();
printf("\nthe size id: %d",sizeMaster);
printMasterBook();
struct Block v;
struct Transaction t1;
t1.money = 4;
t1.receiver = 2;
t1.sender = 3;
strcpy(t1.timestamp,"ciao");
t1.reward = 4;
v.tr1 = &t1;
addBlock(v);
printMasterBook();
printf("\nthe size id: %d",sizeMaster);

node.c

int main(int argc, char const *argv[])
{
    initMasterBook();
    sizeMaster=0;
    printMasterBook();
    freeMasterBook();
    return 0;
}

核心问题分析

你的猜想完全正确,Block结构体中的指针是导致问题的根源,具体原因如下:

  1. 指针地址的进程独立性:在master.c中,你将v.tr1指向栈上的局部变量t1,这个地址是当前进程的虚拟内存地址,在另一个进程(node.c)中完全无效,甚至可能指向非法内存区域,导致进程崩溃或卡住。
  2. 共享内存仅存储指针值而非实际数据:共享内存中只保存了Block结构体里的指针数值,Transaction的实际数据并没有放入共享内存,其他进程无法访问到有效数据。
  3. sizeMaster的同步问题:sizeMaster是普通全局变量,没有放入共享内存,两个进程各自维护独立的sizeMaster值,node.c中手动设置sizeMaster=0完全错误,和master.c中的实际值不一致。

修复方案

1. 修改结构体定义,去掉指针直接嵌入数据

将Block中的指针改为直接包含Transaction结构体,让数据直接存储在共享内存中:

struct Transaction {
    char timestamp[24];
    int sender; /* pid user sent */
    int receiver;
    int reward;
    int money;
};

struct Block
{
    int id;
    struct Transaction tr1;
    struct Transaction tr2;
    struct Transaction reward;
    // 添加标记位表示是否有有效数据
    int has_tr1;
    int has_tr2;
    int has_reward;
};

2. 将sizeMaster放入共享内存

不能用普通全局变量维护数组大小,需要把它也放到共享内存中,确保两个进程访问的是同一个值:

struct Block* MasterBook;
int* sizeMaster; // 改为指针,指向共享内存中的变量

void initMasterBook(){
    int shmid, size_shmid;
    // 创建Block数组的共享内存
    shmid = shmget(SH_KEY_MASTERBOOK, MAX_MASTER*sizeof(struct Block), IPC_CREAT | 0666);
    if(shmid == -1) {
        perror("shmget error");
        exit(EXIT_FAILURE);
    }
    MasterBook = (struct Block*)shmat(shmid, NULL, 0);
    if (MasterBook == (void*)-1) { // shmat失败返回(void*)-1,注意类型判断
        perror("Error in shmat: ");
        exit(EXIT_FAILURE);
    }
    // 创建sizeMaster的共享内存
    size_shmid = shmget(SH_KEY_SIZE, sizeof(int), IPC_CREAT | 0666);
    if(size_shmid == -1) {
        perror("shmget size error");
        exit(EXIT_FAILURE);
    }
    sizeMaster = (int*)shmat(size_shmid, NULL, 0);
    if (sizeMaster == (void*)-1) {
        perror("Error in shmat size: ");
        exit(EXIT_FAILURE);
    }
    // 仅首次创建时初始化sizeMaster为-1
    struct shmid_ds ds;
    if(shmctl(size_shmid, IPC_STAT, &ds) != -1 && ds.shm_nattch == 1) {
        *sizeMaster = -1;
    }
}

void freeMasterBook(){
    shmdt((void *)MasterBook);
    shmdt((void *)sizeMaster);
}

int addBlock(struct Block block){
    if(*sizeMaster >= MAX_MASTER-1) return -1;
    ++(*sizeMaster);
    block.id = *sizeMaster;
    MasterBook[*sizeMaster] = block;
    return 0;
}

3. 调整print函数和数据填充逻辑

使用标记位判断是否有有效数据,不再依赖指针:

int printMasterBook(){
    if(*sizeMaster < 0) {
        printf("\nMasterbook vuoto");
        return -1;
    }
    int i = 0;
    for (; i <= *sizeMaster; i++)
    {
        printf("\nBlocco n° : %d", MasterBook[i].id);
        
        if(MasterBook[i].has_tr1){
            printf("\n\tTransazione n° 1 : ");
            printf("\n\t\tReceiver: %d", MasterBook[i].tr1.receiver);
            printf("\n\t\tSender: %d", MasterBook[i].tr1.sender);
            printf("\n\t\tReward: %d", MasterBook[i].tr1.reward);
            printf("\n\t\tTimestamp: %s", MasterBook[i].tr1.timestamp);
        } else {
            printf("\nThere's no transaction 1 set!");
        }

        if(MasterBook[i].has_tr2){
            printf("\n\tTransazione n° 2 : ");
            printf("\n\t\tReceiver: %d", MasterBook[i].tr2.receiver);
            printf("\n\t\tSender: %d", MasterBook[i].tr2.sender);
            printf("\n\t\tReward: %d", MasterBook[i].tr2.reward);
            printf("\n\t\tTimestamp: %s", MasterBook[i].tr2.timestamp);
        } else {
            printf("\nThere's no transaction 2 set!");
        }

        if(MasterBook[i].has_reward){
            printf("\n\tTransazione Reward : ");
            printf("\n\t\tReceiver: %d", MasterBook[i].reward.receiver);
            printf("\n\t\tSender: %d", MasterBook[i].reward.sender);
            printf("\n\t\tReward: %d", MasterBook[i].reward.reward);
            printf("\n\t\tTimestamp: %s", MasterBook[i].reward.timestamp);
        } else {
            printf("\nThere's no transaction reward set!");
        }
    
        printf("finish print");
    }
    return 0;
}

修改master.c的数据填充逻辑:

initMasterBook();
printf("\nthe size id: %d", *sizeMaster);
printMasterBook();
struct Block v;
// 初始化标记位
v.has_tr1 = 1;
v.has_tr2 = 0;
v.has_reward = 0;
// 直接赋值tr1
v.tr1.money = 4;
v.tr1.receiver = 2;
v.tr1.sender = 3;
strcpy(v.tr1.timestamp, "ciao");
v.tr1.reward = 4;
addBlock(v);
printMasterBook();
printf("\nthe size id: %d", *sizeMaster);

4. 修复node.c的错误

node.c中不能手动设置sizeMaster,直接使用共享内存中的值即可:

int main(int argc, char const *argv[])
{
    initMasterBook();
    printMasterBook();
    freeMasterBook();
    return 0;
}

额外注意事项

  • 共享内存的键值(SH_KEY_MASTERBOOK、SH_KEY_SIZE)要确保两个进程使用相同的值,建议定义在公共头文件中。
  • 使用shmat时,判断失败的正确方式是if (shmat_result == (void*)-1),因为返回值是指针类型而非int。
  • 进程退出时可通过shmctl删除共享内存,避免系统资源残留。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 10:06:21