父子进程场景下如何将共享内存中的学生数据写入文本文件?
解决共享内存数据写入文件的问题
Hey,我来帮你搞定这个从共享内存读取学生数据写入文件的问题。你的核心问题是没有正确解析共享内存里的struct student数据,咱们一步步修正,先看问题出在哪,再给你完整的解决方案。
问题分析
- 指针类型错误:你把共享内存附加后的
char*指针直接当作学生数据来访问,但没有转换成struct student*类型,导致无法正确解析每个学生的姓名和分数。 - 读取时机不对:父进程在
wait(NULL)之前就附加了共享内存,这时候子进程可能还没写完数据,读取到的内容不完整。 - 共享内存大小固定:你用固定的100字节创建共享内存,当学生数量较多时,会超出内存大小导致数据损坏。
- fprintf格式错误:你原来的
fprintf只输出了姓名,没有正确输出分数,格式字符串缺少对应的占位符。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/wait.h> #include <string.h> #include <unistd.h> struct student { char name[20]; float marks; }; int main(int argc, char* argv[]) { if (argc != 2) { printf("Usage: %s <number_of_students>\n", argv[0]); exit(1); } int n = atoi(argv[1]); if (n <= 0) { printf("Invalid number of students\n"); exit(1); } key_t key = 10; // 根据学生数量动态计算共享内存大小,避免溢出 size_t shm_size = n * sizeof(struct student); int shmID = shmget(key, shm_size, 0666 | IPC_CREAT | IPC_EXCL); if (shmID == -1) { perror("shmget failed"); exit(1); } pid_t pid = fork(); if (pid == -1) { perror("fork failed"); shmctl(shmID, IPC_RMID, NULL); exit(1); } if(pid == 0) { // 子进程:写入学生数据到共享内存 struct student* child_at = (struct student*) shmat(shmID, NULL, 0); if (child_at == (void*)-1) { perror("shmat child failed"); exit(1); } printf("Number of students: %d\n", n); for (int i = 0; i < n; ++i) { printf("For student %d\nEnter name: ", i+1); scanf("%s", child_at[i].name); // 直接写入共享内存,不需要本地数组 printf("Enter marks: "); scanf("%f", &child_at[i].marks); } // 打印验证写入的数据 printf("\n\tName\tMarks\t\n"); for(int i = 0; i < n; i++) { printf("\t%s\t%.2f\t\n", child_at[i].name, child_at[i].marks); } int d1 = shmdt(child_at); if (d1 == -1) { perror("shmdt child failed"); } exit(0); } else if (pid > 0) { // 父进程:等待子进程完成,读取共享内存数据并写入文件 wait(NULL); // 确保子进程完全写入数据 struct student* parent_at = (struct student*) shmat(shmID, NULL, 0); if (parent_at == (void*)-1) { perror("shmat parent failed"); shmctl(shmID, IPC_RMID, NULL); exit(1); } FILE *fptr = fopen("student.txt", "w"); if(fptr == NULL) { perror("fopen failed"); shmdt(parent_at); shmctl(shmID, IPC_RMID, NULL); exit(1); } // 直接从共享内存读取数据写入文件,也可以复制到本地数组再写入 fprintf(fptr, "Name\t\tMarks\n"); fprintf(fptr, "------------------------\n"); for (int i = 0; i < n; i++) { fprintf(fptr, "%s\t\t%.2f\n", parent_at[i].name, parent_at[i].marks); } fclose(fptr); int d2 = shmdt(parent_at); if (d2 == -1) { perror("shmdt parent failed"); } shmctl(shmID, IPC_RMID, NULL); } return 0; }
关键修改点说明
- 动态计算共享内存大小:根据学生数量
n乘以单个struct student的大小来创建共享内存,避免固定大小导致的溢出问题。 - 子进程直接写入共享内存:去掉了本地的
stuarr数组,直接把数据写入共享内存的指针,减少内存复制,更高效。 - 父进程等待后再附加共享内存:先调用
wait(NULL)等待子进程完成写入,再附加共享内存,确保读取到完整的数据。 - 正确转换指针类型:把共享内存附加后的指针转换成
struct student*,这样可以像访问普通数组一样直接访问每个学生的数据。 - 修复fprintf格式:添加了分数的占位符
%.2f,并优化了文件输出的格式,让内容更清晰。 - 增加错误检查:对
shmget、fork、shmat、fopen等系统调用的返回值进行错误检查,方便调试问题。
测试方法
编译并运行程序,比如:
gcc -o student_shm student_shm.c ./student_shm 2
输入两个学生的姓名和分数后,查看student.txt文件,就能看到正确写入的数据了。
内容的提问来源于stack exchange,提问作者Wanheda
相关产品推荐
相关产品推荐

