03 | linux下进程通信(共享内存)
接下来我们介绍一种简单而高效的进程间通信的方式!
共享内存
注意了是通信也就是数据交换,如果要想避免同时读写发生的同步问题就得需要其他机制!
在实际编程中,常用的同步机制有 信号量、传递消息(使用管道或IPC消息)、生成信号。
但是在这次的实现里面我们用自己提供的非常丑陋的同步标志written_by_you,它是一个非常缺乏效率的忙等待(不停地循环)。
头文件
首先是头文件
//shm_com.h
#define TEXT_SZ 2048
struct shared_use_st {
int written_by_you; //用来通知消费者
char some_text[TEXT_SZ]; //需要传输的文本长度为2K是由我们随意决定的
};
消费者文件
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h> //相关的主要函数
#include "shm_com.h"
int main() {
int running = 1;
void *shared_memory = (void*)0; //指向整个共享内存的起点
struct shared_use_st *shared_stuff; //具体的一条消息即结构体
int shmid; //共享内存标识符,不同的进程通过shmget返回的不同,但是shmget的第一个参数都是相同的!
srand((unsigned int)getpid());
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT); //指定 键 、 共享内存大小 、 权限和创建策略
if (shmid == -1) {
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
//消费者访问内存 (即将共享内存的地址映射到自己进程)
//**第二个参数指定的是共享内存连接到当前进程中的地址位置,它通常是一个空指针,表示让系统来选择共享内存出现的地址。**
//第三个参数是一组标志。它的两个的可能取值为SHM_RND(这个标志与第二个参数联合使用用来控制共享内存连接的地址)和SHM_RDONLY(它使得连接的 内存只读)。
shared_memory = shmat(shmid, (void*)0, 0);
if (shared_memory == (void*)-1) {
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("memory attached at %X\n", (int)shared_memory);
//程序的下一部分将shared_memory分配给shared_stuff,然后它输出written_by_you中的文本
//循环将一支执行到在written_by_you中找到end字符为止。
//sleep调用强迫消费者程序在临界区多呆一会,让生产者程序等待
shared_stuff = (struct shared_use_st*)shared_memory;
shared_stuff->written_by_you = 0;
while (running) {
if (shared_stuff->written_by_you) {
printf("you wrote %s", shared_stuff->some_text);
sleep(rand() % 4);
shared_stuff->written_by_you = 0;
if (strncmp(shared_stuff->some_text, "end", 3) == 0) {
running = 0;
}
}
}
//shmdt函数 将共享内存分离,是shmat的反向操作
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
/*小课堂shmid_ds结构体*/
/*
struct shmid_ds{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
*/
//shmctl 共享内存的控制函数。
//第一个参数为 标识符
//第二个参数 为 命令:IPC_STAT(将shmid_ds结构中的数据设置为共享内的当前关联值)、IPC_SET(如果当前进程有足够的权限,就把共享内存的关联值设置为shmid_ds结构中给出的值)、IPC_RMID(删除共享内存段)
if (shmctl(shmid, IPC_RMID, 0) == -1) {
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
生产者文件
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shm_com.h"
int main() {
int running = 1;
void *shared_memory = (void*)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid;
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
if (shmid == -1) {
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
//生产者访问共享内存
shared_memory = shmat(shmid, (void*)0, 0);
if (shared_memory == (void*)-1) {
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("memory attached at %X\n", (int)shared_memory);
shared_stuff = (struct shared_use_st*)shared_memory;
while (running) {
while (shared_stuff->written_by_you == 1) {
sleep(1);
printf("waiting for client...\n");
}
printf("enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
shared_stuff->written_by_you = 1;
if (strncmp(buffer, "end", 3) == 0) {
running = 0;
}
}
//分离共享内存
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
效果展示
尽管使用的是同一块物理地址,在各个进程中的地址也不同 啊!