文章目录

  • 0.linux共享内存相关操作命令
    • 1)ipcs用法
    • 2)ipcrm用法
  • 1.共享内存的通信原理
    • 0)特点
    • 1)定义:
    • 2)原理:
    • 3)特别提醒:
    • 4)原理图:
    • 5)补充
  • 2.为什么共享内存速度最快
    • 1)原理
    • 2)步骤
    • 3)为什么速度快?
  • 3.共享内存的API
    • 3.0 相关头文件
    • 3.1.linux查看系统中的共享存储段指令
    • 3.2.linux删除系统中的共享存储段指令
    • 3.3 int shmget(key_t key, size_t size, int shmflg);创建共享内存或若共享内存已经存在,则打开共享内存
    • 3.4 void *shmat(int shmid, const void *shmaddr, int shmflg);:挂接共享内存,启动对该共享内存的访问,并把共享内存连接到进程的地址空间
    • 3.5 int shmdt(const void *shmaddr);和当前进程分离
    • 3.6 int shmctl(int shmid, int cmd, struct shmid_ds *buf);用来控制共享内存的各种操作
  • 4.模拟共享内存
  • 5.使用共享内存完成进程间通信
  • 6.总结
  • 7.共享内存使用流程(怎么用共享内存?)
    • 1)向内核申请一块内存,并指定内存有多大(这块内存属于内核)
    • 2)如果有两个进程需要通信,可以使用共享内存完成,先创建两个进程
    • 3)进程A和进程B分别和这块共享内存关联,那这两个进程就拿到了共享内存的地址-》首地址
    • 4)两个进程可以通过这个首地址对共享内存进行读/写操作
    • 5)如果这个进程不再使用这块共享内存,需要和共享内存断开关联(进程退出,对共享内存没影响)
    • 6)当不再使用共享内存时候,需要将共享内存销毁
  • 8.ftok函数:获得key_t的参数
  • 9.共享内存shm和映射区mmap的区别
    • 1)shm可以直接创建,mmap创建需要依赖磁盘文件(也就是fd套接字)
    • 2)shm效率更高
    • 3)内存共享区域不同
    • 4)数据安全性不同
    • 5)声明周期不同
  • 10.共享内存类的封装
    • 1)BasShm(父类)
    • 2)SecKey Shm存储密钥(子类)
    • 3)NodeSHMInfo每个节点就是一个结构体

0.linux共享内存相关操作命令

1)ipcs用法

ipcs  -a             //打印当前系统所有进程间通信方式的信息
ipcs  -m            //打印出使用共享内存进行进程间通信的信息
================以下仅为了解信息==================
ipcs   -q           //打印出使用消息队列进行进程间通信的信息
ipcs  -s                //打印出使用信号进行进程间通信的信息

2)ipcrm用法

ipcrm   -M  shmkey           //移除用shmkey创建的共享内存段
ipcrm  -m   shmid           //移除用shmid标识的消息队列
================以下仅为了解信息==================
ipcrm   -Q  msgkey          //移除用msgkey创建的消息队列
ipcrm  -q  msqid                //移除msqid标识的消息队列
ipcrm  -S  semkey           //移除semkey创建的信号
ipcrm  -s  semid                //移除用semid标识的信号

1.共享内存的通信原理

0)特点

最快的进程间通信;

1)定义:

允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。

2)原理:

不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

3)特别提醒:

共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量

4)原理图:

在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存

共享内存的通信原理示意图:

5)补充

对于一个共享内存,实现采用的是引用计数的原理,当进程脱离共享存储区后,计数器减一,挂架成功时,计数器加一,只有当计数器变为零时,才能被删除。当进程终止时,它所附加的共享存储区都会自动脱离。

2.为什么共享内存速度最快

1)原理

  • 借助上图说明:Proc A 进程给内存中写数据, Proc B 进程从内存中读取数据,在此期间一共发生了两次复制

2)步骤

(1)Proc A 到共享内存 (2)共享内存到 Proc B

3)为什么速度快?

因为直接在内存上操作,所以共享内存的速度也就提高了。

3.共享内存的API

3.0 相关头文件

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

3.1.linux查看系统中的共享存储段指令

ipcs -m

3.2.linux删除系统中的共享存储段指令

ipcrm -m [shmid]

3.3 int shmget(key_t key, size_t size, int shmflg);创建共享内存或若共享内存已经存在,则打开共享内存

