共享内存机制
是允许两个或多个进程(不相关或有亲缘关系)访问同一个逻辑内存的机制。它是共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。
两种常用共享内存方式
- System V版本的共享内存 shmm
多个进程直接共享内存
2.文件映射 mmap
- 文件进行频繁读写,将一个普通文件映射到内存中
- 将特殊文件进行匿名内存映射,为关联进程提供共享内存空间
- 为无关联的进程提供共享内存空间,将一个普通文件映射到内存中
IPC (Inter-Process Communication) 通信中的共享内存是一种允许不同进程访问同一块内存区域的技术。这种技术通常用于高效地传递大量数据,因为进程可以直接读写内存,而不需要通过内核进行复制。
创建writer.c程序:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define SHARED_FILE "shared_data"
#define SHARED_SIZE 4096 // 共享映射区的大小
int main() {
int fd;
void *shared_memory;
// 打开共享文件
if ((fd = open(SHARED_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) == -1) {
perror("open");
exit(1);
}
// 截断文件到指定大小
if (ftruncate(fd, SHARED_SIZE) == -1) {
perror("ftruncate");
exit(1);
}
// 将文件映射到内存
shared_memory = mmap(NULL, SHARED_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shared_memory == MAP_FAILED) {
perror("mmap");
exit(1);
}
// 写入数据到共享映射区
const char *message = "Hello from writer!";
strcpy(shared_memory, message);
// 等待用户输入,以便读者有时间读取数据
printf("Data written to shared memory. Press Enter to continue...\n");
getchar();
// 解除映射和关闭文件
if (munmap(shared_memory, SHARED_SIZE) == -1) {
perror("munmap");
exit(1);
}
close(fd);
return 0;
}
接下来,创建reader.c程序:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define SHARED_FILE "shared_data"
#define SHARED_SIZE 4096 // 共享映射区的大小
int main() {
int fd;
void *shared_memory;
char buffer[SHARED_SIZE];
// 打开共享文件
if ((fd = open(SHARED_FILE, O_RDONLY)) == -1) {
perror("open");
exit(1);
}
// 将文件映射到内存
shared_memory = mmap(NULL, SHARED_SIZE, PROT_READ, MAP_SHARED, fd, 0);
if (shared_memory == MAP_FAILED) {
perror("mmap");
exit(1);
}
// 从共享映射区读取数据
memcpy(buffer, shared_memory, SHARED_SIZE);
printf("Data read from shared memory: %s\n", buffer);
// 解除映射和关闭文件
if (munmap(shared_memory, SHARED_SIZE) == -1) {
perror("munmap");
exit(1);
}
close(fd);
return 0;
}
编译并运行这两个程序:
bash
gcc -o writer writer.c
gcc -o reader reader.c
# 在一个终端中运行 writer 程序
./writer
# 在另一个终端中运行 reader 程序
./reader
注意事项:
- 同步:在多个进程访问共享内存时,必须小心处理同步问题,以避免数据竞争和不一致。可以使用信号量、互斥锁或其他同步机制来确保数据的一致性。
- 错误处理:在实际应用中,应更全面地处理错误,例如检查
mmap
、munmap
、shm_open
等函数的返回值。 - 清理:当不再需要共享内存时,应使用
shm_unlink
函数删除它。 - 安全性:确保正确设置共享内存的权限,以防止未经授权的访问。
这只是一个简单的示例,实际使用中可能需要更复杂的逻辑来处理同步、错误处理和资源管理。
shmwrite.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
struct Conn_stat
{
int count;
char ip[64];
};
int main()
{
void *shm = NULL;
int shmid = 0, i = 0;
struct Conn_stat stat = {0,"127.0.0.1"};
//创建共享内存
shmid = shmget((key_t)1234, sizeof(struct Conn_stat), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(1);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, (void*)0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(2);
}
printf("Memory attached at %p\n", shm);
//设置共享内存
struct Conn_stat *p = (struct Conn_stat*)shm;
memcpy(p,&stat,sizeof(struct Conn_stat));
while((i++) < 30)//修改共享内存中写数据
{
p->count++;
sleep(1);
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(3);
}
exit(0);
}
//shmread.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>
#include <errno.h>
struct Conn_stat
{
int count;
char ip[64];
};
int main()
{
void *shm = NULL;//分配的共享内存的原始首地址
struct Conn_stat *stat = NULL;//指向shm
int shmid;//共享内存标识符
//创建共享内存
shmid = shmget((key_t)1234, sizeof(struct Conn_stat), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(0);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(1);
}
printf("\nMemory attached at %p\n", shm);
//设置共享内存
stat = (struct Conn_stat*)shm;
int i = 0;
while((i++) < 10)
{
printf("ip = %s ,count: %d\t\t\n", stat->ip, stat->count);
sleep(1);
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(2);
}
//删除共享内存
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed, reason: %s\n",strerror(errno));
exit(3);
}
exit(0);
}