基于信号量与共享内存实现客户与服务器进程通信
发送进程
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#define SHARED_MEM_NAME "/shared_mem"
#define SEM_NAME "/sem_sync"
#define MEM_SIZE 1024
#define END_STRING "END"
int main() {
int shm_fd;
void *shared_mem;
sem_t *sem;
shm_fd = shm_open(SHARED_MEM_NAME, O_CREAT | O_RDWR, 0666);
if (shm_fd == –1) {
perror("shm_open failed");
exit(1);
}
ftruncate(shm_fd, MEM_SIZE);
shared_mem = mmap(0, MEM_SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shared_mem == MAP_FAILED) {
perror("mmap failed");
exit(1);
}
sem = sem_open(SEM_NAME, O_CREAT, 0666, 0);
if (sem == SEM_FAILED) {
perror("sem_open failed");
exit(1);
}
char buffer[MEM_SIZE];
while (1) {
printf("输入字符串:");
fgets(buffer, MEM_SIZE, stdin);
buffer[strcspn(buffer, "\\n")] = 0; // 去掉换行符
strncpy((char *)shared_mem, buffer, MEM_SIZE);
sem_post(sem);
if (strcmp(buffer, END_STRING) == 0) {
break;
}
}
munmap(shared_mem, MEM_SIZE);
close(shm_fd);
sem_close(sem);
return 0;
}
详细解析
0,头文件与宏定义
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
这些头文件包含了程序中使用的标准库和系统库:
-
stdio.h 和 stdlib.h:提供基本的输入/输出功能(如 printf 和 fgets)以及内存管理功能(如 exit)。
-
string.h:提供字符串操作函数,如 strncpy 和 strcmp。
-
unistd.h:提供对 POSIX 操作系统 API 的访问,如 close 函数。
-
fcntl.h:用于文件控制操作,如 shm_open 中的标志。
-
sys/mman.h:包含内存映射相关的函数,如 mmap 和 shm_open。
-
semaphore.h:提供信号量相关的功能,用于进程同步。
#define SHARED_MEM_NAME "/shared_mem"
#define SEM_NAME "/sem_sync"
#define MEM_SIZE 1024
#define END_STRING "END"
- SHARED_MEM_NAME:共享内存的名称。名称必须以斜杠 / 开头,以表示这是一个 POSIX 共享内存对象。
- SEM_NAME:信号量的名称,用于同步客户端和服务器进程。
- MEM_SIZE:共享内存的大小,以字节为单位。在这里,大小为 1024 字节(1 KB)。
- END_STRING:用于判断是否结束程序的特殊字符串 “END”。
1,shm_fd
shm_fd = shm_open(SHARED_MEM_NAME, O_CREAT | O_RDWR, 0666);
SHARED_MEM_NAME:共享内存的名称。名称必须以斜杠 / 开头,以表示这是一个 POSIX 共享内存对象,这里我们使用 /shared_mem 。
shm_open 函数包含在 sys/mman.h 头文件中,用于创建或打开一个 POSIX 共享内存对象。
其中 O_CREAT | O_RDRW 标志如果共享内存对象不存在就创建它,并且以可读可写模式打开它。
2,
0666 为权限设置,表示共享内存对象对所有用户可读可写。
错误处理:如果 shm_open 失败,返回 -1,并通过 perror 打印错误信息,然后调用 exit(1) 终止程序。
2,ftruncate
ftruncate(shm_fd, MEM_SIZE);
3,mmap
shared_mem = mmap(0, MEM_SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shared_mem == MAP_FAILED) {
perror("mmap failed");
exit(1);
}
设置共享内存对象的大小为 MEM_SIZE 字节,即 1024 字节。
- mmap:将共享内存映射到进程的地址空间,以便我们可以像使用普通内存一样访问它。
- 0:让操作系统选择共享内存的映射地址。
- MEM_SIZE:映射的大小(1024 字节)。
- PROT_WRITE:指定映射区域可写。
- MAP_SHARED:其他进程对共享内存的修改是可见的,并且会影响到共享内存的实际内容。
- shm_fd:共享内存对象的文件描述符。
- 0:偏移量,表示从共享内存的开头开始映射。
- 错误处理:如果 mmap 失败,返回 MAP_FAILED,并打印错误信息,然后终止程序。
4,sem_open
sem = sem_open(SEM_NAME, O_CREAT, 0666, 0);
if (sem == SEM_FAILED) {
perror("sem_open failed");
exit(1);
}
- sem_open:创建或打开一个命名信号量。
- SEM_NAME:信号量的名称。
- O_CREAT:如果信号量不存在,则创建它。
- 0666:权限设置,表示信号量对所有用户可访问。
- 0:信号量的初始值为 0(表示不可用状态)。
- 错误处理:如果 sem_open 失败,返回 SEM_FAILED,并打印错误信息,然后终止程序。
5,主循环
char buffer[MEM_SIZE];
while (1) {
printf("输入字符串:");
fgets(buffer, MEM_SIZE, stdin);
buffer[strcspn(buffer, "\\n")] = 0; // 去掉换行符
strncpy((char *)shared_mem, buffer, MEM_SIZE);
sem_post(sem);
if (strcmp(buffer, END_STRING) == 0) {
break;
}
}
从键盘中读入字符串,然后去掉换行符。
strcspn 的作用是找到 \\n 的位置,然后该位置换为 \\0,表示字符串结尾。
strncpy 表示将 buffer 中的内容复制到 shared_mem 中。
sem_post 表示增加信号量的值,表示共享内存中有新的数据可读,这个操作通知服务器进程可以读取共享内存的内容。
我们重复这个循环,直到输入的字符串为 END 时终止。
6,释放资源
munmap(shared_mem, MEM_SIZE);
close(shm_fd);
sem_close(sem);
munmap : 接触共享内存映射,释放地址空间
close : 关闭共享内存的文件描述符
sem_close : 关闭信号量
接收进程
代码与发送进程类似,故不做解析。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#define SHARED_MEM_NAME "/shared_mem"
#define SEM_NAME "/sem_sync"
#define MEM_SIZE 1024
#define END_STRING "END"
int main() {
int shm_fd;
void *shared_mem;
sem_t *sem;
shm_fd = shm_open(SHARED_MEM_NAME, O_RDONLY, 0666);
if (shm_fd == –1) {
perror("shm_open failed");
exit(1);
}
shared_mem = mmap(0, MEM_SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (shared_mem == MAP_FAILED) {
perror("mmap failed");
exit(1);
}
sem = sem_open(SEM_NAME, 0);
if (sem == SEM_FAILED) {
perror("sem_open failed");
exit(1);
}
char buffer[MEM_SIZE];
while (1) {
sem_wait(sem);
strncpy(buffer, (char *)shared_mem, MEM_SIZE);
buffer[MEM_SIZE – 1] = '\\0'; // 确保字符串以NULL结尾
printf("读取字符串:%s\\n", buffer);
if (strcmp(buffer, END_STRING) == 0) {
break;
}
}
munmap(shared_mem, MEM_SIZE);
close(shm_fd);
sem_close(sem);
return 0;
}
评论前必须登录!
注册