进程学习:进程间通信(system v IPC)2.共享内存

本文详细介绍了共享内存的概念及其与消息队列、管道的区别,并通过四个关键函数shmget、shmat、shmdt、shmctl讲解了如何创建、映射、解除映射及控制共享内存。最后,通过一个编程实例演示了如何在两个终端间使用共享内存进行通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

共享内存与消息队列、管道的区别:


1.消息队列、管道中的数据读取之后就没有了;而共享内存中的数据无论读取多少次,都还会在里面;

2.共享内存读取相对于消息队列与管道来说,效率最高,直接对指向共享内存的指针进行读写操作;


共享内存函数

共享内存函数

一、int  shmget(key_t key, size_t size, int shmflg);

功能:创建或打开共享内存

参数:

          key:键值;

          size:要创建的共享内存的字节数;

          shmflg:权限(IPC_CREAT | IPC_EXCL|0664)

返回值:

          成功:返回shmid;

          失败:返回 -1;


二、void *shmat(int  shmid, const void *shmaddr, int shmflg);

功能:映射共享内存,即把指定的共享内存映射到进程的地址空间,用于访问;

参数:

          shmid:要映射的共享内存的id号;

          shmaddr:共享内存地址的选择方式:1.NULL,系统帮你选一块不碍事儿的地方;2.你自己选,一般不自己选;

          shmflg:读写权限:1.SHM_RDONLY,即对共享内存只进行读操作;2.0,可读可写操作;

返回值:

          成功:返回完成映射之后的共享内存地址;

          失败:返回(void  *)-1;


三、int  shmdt(const void *shmaddr);

功能:解除当前进程与共享内存的映射关系;

参数:shmaddr:shmat的返回值,共享内存的地址;

返回值:

          成功:0

          失败:-1


四、int shmctl(int shmid, int  cmd, struct shmid_ds *buf);

功能:对共享内存进行各种操作;

参数:

          shmid:共享内存的id号;

          cmd:命令:

                           IPC_STAT:获取shmid的属性信息,存放在第三个参数内;

                           IPC_SET:设置shmid的属性,要设置的属性放在第三个参数里;

                           IPC_RMID:删除共享内存,此时第三个参数为NULL;

返回值:

           成功:0;

           失败:-1;


编程实例:

下面我们来编写一个测试代码:从一个terminal输入到共享内存,另一个terminal从共享内存读取并输出打印;

在终端输入到共享内存:

#include "sem.h"

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;
	char *p = NULL;
	char buf[N];

	if( (key = ftok(".", 1)) < 0)
	{
		perror("ftok error");
		exit(1);
	}

	if( (shmid = shmget(key, 512, IPC_CREAT|IPC_EXCL|0664)) < 0)
	{
		if(errno == EEXIST)
		{
			shmid = shmget(key, 512, 0664);
			puts("share buffer is exist");
		}
		else
		{
			perror("shmget error");
			exit(1);
		}
	}

	p = (char *)shmat(shmid, NULL, 0);
	if(p == (char *)-1)
	{
		perror("shmat error");
		exit(1);
	}

	while(fgets(p, N, stdin) != NULL)
	{
		if(strncmp(p, "quit", 4) == 0)
		{
			break;
		}
	}

	shmdt(p);


	return 0;
}

从共享内存读取数据并打印到终端:
#include "sem.h"

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;
	char *p = NULL;
	char buf[N];

	if( (key = ftok(".", 1)) < 0)
	{
		perror("ftok error");
		exit(1);
	}

	if( (shmid = shmget(key, 512, IPC_CREAT|IPC_EXCL|0664)) < 0)
	{
		if(errno == EEXIST)
		{
			shmid = shmget(key, 512, 0664);
			puts("share buffer is exist");
		}
		else
		{
			perror("shmget error");
			exit(1);
		}
	}

	p = (char *)shmat(shmid, NULL, 0);
	if(p == (char *)-1)
	{
		perror("shmat error");
		exit(1);
	}

	while(1)
	{
		if(strncmp(p, "quit", 4) == 0)
		{
			break;
		}

		fputs(p, stdout);
	}

	shmdt(p);

	shmctl(shmid, IPC_RMID, NULL);

	return 0;
}




我们运行程序后发现,负责读取的终端会一直进行输出,刷屏了。这是因为共享内存里的数据不会因为对他进行了读取后就把里面的数据清空。

回想无名管道、有名管道、消息队列,都是读取后里面的东东就没有了。同时那三个是在内核空间进行的通信,这个不是在内核空间进行的通信;

要想实现一端输入,另一端读取(只读取一遍,不刷屏),我们还有结合信号灯的使用,让这个共享内存有个先后执行的顺序(也就是同步)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值