int shmget(key_t key, size_t size, int shmflg);

  • 参数key:由ftok生成的key标识,标识系统的唯一IPC资源,记录共享内存在内核中的位置,需要是一个大于0的整数就行,随便一个数就行

  • 参数size:需要申请共享内存的大小。在操作系统中,申请内存的最小单位为页,一页是4k字节,为了避免内存碎片,我们一般申请的内存大小为页的整数倍。如果是打开一个已经存在的共享内存,size写0就行

  • 参数shmflg:类似open函数的flag,创建后打开填啥都行

-IPC_CREAT: 创建贡献内存
-IPC_CREAT | 0664 创建的时候给共享内存一个操作权限
-IPC_CREAT | IPC_EXCL :检测共享内存是否存在,存在则函数返回-1,不存在返回0

  • 返回值:成功时返回一个新建或已经存在的的共享内存标识符,取决于shmflg的参数。失败则返回-1并设置错误码。

3.4 void *shmat(int shmid, const void *shmaddr, int shmflg);:挂接共享内存,启动对该共享内存的访问,并把共享内存连接到进程的地址空间

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

  • [参数shmid]:共享存储段的标识符,shmid是由shmget()函数返回的共享内存标识(也就是返回值)。通过这个参数访问共享内存

  • [参数*shmaddr]:指定共享内存在内核中的位置;或写NULL,委托内核去指定。
    (shmaddr = 0,则存储段连接到由内核选择的第一个可以地址上(推荐使用)shmaddr指定共享内存连接到当前进程中的地址位置,通常是一个空指针,表示让系统来选择共享内存出现的地址)

  • [参数shmflg]:表示关联成功后对共享内存的操作权限
    (若指定了

SHM_RDONLY位,则以只读方式连接此段,否则以读写方式连接此段。
②shmflg是一组标志位,通常为0。一般很少需要控制共享内存连接的地址,通常都是让系统来选择一个地址,否则就会使应用程序对硬件的依赖性过高。)

  • 使用举例

void* ptr = shmat(shmid , NULL , 0);

  • [返回值]:成功返回共享存储段的地址(虚拟地址),并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数);出错返回(void*)-1。

3.5 int shmdt(const void *shmaddr);和当前进程分离

  • 当一个进程不需要共享内存的时候,就需要去关联。该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程。

  • [参数*shmaddr]:

参数shmaddr是shmat()函数返回的地址指针

  • [返回值]:

成功返回0,并将shmid_ds结构体中的 shm_nattch计数器减1;出错返回-1。

3.6 int shmctl(int shmid, int cmd, struct shmid_ds *buf);用来控制共享内存的各种操作

  • [参数shmid]:

共享存储段标识符,shmid是shmget()函数的返回值,共享内存标识符。

  • [参数cmd]:对共享内存的操作,设置为IPC_RMID时表示可以删除共享内存。

IPC_STAT:获取共享内存的状态
IPC_SET:如果进程有足够的权限,就可以设置共享内存状态
IPC_RMID:标记共享内存要被销毁,类似智能指针的引用计数,引用计数为零就把共享内存销毁,那传入的结构体可为空

  • [参数*buf]:传入的事shmid_ds这个结构体


struct ipc_perm shm_perm 共享内存的权限
size_t shm_segsz 共享内存的大小
time_t shm_atime 最后一次关联的时间
time_t shm_dtime 最后一次解除关联的时间
time_t shm_ctime 最后一次修改的时间
pid_t shm_cpid 表示共享内存是哪个进程创建出来
pid_t shm_lpid 表示哪个进程最后进程了shmat或shmdt
shmatt_t shm_nattch 有多少个进程和共享内存进行了关联操作

  • [返回值]:成功返回0,失败返回-1。
  • 效果:比如说读共享内存的状态,读出来后放在传入的结构体struct shmid_ds *buf里面

4.模拟共享内存

  • 用server来创建共享存储段,用client获取共享存储段的标识符,二者关联起来之后server将数据写入共享存储段,client从共享区读取数据。通信结束之后server与client断开与共享区的关联,并由server释放共享存储段。
  • 头文件
comm.h
//comm.h
#ifndef _COMM_H__
#define _COMM_H__#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>#define PATHNAME "."
#define PROJ_ID 0x6666int CreateShm(int size);
int DestroyShm(int shmid);
int GetShm(int size);
#endif
  • 函数实现文件
