基于信号量与共享内存实现客户与服务器进程通信
基于信号量与共享内存实现客户与服务器进程通信
发送进程
代码
#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;
}