共享内存

标签: 信号量  共享内存  进程间通信

一、定义

  共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式。共享内存的具体实现是不同进程共享的内存安排为同一段物理地址。

    如上图所示,进程A和进程B共享同一块物理内存,共享内存中的数据进程A和进程B均可进行相关操作,这样便可达到两个进程之间数据传递的目的。

二、相关函数

1、shm函数(创建共享内存)

函数声明:

int shmget(key_t ey,size_t size, int shmflg);
第一个参数key:为共享内存段命名,有一个特殊的键值IPC_PRIVATE,用于创建一个只属于创建进程的共享内存
第二个参数size:是共享内存的大小(字节数)
第三个参数shmflg:包含9个比特的权限标志,作用同文件操作是的mode标志位相同
返回值:函数执行成功返回一个非负整数,即内存标识符,失败时返回-1.

2、shmat函数(将共享内存连接到进程)

   第一次创建共享内存段,该共享内存段并不能被任何进程所使用,只有当共享内存被连接到一个进程,才可以访问共享内存,这个功能由shmat函数实现。

函数声明:

void *shmat(int shm_id, const oid *shm_addr, int shmflg);
第一个参数:所要连接的共享内存标识符。
第二个参数:指定共享内存连接到当前进程的地址位置,一般传NULL,表示由系统选择共享内存连接的位置。
第三个参数:可取SHM_RND和SHM_RDONLY(使得连接的内存地址只读)
返回值:函数执行成功返回共享内存的首地址,失败返回-1

3、shmdt函数(分离共享内存)

该函数的参数是shmat函数返回的共享内存地址指针,成功返回0,失败返回-1.

4、shmctl(控制函数)

函数声明:

int shmctl(int shm_id, int command, struct shmid_ds *buf);
第一个参数:共享内存标识符
第二个参数://要采取的动作,有如下取值:
             IPC_STAT:把shm_ds结构中的数据设置为共享内存的当前关联值
             IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构体中的值
             IPC_RMID:删除共享内存。
第三个参数:buf是一个指针,指向包含共享内存模式和访问权限的结构
返回值:成功返回0失败返回-1.
struct shmid_ds
{
    uid_t shm_perm.uid;   
    uid_t shm_perm.gid;    
    mode_t shm_perm.mode;    
}

三、实例

   共享内存和信号量同时使用会使得程序效率更加显著,以下代码实现了一个进程往共享内存里输入数据,并一个进程从共享内存里读取数据,并且只有当写进程写入之后读进程才可读取,不可重复读取相同的数据。

sem.h

# include<unistd.h>
# include<sys/sem.h>

union semun
{
	int val;
};

void sem_init();
void sem_p();
void sem_v();
void sem_destory();

sem.c

# include<stdio.h>
# include<error.h>
# include"sem.h"

static int semid = -1;

void sem_init()
{
	semid = semget((key_t)1234,1,IPC_CREAT | IPC_EXCL | 0600);
	if(semid == -1)
	{
		semid = semget((key_t)1234, 1, 0600);
		if(semid == -1)
		{
			perror("semget error\n");
		}
	}
	else
	{
		union semun a;
		a.val = 0;

		if(semctl(semid,0,SETVAL,a) == -1)
		{
			perror("semctl setval error\n");
		}
	}
}
void sem_p()
{
	struct sembuf buf;
	buf.sem_num = 0;
	buf.sem_op = -1;
	buf.sem_flg = SEM_UNDO;

	if(semop(semid, &buf, 1) == -1)
	{
		perror("semop seeoe\n");
	}
}
void sem_v()	
{
	struct sembuf buf;
	buf.sem_num = 0;
	buf.sem_op = 1;
	buf.sem_flg = SEM_UNDO;

	if(semop(semid, &buf, 1) == -1)
	{
		perror("semop seeoe\n");
	}
}
void sem_destory()
{
	if(semctl(semid,0,IPC_RMID) == -1)
	{
		perror("semctl error\n");
	}
}

shmwrite.c

# include<stdio.h>
# include<stdlib.h>
# include<assert.h>
# include<sys/shm.h>
# include<string.h>
# include"sem.h"

int main()
{
	sem_init();
	int shmid = shmget((key_t)1234, 256,IPC_CREAT|0600);
	assert(shmid != -1);

	char *s = (char *)shmat(shmid, NULL, 0);

	while(1)
	{
		char buf[128] = {0};
		printf("input:\n");
		fgets(buf, 128, stdin);
		strcpy(s,buf);
        sem_v();
		if(strncmp(buf, "end", 3) == 0)
		{
			break;
		}
	}
	shmdt(s);
	exit(0);
}

shmread.c

# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<sys/shm.h>
# include<assert.h>
# include"sem.h"

int main()
{
	sem_init();
	int shmid = shmget((key_t)1234, 256, IPC_CREAT | 0600);
	assert(shmid != -1);
	char *p = (char *)shmat(shmid,NULL,0);

	while(1)
	{
        sem_p();
		if(strncmp(p,"end",3) == 0)
		{
			break;
		}
		printf("%s\n",p);
		sleep(1);
	}
	shmdt(p);
	sem_destory();
	shmctl(shmid, IPC_RMID, NULL);
	exit(0);
}

 

原文链接:加载失败,请重新获取