//comm.c
#include"comm.h"static int CommShm(int size,int flags)
{key_t key = ftok(PATHNAME,PROJ_ID);if(key < 0){perror("ftok");return -1;}int shmid = 0;if((shmid = shmget(key,size,flags)) < 0){perror("shmget");return -2;}return shmid;
}
int DestroyShm(int shmid)
{if(shmctl(shmid,IPC_RMID,NULL) < 0){perror("shmctl");return -1;}return 0;
}
int CreateShm(int size)
{return CommShm(size,IPC_CREAT | IPC_EXCL | 0666);
}
int GetShm(int size)
{return CommShm(size,IPC_CREAT);
}
  • 客户端
//client.c
#include"comm.h"int main()
{int shmid = GetShm(4096);sleep(1);char *addr = shmat(shmid,NULL,0);sleep(2);int i = 0;while(i < 26){addr[i] = 'A' + i;i++;addr[i] = 0;sleep(1);}shmdt(addr);sleep(2);return 0;
}
  • 服务器
//server.c
#include"comm.h"int main()
{int shmid = CreateShm(4096);char *addr = shmat(shmid,NULL,0);sleep(2);int i = 0;while(i++ < 26){printf("client# %s\n",addr);sleep(1);}shmdt(addr);sleep(2);DestroyShm(shmid);return 0;
}
  • makefile
//Makefile
.PHONY:all
all:server clientclient:client.c comm.cgcc -o $@ $^
server:server.c comm.cgcc -o $@ $^.PHONY:clean
clean:rm -f client server

5.使用共享内存完成进程间通信

  • 下面就以两个不相关的进程来说明进程间如何利用共享内存来进行通信。其中一个文件shm1.c(消费者)创建共享内存,并读取其中的信息,另一个文件shm2.c(生产者)将连接一个已有的共享内存段,向共享内存中写入数据。
#ifndef _SHM_COM_H_HEADER   //shm_com.h
#define _SHM_COM_H_HEADER#define TEXT_SZ 2048struct shared_use_st
{int written_by_you;       // 作为一个标志,非0:表示可读,0:表示可写char some_text[TEXT_SZ];  // 记录写入 和 读取 的文本
};#endif
#include <stddef.h>  //shm1.c
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "shm_com.h"int main(int argc, char **argv)
{int running = 1;void *shared_memory = NULL;struct shared_use_st *shared_stuff; // 指向shmint shmid; // 共享内存标识符srand((unsigned int)getpid());// 创建共享内存shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);if (shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}// 将共享内存连接到当前进程的地址空间shared_memory = shmat(shmid, (void *)0, 0);if (shared_memory == (void *)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("\nMemory attached at %p\n", shared_memory);printf("Memory attched at %d\n", *(int*)shared_memory);// 设置共享内存shared_stuff = (struct shared_use_st*)shared_memory; // 注意:shm有点类似通过 malloc() 获取到的内存,所以这里需要做个 类型强制转换shared_stuff->written_by_you = 0;while (running) // 读取共享内存中的数据{// 没有进程向内存写数据,有数据可读取if (shared_stuff->written_by_you == 1){printf("You wrote: %s", shared_stuff->some_text);sleep(1);// 读取完数据,设置written使共享内存段可写shared_stuff->written_by_you = 0;// 输入了 end,退出循环(程序)if (strncmp(shared_stuff->some_text, "end", 3) == 0){running = 0;}}}// 把共享内存从当前进程中分离if (shmdt(shared_memory) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}// 删除共享内存if (shmctl(shmid, IPC_RMID, 0) == -1){fprintf(stderr, "shmctl(IPC_RMID) failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);reutrn 0;
}
#include <unistd.h>  //shm2.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shm_com.h"int main(int argc, char **argv)
{int running = 1;void *shared_memory = NULL;struct shared_use_st *shared_stuff; // 指向shmchar buffer[BUFSIZ];int shmid; // 共享内存标识符// 创建共享内存shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);if (shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}// 将共享内存连接到当前的进程地址空间shared_memory = shmat(shmid, (void *)0, 0);if (shared_memory == (void *)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("Memory attched at %p\n", shared_memory);printf("Memory attched at %d\n", *(int*)shared_memory);// 设置共享内存shared_stuff = (struct shared_use_st *)shared_memory;while (running) // 向共享内存中写数据{// 数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本while (shared_stuff->written_by_you == 1){sleep(1);printf("Waiting...\n");}// 向共享内存中写入数据printf("Enter some text: ");fgets(buffer, BUFSIZ, stdin);strncpy(shared_stuff->some_text, buffer, TEXT_SZ);// 写完数据,设置written使共享内存段可读shared_stuff->written_by_you = 1;// 输入了end,退出循环(程序)if (strncmp(buffer, "end", 3) == 0){running = 0;}}// 把共享内存从当前进程中分离if (shmdt(shared_memory) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);return 0;
}


  • 实验解析

