进程间的通信之共享内存

一、共享内存        

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间,这一块空间是内核专门提供给多个进程交换信息的。但是内核并没有实现对这个内存进行同步,必然需要某种同步机制,互斥锁和信号量都可以。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝

二、函数原型

1.创建/打开共享内存

  
 int shmget(key_t key,  int size,  int shmflag ); 
 

key:IPC_PRIVATE或ftok的返回值

size: 共享内存的大小

扫描二维码关注公众号,回复: 1683662 查看本文章

shmflag: 共享内存的权限,同open函数

返回值: 成功:共享内存段标识符  出错:-1

注意:这个key值主要是针对不同的进程如何去获取所要的共享内存

IPC_PRIVATE:这个的默认值是0,这样会导致不同进程(无血缘的)之间无法去获取同一块内存id,在有血缘进程间还是可以使用的,但是必须在fork函数之前使用。ftok返回:key_t ftok(const char *fname, int proj_id);fname 随便一个已经存在的文件, proj_id 可以任意取值,但是必须在0-255.key还可以自己任意指定一个没有用过的关联值。用ipcs查看已经用过的key值。

shmflag:加上IPC_CREAT这个flag,例如0666 | IPC_CREAT。因为不同进程获取同一块内存id,第一个执行的进程是创建,后面执行的进程都是打开。

2.映射共享内存,把指定的共享内存映射到进程地址空间用于访问

void* shmat(int shmid, const void * shmaddr, int shmflag);

shmid: 共享内存标识符,即上一个函数的成功返回值

shmaddr: 将共享内存映射到指定地址,NULL 系统自动完成映射

shmflag: 对该内存的读写权限,一般为0,可以读写, SHM_RDONLY 只读

返回值: 成功:映射后的地址  出错:-1

注意:shmaddr可以为字符数组,结构体, 也可以为NULL,成功之后再把返回的地址赋给某一个变量。

3.撤销共享内存映射

int shmdt(const void * shmaddr);

shmaddr:共享内存映射后的地址

返回值:成功:0   出错:-1

注意:这个只是让原本指向这块共享内存的地址不指向这里,并没有删除这块内存

4.控制共享内存

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

shmid: 共享内存标识符

cmd:IPC_STAT(获取对象属性)

    IPC_SET(获取对象属性)

    IPC_RMID(删除对象)

buf:指定IPC_STAT/IPC_SET时用以保存/设置属性

返回值:成功: 0  出错: -1

注意:只要有一个进程进行删除就行

三、代码示例

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

#define BUF_SIZE 1024

int p(int semid)    
{    
    struct sembuf sem_p;    
    sem_p.sem_num = 0;    
    sem_p.sem_op = -1;
	sem_p.sem_flg = SEM_UNDO;    
    if(semop(semid, &sem_p, 1) == -1)    
    {
		perror("semop failed!");
		exit(1);
	}
	return 0;
}    
        
int v(int semid)    
{    
    struct sembuf sem_v;    
    sem_v.sem_num = 0;    
    sem_v.sem_op = 1;
	sem_v.sem_flg = SEM_UNDO;    
    if(semop(semid, &sem_v, 1) == -1)    
	{
		perror("semop failed!");
		exit(1);
	}
	
	return 0;
} 

