目录
- 共享内存的概念
- 相关函数
- shmread.c, shmwrite.c
- 输出
(一)共享内存的概念
- 共享内存是进程间通信中最简单的方式之一
- 具有最高的效率,控制不好容易出乱子
- 内核没有对访问内存进行同步,所以要自己提供同步的方法
(二)相关函数
(1)新建共享内存
//allocates a shared memory segment,相当于open函数返回的句柄
int shmget(key_t key, size_t size, int shmflg);
参数分析:
- key:共享内存关联的标识符,本质是整数,可以弄个整数强制转化下就可以了,如(key_t)1234
- size: 新建立的内存大小
- shmflg:权限+创建(如果不存在则创建,存在则打开)
IPC_CREAT:如果没有此共享内存则创建它
当IPC_EXCL和IPC_CREAT一起使用时,如果共享内存已经存在,则失败。 - 返回值:A valid segment identifier-shmid, is returned on success, -1 on error.
注1: 我们经常使用的两个组合:
IPC_CREAT|IPC_EXCL|0666 和 IPC_CREAT|0666
- IPC_CREAT|IPC_EXCL|0666:如果没有这个信号则创建一个, 如果已经存在则产生一个存在错误
- IPC_CREAT|0666:没有创建信号,有则返回已经存在的信号 ID
**注2:**共享内存需要用户自己定义一个自己的小协议
- 下面代码定义了一个结构体,就代表我们将要申请内存的数据结构
- 自己分配了2K大小
- 并且定义了一个协议,write = 1-----读(非零都可以哈,因为下面就是两个程序咯); write = 0-----写
#ifndef _SHAM_H
#define _SHAM_H
#define TEST_SZ 2048 //内存大小为2K
//其中write是一个小协议哈!一个读进程,一个写进程,看见write是0,那么写进程就去写了,读进程不会去抢,也抢不到,反之亦然
struct share_used_st
{
int write; //write = 1-----读; write = 0-----写
char text[TEST_SZ]; //共享内存内容储存
};
#endif
(2)链接写进程到当前共享内存地址空间
//attaches the shared memory segment identified by shmid to the address space of the calling proces
void *shmat(int shmid, const void *shmaddr, int shmflg);
- shmid: shmget返回的id
- shmaddr指定共享内存出现在进程内存地址的什么位置,直接指定为NULL,让内核自己决定一个合适的地址位置,当然如果不为0,很麻烦,要页对齐。这种分配内存还是交给操作系统处理,不是重点,继续。。
- shmflg:只有两种。一种只读,另外种读写方式,没有只写的共享内存
SHM_RDONLY:为只读模式,否则为读写!
//头文件/usr/include/bits/shm.h
#define SHM_RDONLY 010000 /* attach read-only else read-write */
- 返回共享的内存地址,否则返回-1
(3)共享内存从当前进程中分离
int shmdt(const void *shmaddr);
参数分析:
- shmaddr:连接的共享内存的起始地址
- 返回值:On success shmdt() returns 0; on error -1 is returned
(4)删除共享内存
//共享内存的控制函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数分析:
- shmid:shmget返回的id
- cmd:和之前的差不多,不说了。下面是删除命令
IPC_RMID:删除这片共享内存
IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
- buf: pointer to a shmid_ds structure, 没用到,不关心了,况且在命令IPC_SET、IPC_STAT中提及了,删除命令没有提及buf,给空就行了。
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
(5)shmctl和shmdt区别
- shmdt:断开链接的共享内存指针,并不是真正意义上的删除,把相关shmid_ds结构的 shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段,举个例子,有10个进程共享一段内存,一个进程走了,还剩9个,以此类推。。直到0
- shmctl:不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生在最后一个进程离开这个共享段时。
(三)shmwrite.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shmdata.h"
#include <string.h>
#include <unistd.h>
/****************************************************************************************
** 共享内存
** shmget():新建共享内存
** int shmget(key_t key, size_t size, int shmflg);
** key :共享内存关联的标识符
** size :新建立的内存大小
** shmflg:权限+创建(如果不存在则创建,存在则打开)
** RETURN VALUE
** A valid segment identifier-shmid, is returned on success, -1 on error.
** shmat():链接写进程到当前共享内存地址空间
** void *shmat(int shmid, const void *shmaddr, int shmflg);
** shmid :shmget返回的id
** shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL,让内核自己决定一个合适的地址位置
** shmflg :一种只读,另外种读写方式,没有只写的共享内存
** RETURN VALUE
** 返回共享的内存地址,否则返回-1
** shmdt():共享内存从当前进程中分离
** int shmdt(const void *shmaddr);
** shmaddr:连接的共享内存的起始地址
** RETURN VALUE
** On success shmdt() returns 0; on error -1 is returned
****************************************************************************************/
int main()
{
int running = 1;
int shmid; //shmget返回的标识符
void *shm = NULL;
struct share_used_st *share = NULL;
char buffer[BUFSIZ+1]; //用于保存输入的文本
//创建内存对象
shmid = shmget((key_t)1234, sizeof(struct share_used_st), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "Creart a memory object error!!!\n");
exit(EXIT_FAILURE);
}
//链接进程与当前共享内存地址空间--返回值是NULL
shm = shmat(shmid, 0, 0); //前面0是让OS自动分配地址,后面0是除(#define SHM_RDONLY 010000)都为读写方式
if(shm == (void *)-1) //强制转化为指针0xFFF...,方便移植
{
fprintf(stderr,"shmat failed!!!\n");
exit(EXIT_FAILURE);
}
printf("Memory attched at %p!!!\n",shm); //输出内存起始地址
share = (struct share_used_st *)shm; //把其转化为结构体是为了,实现小协议,对write标识位操作
while(running)
{
if(share->write == 1) //按照自己规定的协议,为0才是写,为1只能等待
{
sleep(1);
printf("Wating .....\n");
}
printf("Please enter some data:\n"); //输入字符
fgets(buffer, BUFSIZ, stdin); //获取输入的字符,BUFSIZ是系统常量,为8012
strncpy(share->text, buffer, TEST_SZ); //复制到结构体的text数组中
//写完数据,设置共性内存可读
share->write = 1; //写完。置标志位为读
//输入了end,退出循环(程序)
if(strncmp(buffer, "end", 3) == 0)
{
running = 0;
}
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "Process seperates from memory error!!!\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
(四)shmread.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shmdata.h"
#include <string.h>
#include <unistd.h>
/****************************************************************************************
** 共享内存
** shmget():新建共享内存
** int shmget(key_t key, size_t size, int shmflg);
** key :共享内存关联的标识符
** size :新建立的内存大小
** shmflg:权限+创建(如果不存在则创建,存在则打开)
** RETURN VALUE
** A valid segment identifier-shmid, is returned on success, -1 on error.
** shmat():链接写进程到当前共享内存地址空间
** void *shmat(int shmid, const void *shmaddr, int shmflg);
** shmid :shmget返回的id
** shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL,让内核自己决定一个合适的地址位置
** shmflg :一种只读,另外种读写方式,没有只写的共享内存
** RETURN VALUE
** 返回共享的内存地址,否则返回-1
** shmdt():共享内存从当前进程中分离
** int shmdt(const void *shmaddr);
** shmaddr:连接的共享内存的起始地址
** RETURN VALUE
** On success shmdt() returns 0; on error -1 is returned
** shmctl():删除共享内存
** int shmctl(int shmid, int cmd, struct shmid_ds *buf);
** shmid :shmget返回的id
** cmd :IPC_RMID:删除这片共享内存
** buf :pointer to a shmid_ds structure
** RETURN VALUE
** On success shmdt() returns 0; on error -1 is returned
****************************************************************************************/
int main()
{
int running = 1;
int shmid; //shmget返回的标识符
void *shm = NULL;
struct share_used_st *share = NULL;
//创建内存对象
shmid = shmget((key_t)1234, sizeof(struct share_used_st), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "Creart a memory object error!!!\n");
exit(EXIT_FAILURE);
}
//链接进程与当前地址空间
shm = shmat(shmid, 0, 0); //前面0是让OS自动分配地址,后面0是除(#define SHM_RDONLY 010000)都为读写方式
if(shm == (void *)-1) //强制转化为指针0xFFF...,方便移植
{
fprintf(stderr,"shmat failed!!!\n");
exit(EXIT_FAILURE);
}
printf("Memory attched at %p!!!\n",shm); //输出内存起始地址
//设置内存共享,并设置可写
share = (struct share_used_st *)shm; //把其转化为结构体是为了,实现小协议,对write标识位操作
//这里解释下,为什么这里在while前面把标志位置写?实际中,read会在后台先运行,这样的话后台的读-等待,写直接进入写数据,这个操作对两个进程都是一样的,即两个进程看到的表示都一样,都共享了啊
share->write = 0; //按照自己规定的协议,为0才是写,为1只能等待
while(running)
{
if(share->write != 0) //读数据
{
printf("You just wrote: %s\n",share->text);
sleep(rand()%3);
//读取完数据,设置written使共享内存段可写
share->write = 0; //不然就写不进去了
if(strncmp(share->text, "end", 3) == 0)
{
running = 0;
}
}
else
sleep(1);
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "Process seperates from memory error!!!\n");
exit(EXIT_FAILURE);
}
//将共享内存释放
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "Freeing memory error!!!\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}