首页 > 技术文章 > 【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 共享内存 分类: Linux --- 应用程序设计 2014-11-15 18:25 55人阅读 评论(0) 收藏

gengzj 2014-11-15 18:25 原文


【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 共享内存


共享内存IPC原理

共享内存进程间通信机制主要用于实现进程间大量的数据传输。


共享内存是在内存中单独开辟一段内存空间,这段内存空间有自己特有的数据结构,包括访问权限、大小和最近访问时间等信息。该数据结构定义如下:


在使用共享内存进行数据存取是,有必要使用二元信号量来同步两个进程,以实现对共享内存的写操作。

共享内存与管道的对比

Linux共享内存管理

1. 创建共享内存


2. 共享内存控制


3. 映射共享内存

int shmat(int shmid,char *shmaddr,int flag)
参数:
shmid:shmget函数返回的共享存储标识符
flag:决定以什么样的方式来确定映射的地址(通常为0)
返回值:
如果成功,则返回共享内存映射到进程中的地址;如果失败,则返回-1。

4. 分离共享内存对象的映射

当一个进程不再需要共享内存时,需要把它从进程地址空间中多里。
int shmdt(char *shmaddr)

共享内存的权限管理示例

#include<sys/shm.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/ipc.h>
#include<string.h>

int main(int argc,char *argv[])
{
	key_t key;
	int shm_id;
	char *ptr;
	key=ftok("/",10);
	
	shm_id=shmget(key,100,IPC_CREAT|SHM_R);
	
	printf("get the shm id is %d\n",shm_id);

	if ((ptr = (char *)shmat(shm_id, NULL, SHM_RDONLY)) == NULL)
	{
		if (shmctl(shm_id, IPC_RMID, NULL) == -1)
			perror("Failed to  remove memory segment");
		exit(EXIT_FAILURE);
	}

	printf("in yangzd the attach add is %p\n",ptr);


	printf("*ptr=%c\n",*ptr);
	printf("now ,try to write the memory\n");
	*ptr='d';
	printf("*ptr=%c\n",*ptr);
	
	shmdt(ptr);
	shmctl(shm_id,IPC_RMID,0);
}
运行结果(因为是SHM_RDONLY,所以普通用户不能进行写操作,会出现段错误):

$ ./shmat_rd_flag_regular 
get the shm id is 32769
in yangzd the attach add is 0xb7769000
*ptr=
now ,try to write the memory
段错误 (核心已转储)

共享内存处理应用示例

发送端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#include <string.h>
int main(int argc,char *argv[])
{
	int running=1;
	int shid;
	int semid;
	int value;
	void *sharem=NULL;

	struct sembuf  sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_flg = SEM_UNDO;
	
	if((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1)
	{
		perror("semget");
		exit(EXIT_FAILURE);
	}
	
	if (semctl(semid, 0, SETVAL, 0) == -1)
	{
		printf("sem init error");
        if(semctl(semid,0,IPC_RMID,0)!=0)
		{
			perror("semctl");
			exit(EXIT_FAILURE);
		}
	exit(EXIT_FAILURE);	
	}
	shid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);
	if(shid==-1)
	{
		perror("shmget");
		exit(EXIT_FAILURE);
	}

	
	sharem=shmat(shid,NULL,0);
	if(sharem==NULL)
	{
		perror("shmat");
		exit(EXIT_FAILURE);
	}
	
	while(running)
	{
		if((value=semctl( semid, 0, GETVAL ))==0)
		{	
			printf("write data operate\n");
			printf("please input something:");
			scanf("%s",sharem);
			sem_b.sem_op = 1;
			if (semop(semid, &sem_b, 1) == -1) 
			{
				fprintf(stderr, "semaphore_p failed\n");
				exit(EXIT_FAILURE);
			}
		}
		if(strcmp(sharem,"end")==0)
			running--;
	}
		shmdt(sharem);
		return 0;
}
接收端:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

int main(int argc,char *argv[])
{
	int running=1;
	char *shm_p=NULL;
	int shmid;	
	int semid;
	int value;

	struct sembuf  sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_flg = SEM_UNDO;
				
	if((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1)
	{
		perror("semget");
		exit(EXIT_FAILURE);
	}
	shmid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);
	if(shmid==-1)
	{
		perror("shmget");
		exit(EXIT_FAILURE);
	}
	shm_p=shmat(shmid,NULL,0);
	if(shm_p==NULL)
	{
		perror("shmat");
		exit(EXIT_FAILURE);
	}
	
	while(running)
	{
		if((value=semctl( semid, 0, GETVAL ))==1)
		{
			printf("read data operate\n");
			sem_b.sem_op = -1;
			if (semop(semid, &sem_b, 1) == -1) 
			{
				fprintf(stderr, "semaphore_p failed\n");
				exit(EXIT_FAILURE);
			}
			printf("%s\n",shm_p);
		}
		if(strcmp(shm_p,"end")==0)
			running--;
	}
	shmdt(shm_p);
	if(shmctl(shmid,IPC_RMID,0)!=0)
	{
		perror("shmctl");
		exit(EXIT_FAILURE);
	}

	if(semctl(semid,0,IPC_RMID,0)!=0)
	{
		perror("semctl");
		exit(EXIT_FAILURE);
	}
	return 0;
}
发送端运行结果:

$ ./shm_sem_example_send 
write data operate
please input something:hello
write data operate
please input something:world
write data operate
please input something:end
接收端运行结果:

$ ./shm_sem_example_recv 
read data operate
hello
read data operate
world

运行过程中在另一终端中用“ipcs”命令可以查看使用的共享内存和信号量信息

$ ipcs

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     nattch     状态      
0x00000000 0          yang       600        393216     2          目标       
0x0a010002 32769      yang       400        100        0                       
0x0009fbf1 98306      yang       600        2048       2                       

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0x0001e240 32768      yang       666        1         

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      

原文链接:

http://blog.csdn.net/geng823/article/details/41147911

版权声明:本文为博主原创文章,未经博主允许不得转载。

推荐阅读