mmxingye

导航

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);
}

效果展示

尽管使用的是同一块物理地址,在各个进程中的地址也不同 啊!

posted on 2022-04-20 15:35  独立树  阅读(55)  评论(0编辑  收藏  举报