第三季-第18课-共享内存通讯
第18课-共享内存通讯
18.1 基本概念
共享内存是IPC机制中的一种. 顾名思义,它允许两个不相关的进程访问同一段内存,这是传递数据的一种非常有效的方式。即,可以让进程A和B同时访问一段内存。
18.2 函数学习
1. 创建/获取共享内存
(1)函数名
shmget
(2)函数原型
int shmget(key_t key, size_t size ,int shmflg);
(3)函数功能
创建或者获取共享内存,并返回其描述符。(allocates a shared memory segment)。
(4)所属头文件
#include<sys/ipc.h>
#include<sys/shm.h>
(5)返回值
成功:返回创建或者获取到的共享内存的描述符
失败:-1
(6)参数说明
key:共享内存的键值
size:共享内存的大小
shnflg:打开标志,如果使用了IPC_CREAT这个标志,就会新创建一个共享内存。
2. 映射共享内存
(1)函数名
shmat
(2)函数原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
(3)函数功能
把shmid指定的共享内存映射到进程的地址空间里。(shared memory operations)。
(4)所属头文件
#include<sys/types.h>
#include<sys/shm.h>
(5)返回值
成功:返回映射到进程空间之后的内存地址
失败:-1
(6)参数说明
shmid:要映射的共享内存的描述符
shmaddr:指定映射之后的地址,一般的情况下,改参数都是NULL,表明让linux系统自动的选择地址
shmflg:标志
3. 分离(脱离)共享内存
(1)函数名
shmdt
(2)函数原型
int shmdt(const void *shmaddr);
(3)函数功能
从进程地址空间中,断掉与共享内存的联系。(shared memory operations)。
(4)所属头文件
#include<sys/types.h>
#include<sys/shm.h>
(5)返回值
成功:0
失败:-1
(6)参数说明
shmaddr:要断开的共享内存的地址
4. 删除(控制)共享内存
(1)函数名
shmctl
(2)函数原型
int shmctl(int shmid, int cmd, sruct shmid_ds *buf);
(3)函数功能
控制共享内存(performs the control operation specified by cmd on the shared memory segment whose identifier is given in shmid)。
(4)所属头文件
#include<sys/ipc.h>
#include<sys/shm.h>
(5)返回值
成功:0
失败:-1
(6)参数说明
shmid:要控制的共享内存的id
cmd:决定执行什么样的控制操作,如IPC_RMID(表示删除)
内核为每一个共享存储段设置了一个shmid_ds结构,该结构如下:
struct shmid_ds {
struct ipc_perm shm_perm; /*see Section 15.6.2*/
size_t shm_segsz; /*size of segment in bytes*/
pid_t shm_lpid; /*pid of last shmop*/
shmatt_t shm_nattch; /*number of current attaches*/
time_t shm_atime; /*last-attach time*/
time_t shm_dtime; /*last-detach time*/
time_t shm_ctime; /*last-change time*/
(按照支持共享存储段的需要,每种实现会在shmid_ds结构中增加其他成员)
buf:获取linux中描述共享内存的shmid_ds结构。基本不使用。
18.3 综合实例
1. 流程:
A和B两个进程,A先创建一个共享内存,然后再映射该共享内存。对于B进程先获取共享内存,然后再映射该共享内存。当用完了该共享内存后,A和B进程再分别分离该共享内存。A进程去删除共享内存。
2. 程序解读
我们设置两个进程,一个write进程,一个read进程。前一个进程是读取键盘上的输入的字符串,并且把字符串保存在共享内存之中。后一个进程的作用是,在共享内存中读取字符串,并且打印它们。
write进程的流程:(1)创建共享内存;(2)映射共享内存;(3)获取用户输入,把字符串放入共享内存,循环来做直到用户终止;(4)脱离共享内存。
write.c:
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#define TEXT_SZ 2048
struct shared_use_st
{
int written_by_you;
char some_text[TEXT_SZ];
};
int main()
{
int running = 1; //定义循环标志
key_t key;
key = ftok("/root",3);
int shmid;
struct shared_use_st *shared_stuff;
char buffer[TEXT_SZ]; //fgets()函数用到的存储用的数组
//1.创建共享内存;
shmid = shmget(key, sizeof(struct shared_use_st),IPC_CREAT);
if(shmid == -1)
{
printf("creat share memory fail!\n");
exit(EXIT_FAILURE); //退出的标志
}
//2.映射共享内存.
shared_stuff = (struct shared_use_st *)shmat(shmid, NULL, 0); //强制转换
//3.循环
while(running)
{
//观看数据有没有被取走,没被取走一直等待
while(shared_stuff->written_by_you == 1)
{
sleep(1);
printf("wait read process!\n");
}
//3.1 获取用户输入
fgets(buffer,TEXT_SZ,stdin); // char *fgets(char *s, int szie FILE *stream);
//3.2把字符串放入共享内存
strncpy(shared_stuff->some_text,buffer,TEXT_SZ);
shared_stuff->written_by_you = 1;
if(strncmp(buffer,"end",3)==0)
{
running = 0;
}
}
//4.脱离共享内存
shmdt((const void *)shared_stuff); //强制转换
return 1;
}
read进程流程:(1)创建/获取共享内存;(2)映射共享内存;(3)循环,打印共享内存中的字符串,直到收到结束的通知;(4)脱离共享内存;(5)删除共享内存。
read.c
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#define TEXT_SZ 2048
struct shared_use_st
{
int written_by_you;
char some_text[TEXT_SZ];
};
int main()
{
int running = 1; //定义循环标志
int shmid;
key_t key;
key = ftok("/root",3);
struct shared_use_st *shared_stuff;
char buffer[TEXT_SZ];
//1.创建/获取共享内存;
shmid = shmget(key, sizeof(struct shared_use_st),IPC_CREAT);
//2.映射共享内存;
shared_stuff = (struct shared_use_st *)shmat(shmid, NULL, 0); //强制转换
shared_stuff->written_by_you = 0; //最初的标识是0;
//3.循环
while(running)
{
//打印共享内存中的字符串,直到收到结束的通知;
if(shared_stuff->written_by_you == 1)
{
printf("write process is %s\n",shared_stuff->some_text);
shared_stuff->written_by_you = 0;
if(strncmp(shared_stuff->some_text,"end",3)==0)
running = 0;
}
}
//4.脱离共享内存;
shmdt((const void *)shared_stuff); //强制转换
//5.删除共享内存。
shmctl(shmid,IPC_RMID,0);
return 1;
}
运行结果:在两个一样的终端中,先运行./read,在运行./write。我们在./write中输入的字符串会在./read中显示,直到输入end,两个程序都停止。