第一个程序shm1创建共享内存段,然后它连接到自己的地址空间中。我们在共享内存的开始处使用一个结构体shared_use_st。该结构体中有个标志written_by_you,当共享内存中有数据写入时,就设置这个标志。这个标志被设置时,程序就从共享内存中读取文本,将它打印出来,然后清除这个标志表示已经读完数据。我们用一个特殊字符串end来退出循环。接下来,程序分离共享内存段并删除它。

第二个程序shm2使用相同的键1234来取得并连接同一个共享内存段。然后它提示用户输入一些文本。如果标志written_by_you被设置,shm2就知道客户进程还未读完上一次的数据,因此就继续等待。当其他进程清除了这个标志后,shm2写入数据并设置该标志。它还使用字符串end来终止并分离共享内存段。

注意,这里提供了非常简陋的同步标志written_by_you,它包括一个非常缺乏效率的忙等待(不停地循环)。这可以使得我们的示例比较简单,但在实际编程中,应该使用信号量或通过传递消息、生成信号的方式来提供应用程序读、写部分之间的一种更有效的同步机制。

6.总结

总结:

(1)优点:我们可以看到使用共享内存进行进程之间的通信是非常方便的,而且函数的接口也比较简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,加快了程序的效率。

(2)缺点:共享内存没有提供同步机制,这使得我们在使用共享内存进行进程之间的通信时,往往需要借助其他手段来保证进程之间的同步工作。

7.共享内存使用流程(怎么用共享内存?)

1)向内核申请一块内存,并指定内存有多大(这块内存属于内核)

2)如果有两个进程需要通信,可以使用共享内存完成,先创建两个进程

3)进程A和进程B分别和这块共享内存关联,那这两个进程就拿到了共享内存的地址-》首地址

4)两个进程可以通过这个首地址对共享内存进行读/写操作

5)如果这个进程不再使用这块共享内存,需要和共享内存断开关联(进程退出,对共享内存没影响)

6)当不再使用共享内存时候,需要将共享内存销毁

8.ftok函数:获得key_t的参数

key_t  ftok(const char* pathname, int proj_id)-参数pathname:对应某个存在的路径或路径对应的文件名(路径或名都可以)-参数proj_id:目前只占用了该变量占用内存的一部分(1个字节,0到255都可以)举例:
key_t  t = ftok('/home/','a');//a就是129
shmget(t,0,0);

9.共享内存shm和映射区mmap的区别

1)shm可以直接创建,mmap创建需要依赖磁盘文件(也就是fd套接字)

内存映射区匿名映射不能进行无血缘关系的进程通信

2)shm效率更高

①shm直接对内存操作
②mmap需要同步磁盘文件

3)内存共享区域不同

①shm中所有进程操作同一块内存
②内存映射区:每个进程都会在自己的虚拟地址空间有一块独立的内存

4)数据安全性不同

①进程突然退出:
-》共享内存还在
-》内存映射区消失了
②运行进程的电脑突然挂了:
-》共享内存的数据消失了
-》内存映射区的数据还在

5)声明周期不同

①内存映射区:进程退出,内存映射区销毁
②共享内存:进程退出,共享内存还在,手动删除,或者关机

10.共享内存类的封装

1)BasShm(父类)

  • UML类图信息

  • 代码函数

①mapshm关联
②unmapshm解除关联
③delshm删除共享内存
void* m_ptr和int 类型的m_shmidproteced的,下面图片写错了

2)SecKey Shm存储密钥(子类)

  • 类图

3)NodeSHMInfo每个节点就是一个结构体