int main(int argc, char* argv[])
{
	void* shmaddr;
	char* buf;
	int shm_id;
	int sem_write;
	int sem_read;
	
    if (-1 == (sem_write = semget((key_t)1245, 1, 0666 | IPC_CREAT)))
	{
		perror("semget failed!");
		exit(1);
	}
	if (-1 == (sem_read = semget((key_t)1243, 1, 0666 | IPC_CREAT)))
	{
		perror("semget failed!");
		exit(1);
	}
	if (semctl(sem_write, 0, SETVAL, 1) < 0)
	{
		perror("semctl failed!");
		exit(1);
	}
	if (semctl(sem_read, 0, SETVAL, 0) < 0)
	{
		perror("semctl failed!");
		exit(1);
	}
	if (-1 == (shm_id = shmget((key_t)1211, BUF_SIZE, 0666 | IPC_CREAT)))
	{
		perror("shmat failed!");
		exit(1);
	}
	if ((void*)(-1) == (shmaddr = shmat(shm_id, NULL, 0)))
	{
		perror("shmat failed!");
		exit(1);
	}
	buf = (char*)shmaddr;
	
	while(1)
	{
		p(sem_write);
		printf("write buf: ");
		fgets(buf, BUF_SIZE, stdin);
		buf[strlen(buf) - 1] = '\0'; //Remove the newline, Plus the end character
		v(sem_read);
		if (strncmp(buf, "bye", 3) == 0)
		{

			break;
		}
		usleep(500);
	}
	
	if (shmdt(shmaddr) < 0)
    {
		perror("shmdt failed!");
		exit(1);
	}
	if (shmctl(shm_id, IPC_RMID, NULL) < 0)
	{
		perror("shmctl failed!");
		exit(1);
	}
	#if 0
	if (semctl(sem_read, 0, IPC_RMID, 0) < 0)
	{
		perror("semctl failed!");
		exit(1);
	}
	if (semctl(sem_write, 0, IPC_RMID, 0) < 0)
	{
		perror("semctl failed!");
		exit(1);
	}
	#endif		
	return 0;
}
shm_read.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<unistd.h>
#include<string.h>
#include<sys/sem.h>

#define BUF_SIZE 1024

int p(int semid)    
{    
    struct sembuf sem_p;    
    sem_p.sem_num = 0;    
    sem_p.sem_op = -1;
	sem_p.sem_flg = SEM_UNDO;    
    if(semop(semid, &sem_p, 1) == -1)    
    {
		perror("semop failed!");
		exit(1);
	}
	return 0;
}    
        
int v(int semid)    
{    
    struct sembuf sem_v;    
    sem_v.sem_num = 0;    
    sem_v.sem_op = 1;
	sem_v.sem_flg = SEM_UNDO;    
    if(semop(semid, &sem_v, 1) == -1)    
	{
		perror("semop failed!");
		exit(1);
	}
	
	return 0;
}   

int main(int argc, char* argv[])
{
	void* shmaddr;
	char* buf;
	int shm_id;
	int sem_read;
	int sem_write;
	
    if (-1 == (sem_write = semget((key_t)1245, 1, 0666 | IPC_CREAT)))
	{
		perror("semget failed!");
		exit(1);
	}
	if (-1 == (sem_read = semget((key_t)1243, 1, 0666 | IPC_CREAT)))
	{
		perror("semget failed!");
		exit(1);
	}
	
	if (semctl(sem_write, 0, SETVAL, 1) < 0)
	{
		perror("semctl failed!");
		exit(1);
	}
	if (semctl(sem_read, 0, SETVAL, 0) < 0)
	{
		perror("semctl failed!");
		exit(1);
	}
	
	if (-1 == (shm_id = shmget((key_t)1211, BUF_SIZE, 0666 | IPC_CREAT)))
	{
		perror("shmat failed!");
		exit(1);
	}
	if ((void*)(-1) == (shmaddr = shmat(shm_id, NULL, 0)))
	{
		perror("shmat failed!");
		exit(1);
	}
	buf = (char*)shmaddr;
	
	while(1)
	{
		p(sem_read);
		printf("read buf: %s\n", buf);
	
		if (strncmp(buf, "bye", 3) == 0)
		{
			break;
		}
		else
		{
		    v(sem_write);
		}
		
	}
	
	if (shmdt(shmaddr) < 0)
    {
		perror("shmdt failed!");
		exit(1);
	}
				
	return 0;
}

这里面用到2个system v信号量,用1个信号量很容易在一个进程里面循环读取缓冲区的数据,我本来是用一个信号量,再加上sleep函数切换2个进程cpu的控制权。但是睡眠时间难以把控,而且影响性能。还有一个#if 0注释掉的一段信号量删除代码,也有点问题。希望有大神指正一下。




    


猜你喜欢

转载自blog.csdn.net/follow_blast/article/details/76060530