共享内存中结构体数组跨进程共享失效的问题排查求助
共享内存中结构体数组异常问题排查
问题描述
我开发了一个用于在两个进程间初始化并填充结构体数组的库,但功能无法正常工作——不过共享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结构体中的指针是导致问题的根源,具体原因如下:
- 指针地址的进程独立性:在
master.c中,你将v.tr1指向栈上的局部变量t1,这个地址是当前进程的虚拟内存地址,在另一个进程(node.c)中完全无效,甚至可能指向非法内存区域,导致进程崩溃或卡住。 - 共享内存仅存储指针值而非实际数据:共享内存中只保存了
Block结构体里的指针数值,Transaction的实际数据并没有放入共享内存,其他进程无法访问到有效数据。 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
相关产品推荐
相关产品推荐