正文6:System V共享内存(修正版)及ftok函数讲解相关推荐

  1. Linux进程间通信一 System V 共享内存简介与示例

    目录 1. System V共享内存简介 2. API介绍 2.0 key_t和标识符 2.1  创建system v共享内存 2.2 映射共享内存并使用 2.3 取消共享内存映射 2.4 控制共享内 ...

  2. mmap内存映射、system V共享内存和Posix共享内存

    linux内核支持多种共享内存方式,如mmap内存映射,Posix共享内存,以system V共享内存.当内核空间和用户空间存在大量数据交互时,共享内存映射就成了这种情况下的不二选择.它能够最大限度的 ...

  3. 阐述linux IPC(五岁以下儿童):system V共享内存

    [版权声明:尊重原创.转载请保留源:blog.csdn.net/shallnet 要么 .../gentleliu,文章学习交流,不用于商业用途]         system V共享内存和posix ...

  4. linux环型共享内存,Linux system v 共享内存

    system v 共享内存 #include #include int shmget(key_t key, size_t size, int shmflg); 建立:进程与共享内存的关联关系 key_ ...

  5. Linux IPC实践(9) --System V共享内存

    共享内存API #include <sys/ipc.h> #include <sys/shm.h>int shmget(key_t key, size_t size, int ...

  6. 嵌入式Linux系统编程学习之二十三 System V 共享内存机制

    文章目录 前言 一.ftok 函数 二.shmget 函数 三.shmat 函数 四.shmdt 函数 五.shmctl 函数 补充 前言   共享内存也是进程间(进程间不需要有继承关系)通信的一种常 ...

  7. 共享内存之——system V共享内存

    System V 的IPC对象有共享内存.消息队列.信号灯(量). 注意:在IPC的通信模式下,不管是共享内存.消息队列还是信号灯,每个IPC的对象都有唯一的名字,称为"键(key)&quo ...

  8. 【校招 --阶段一 系统编程】system V共享内存

    一.什么是system V共享内存 共享内存区是最快的IPC形式.一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到 内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此 ...

  9. 【Linux】进程间通信 - 匿名/命名管道与System V共享内存

    目录 前言 一.管道 0.什么是管道 1).管道的概念 2).管道的本质 3).管道指令: "|" 1.匿名管道 1).如何创建匿名管道 2).如何使用匿名管道进行通信 3).匿名 ...

  10. 【Linux篇】第十二篇——进程间通信(管道+system V共享内存)

    进程间通信介绍 概念 目的 本质 分类 管道 什么是管道 匿名管道 匿名管道的原理 pipe函数 匿名管道使用步骤 管道读写规则 管道的特点 管道的大小 命名管道 命名管道的原理 使用命令创建命名管道 ...

最新文章

  1. 学 AI 和机器学习的人必须关注的 6 个领域
  2. SAP MM 并非奇怪现象之MB5B报表查不到某一笔出库记录?
  3. 软件开发打败了 80 %的程序员
  4. Ajax解决缓存的5种方法
  5. 【收藏】C# 2.03.0新特性总结
  6. Spark MaprLab-Auction Data分析
  7. leetcode 395. 至少有 K 个重复字符的最长子串(滑动窗口)
  8. 高一计算机网络技术应用计划,高一计算机网络应用基础教学计划
  9. 讲解SQL Server定时作业job的设置方法
  10. dvt高危患者的护理措施_dvt的预防及护理
  11. Spring-data-jpa中用@ColumnTransformer注解加密,中文乱码问题(数据库正常,在java代码和页面中乱码)
  12. cad2010多个文件并排显示_飞利浦显示器推荐,提升你的工作效率与水平
  13. SQL 游标使用实例
  14. 【Oracle】ERROR: ORA-28000: the account is locked
  15. proteus 7.8下载链接
  16. 指数函数及其性质教学设计
  17. 北大光华女的超强面经! 感动之余,真的受益匪浅。
  18. SCP,NFS,TFTP的初步认识
  19. Vivado安装使用【Verilog】
  20. ArcGIS API for JavaScript根据两个点坐标在地图上画线

热门文章

  1. 0基础用android做一个excel表查询器(2):编写程序
  2. linux增加swap空间
  3. cmd无法运行python_如何解决运行python指令提示不是内部或外部命令
  4. 《哈迪斯:杀出地狱》:超爽的地狱逃生之旅
  5. 倍福写入txt文件的方法
  6. 低级格式化和高级格式化的区别是什么?
  7. C# string.Format
  8. cocos2dx经典魔塔游戏改编完整源码
  9. CSS 3.0实现炫酷发光特效
  10. XML各层对象的方法