C语言Shared Memory客户端服务端不同步及矩阵转置实现求助
基于共享内存的C语言客户端服务端通信问题
用C语言实现共享内存客户端服务端程序时,遇到两个核心问题:
- 客户端与服务端无法同时运行,一方启动后另一方会直接退出;
- 首次运行时数据传输不符合预期,无法完成矩阵的发送-转置-回传流程。
预期功能:客户端输入矩阵后传给服务端,服务端显示原矩阵并计算转置,再将转置矩阵回传给客户端显示。
需求说明
- 客户端接收Matrix并传输至服务端;
- 服务端显示接收的Matrix并计算其转置;
- 服务端将转置后的Matrix回传给客户端;
- 客户端显示接收的转置Matrix。
原client.c代码
/* 编写基于Shared Memory的客户端服务端程序,实现数据传输:客户端接收Matrix并传输至服务端,服务端显示接收的Matrix并计算其转置,再将转置后的Matrix回传给客户端,客户端显示接收的转置Matrix。 */ #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #define SIZE 3 int main() { struct matrix{ int arr[SIZE][SIZE]; int transpose[SIZE][SIZE]; int r,c; }buf; struct matrix* ptr; int shmid, pid; key_t key = ftok("shmfile",65); // shmget返回标识符存入shmid shmid = shmget(key,1024,0666|IPC_CREAT); // shmat连接至共享内存 ptr = (struct matrix*) shmat(shmid,(struct matrix*)0,0); printf("请输入数据 --\n"); for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ int temp; scanf("%d",&temp); ptr->arr[i][j] = temp; } } printf("内存中写入的数据:\n"); for(int i=0;i<3;i++) { printf("\n"); for(int j=0;j<3;j++) { printf("%d ",ptr->transpose[i][j]); } } printf("\n"); return 0; }
原server.c代码
/* 编写基于Shared Memory的客户端服务端程序,实现数据传输:客户端接收Matrix并传输至服务端,服务端显示接收的Matrix并计算其转置,再将转置后的Matrix回传给客户端,客户端显示接收的转置Matrix。 */ #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #define SIZE 3 int main() { struct matrix{ int arr[SIZE][SIZE]; int transpose[SIZE][SIZE]; int r,c; }buf; struct matrix* ptr; int shmid, pid; key_t key = ftok("shmfile",65); // shmget返回标识符存入shmid shmid = shmget(key,1024,0666|IPC_CREAT); // shmat连接至共享内存 ptr = (struct matrix*) shmat(shmid,(struct matrix*)0,0); printf("\n转置前"); // 打印原矩阵 for(int i=0;i<3;i++) { printf("\n"); for(int j=0;j<3;j++) { printf("%d ",ptr->arr[i][j]); } } printf("\n"); for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) { ptr->transpose[j][i] = ptr->arr[i][j]; } printf("转置矩阵已写入内存:\n"); return 0; }
原运行输出
服务端首次运行
sharon@LAPTOP-5LEVPDNC:~/CN_LAB/L5$ gcc q1server.c -o q1server sharon@LAPTOP-5LEVPDNC:~/CN_LAB/L5$ ./q1server 转置前 0 0 0 0 0 0 0 0 0 转置矩阵已写入内存:
客户端首次运行
sharon@LAPTOP-5LEVPDNC:~/CN_LAB/L5$ gcc q1client.c -o q1client sharon@LAPTOP-5LEVPDNC:~/CN_LAB/L5$ ./q1client 请输入数据 -- 1 2 3 4 5 6 7 8 9 内存中写入的数据: 0 0 0 0 0 0 0 0 0
再次运行服务端
sharon@LAPTOP-5LEVPDNC:~/CN_LAB/L5$ ./q1server 转置前 1 2 3 4 5 6 7 8 9 转置矩阵已写入内存:
再次运行客户端
sharon@LAPTOP-5LEVPDNC:~/CN_LAB/L5$ ./q1client 请输入数据 -- 1 2 3 4 5 6 7 8 9 内存中写入的数据: 1 4 7 2 5 8 3 6 9
问题根源
- 缺乏进程同步机制:共享内存仅提供内存空间共享,不保证进程间的读写顺序。当前代码启动后直接完成读写就退出,导致双方无法同时运行,也无法确保数据写入后再被读取、处理完成后再被回读。
- 读写时机不匹配:客户端写入矩阵后立即读取转置矩阵,但此时服务端尚未处理;服务端启动时直接读取矩阵,此时客户端可能还未完成输入,因此首次运行读到初始值0。
修复方案
使用System V信号量实现进程间同步,配合共享内存完成有序的读写流程,同时优化共享内存的创建与使用:
步骤1:确保shmfile存在
先执行命令创建文件:
touch shmfile
修复后的client.c
#include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <stdio.h> #include <stdlib.h> #define SIZE 3 // 信号量操作联合体 union semun { int val; struct semid_ds *buf; unsigned short *array; }; struct matrix{ int arr[SIZE][SIZE]; int transpose[SIZE][SIZE]; int status; // 0:未写入, 1:已写入等待处理, 2:处理完成可读取 }; int main() { struct matrix* ptr; int shmid, semid; key_t key = ftok("shmfile",65); // 创建/获取共享内存,使用结构体大小指定内存大小 shmid = shmget(key, sizeof(struct matrix), 0666|IPC_CREAT); if(shmid == -1){ perror("shmget failed"); return 1; } // 连接共享内存 ptr = (struct matrix*) shmat(shmid, NULL, 0); if(ptr == (void*)-1){ perror("shmat failed"); return 1; } // 创建/获取信号量,2个信号量:0用于客户端通知服务端,1用于服务端通知客户端 semid = semget(key, 2, 0666|IPC_CREAT); if(semid == -1){ perror("semget failed"); shmdt(ptr); return 1; } // 初始化信号量:客户端信号量(0)初始为0,服务端信号量(1)初始为0 union semun sem_init; sem_init.val = 0; semctl(semid, 0, SETVAL, sem_init); semctl(semid, 1, SETVAL, sem_init); printf("请输入3x3矩阵数据 --\n"); for(int i=0;i<SIZE;i++){ for(int j=0;j<SIZE;j++){ scanf("%d", &ptr->arr[i][j]); } } ptr->status = 1; // 标记矩阵已写入 // 发送信号通知服务端可以处理 struct sembuf sem_op; sem_op.sem_num = 0; sem_op.sem_op = 1; // V操作,增加信号量 sem_op.sem_flg = 0; semop(semid, &sem_op, 1); // 等待服务端处理完成的信号 sem_op.sem_num = 1; sem_op.sem_op = -1; // P操作,等待信号量>0 semop(semid, &sem_op, 1); // 读取并显示转置矩阵 printf("收到服务端返回的转置矩阵:\n"); for(int i=0;i<SIZE;i++){ printf("\n"); for(int j=0;j<SIZE;j++){ printf("%d ", ptr->transpose[i][j]); } } printf("\n"); // 断开共享内存连接 shmdt(ptr); return 0; }
修复后的server.c
#include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <stdio.h> #include <stdlib.h> #define SIZE 3 // 信号量操作联合体 union semun { int val; struct semid_ds *buf; unsigned short *array; }; struct matrix{ int arr[SIZE][SIZE]; int transpose[SIZE][SIZE]; int status; // 0:未写入, 1:已写入等待处理, 2:处理完成可读取 }; int main() { struct matrix* ptr; int shmid, semid; key_t key = ftok("shmfile",65); // 获取共享内存 shmid = shmget(key, sizeof(struct matrix), 0666|IPC_CREAT); if(shmid == -1){ perror("shmget failed"); return 1; } // 连接共享内存 ptr = (struct matrix*) shmat(shmid, NULL, 0); if(ptr == (void*)-1){ perror("shmat failed"); return 1; } // 获取信号量 semid = semget(key, 2, 0666|IPC_CREAT); if(semid == -1){ perror("semget failed"); shmdt(ptr); return 1; } // 等待客户端写入完成的信号 struct sembuf sem_op; sem_op.sem_num = 0; sem_op.sem_op = -1; // P操作 semop(semid, &sem_op, 1); // 显示原矩阵 printf("收到客户端的矩阵:\n"); for(int i=0;i<SIZE;i++){ printf("\n"); for(int j=0;j<SIZE;j++){ printf("%d ", ptr->arr[i][j]); } } printf("\n"); // 计算转置矩阵 for(int i=0;i<SIZE;i++){ for(int j=0;j<SIZE;j++){ ptr->transpose[j][i] = ptr->arr[i][j]; } } ptr->status = 2; // 标记转置完成 // 发送信号通知客户端可以读取 sem_op.sem_num = 1; sem_op.sem_op = 1; // V操作 semop(semid, &sem_op, 1); printf("转置矩阵已写入共享内存\n"); // 断开共享内存连接 shmdt(ptr); return 0; }
运行方法
- 先启动服务端(会阻塞等待客户端信号):
gcc server.c -o server ./server
- 再打开另一个终端运行客户端,输入矩阵数据:
gcc client.c -o client ./client
额外注意事项
- 若不再使用共享内存和信号量,可手动删除:
# 查看共享内存和信号量 ipcs -m ipcs -s # 删除指定的共享内存(shmid为对应ID) ipcrm shm <shmid> # 删除指定的信号量(semid为对应ID) ipcrm sem <semid>
内容的提问来源于stack exchange,提问作者potato
相关产品推荐
相关产品推荐

