【hello Linux】进程间通信——共享内存

这篇具有很好参考价值的文章主要介绍了【hello Linux】进程间通信——共享内存。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言:

1. System V共享内存

1. 共享内存的理解

2. 共享内存的使用步骤

3. 共享内存的使用

        1. 共享内存的创建

        查看共享内存

        2. 共享内存的释放

        3. 共享内存的挂接

        4. 共享内存的去挂接

4. 共享内存的使用示例

1. 两进程挂接与去挂接演示:

2. 两进程通信演示;


【hello Linux】进程间通信——共享内存

Linux🌷

前言:

在之前写的进程间通信的两篇博客中,不管是匿名管道通信还是命名管道通信都是基于文件的一种通信方式;

而今天我们要学习另一种通信方式:System V标准下的 “共享内存”;

在这里简单介绍下System V标准,System V标准是计算机学术界的一些领头人物站在OS层面,专门为进程间通信设计的方案;

System V提供了三个主流方案:

1. 共享内存——也就是今天博客的主要内容;

2. 消息队列;

3. 信号量;

其中共享内存和消息队列是为了传输数据设计的,信号量是为了保证进程间的同步和互斥设计的;

下面开始我们的正文!🔮


1. System V共享内存

1. 共享内存的理解

共享内存是一种允许两个或多个进程访问同一块物理内存的通信机制;

用一张图更好的理解下:

【hello Linux】进程间通信——共享内存

其本质就是先在物理内存中开辟一块空间,然后让相互通信的进程通过页表映射将这块共享空间映射到进程的虚拟地址空间中,因此两个进程便看到了同一份资源,便可以进行进程间通信了,物理内存中开辟的空间就是我们的共享内存;

OS中可能存在多对使用共享内存进行通信的进程,共享内存也自然不止一个,OS也当然要将共享内存进行管理,管理方法——“先描述,在组织”

下面看一下共享内存的数据结构:

struct shmid_ds {
 struct ipc_perm shm_perm; /* operation perms */
 int shm_segsz; /* size of segment (bytes) */
 __kernel_time_t shm_atime; /* last attach time */
 __kernel_time_t shm_dtime; /* last detach time */
 __kernel_time_t shm_ctime; /* last change time */
 __kernel_ipc_pid_t shm_cpid; /* pid of creator */
 __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
 unsigned short shm_nattch; /* no. of current attaches */
 unsigned short shm_unused; /* compatibility */
 void *shm_unused2; /* ditto - used by DIPC */
 void *shm_unused3; /* unused */
};

我们申请一块共享内存之后 为了保证这块共享内存的唯一性 我们必须要对它做个标识

我们在 linux 系统中我们使用 key 值来唯一标识一块共享内存

在我们的 shmid_ds 结构体的第一行有一个叫做 shm_perm 的结构体 它的类型是 ipc_perm 它的结构体定义如下

struct ipc_perm{
	__kernel_key_t  key;
	__kernel_uid_t  uid;
	__kernel_gid_t  gid;
	__kernel_uid_t  cuid;
	__kernel_gid_t  cgid;
	__kernel_mode_t mode;
	unsigned short  seq;
};

我们看它的第一行有这么一段描述 __kernel_key_t key;

而这个key就是唯一标识共享内存的key值

2. 共享内存的使用步骤

1. 共享内存的创建(开辟共享内存);

2. 共享内存的挂接(在页表中建立共享内存的映射关系);

3. 共享内存的去挂接(在页表中去掉共享内存的映射关系);

4. 共享内存的释放;

3. 共享内存的使用

1. 共享内存的创建

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

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

key:共享内存段的唯一标识,用户可使用如下函数进行key的自动生成

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

key_t ftok(const char *pathname, int proj_id);

pathname:自定义的路径名;

proj_id:自定义的项目ID;

返回值:成功返回一个非负整数,失败返回-1;

只要pathname和proj_id是一样的,那么生成的key便是一样的,因此两个进程便可以识别同一共享内存 ;

@size:共享内存的大小,建议是4KB的整数倍。

因为OS在分配内存资源时,是以页面为单位进行分配的,页面的大小是4KB,虽然是这样分配的,但是申请多少还是给你多少,为了避免给用户造成一些错误;

@shmflg:由九个权限标志组成,它们的用法和创建文件时使用的mode模式标志是一样的。

我们一般有如下两种用法:

1. IPC_CREAT共享内存不存在时,创建一个共享内存并返回,如果创建的共享内存已经存在的话,则直接返回当前已经存在的共享内存;

2. IPC_CREAT | IPC_EXCL:如果共享内存不存在则创建并返回,如果共享内存已经存在则返回出错,也就是说如果调用成功,得到的一定是一个最新的,没有被别人使用的共享内存!IPC_EXCL单独使用是没有意义的,

3. 共享内存也是有权限的,我们还可以在上述基础上(|或权限),达到共享内存权限的设定;

@返回值:创建成功返回一个非负整数,即该共享内存段的标识码,失败返回-1;
示例:
#include <stdio.h>    
#include <sys/ipc.h>    
#include <sys/shm.h>    
    
#define PATH_NAME "./test.c"    
#define PROJ_ID 0x666    
#define SIZE 4097    
    
int main()    
{    
  //生成key    
  key_t key = ftok(PATH_NAME,PROJ_ID);    
  if(key<0)    
  {    
    perror("ftok fail\n");    
    return 1;    
  }    
    
  //创建共享内存                                                          
  int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL);    
  if(shmid<0)              
  {                             
    perror("shmget fail\n");    
    return 2;              
  }                        
                                             
  printf("key:%u ,shmid:%d\n",key,shmid);    
  return 0;                
}      
结果演示:
【hello Linux】进程间通信——共享内存

查看共享内存

我们可以使用 ipcs 命令查看系统中的进程间通信资源的信息;

【hello Linux】进程间通信——共享内存

如果我们只想知道三种通信方式其中一种的通信信息的话,我们可以在 ipcs 后面携带选项:

-q:列出消息队列相关信息;

-m:列出共享内存相关信息;

-s:列出信号量相关信息;

【hello Linux】进程间通信——共享内存

 其中它的每列信息含义如下:

命名 含义
key 系统区别各个共享内存的唯一标识
shmid 共享内存的用户层id
owner 共享内存的拥有者
perms 共享内存的权限
bytes 共享内存的大小
nattch 挂接共享内存的进程数
status 共享内存的状态

key和shmid的区别:

我们的key是系统中是被共享内存的唯一标识 而shmid是我们用户层识别共享内存的标识

这就好像是inode和文件名的区别

系统中使用inode来标识一个文件 而我们使用文件名来标识一个文件

2. 共享内存的释放

共享内存的生命周期是随内核的 就算进程结束了共享内存也不会被释放

如果再次运行test.c程序 我们就会发现这样子的错误

【hello Linux】进程间通信——共享内存

想要释放共享内存只有两种方式

1. 关机重启

2. 主动进行释放

使用命令方式释放共享内存:

我们可以使用 ipcrm -m shmid命令释放指定id的共享内存资源

【hello Linux】进程间通信——共享内存

使用函数方式释放共享内存:

我们一般使用shmctl函数来控制共享内存 它的函数原型如下

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

shmid:用户层面标识唯一一个共享内存;

cmd:具体的控制命令;

buf:获取或设置所控制共享内存的数据结构(一般设为NULL);

返回值:成功返回0,失败返回-1;

其中关于 cmd 参数 常用的命令有以下几个

选项 作用
IPC_STAT 获取共享内存的当前关联值 此时参数buf作为输出型参数
IPC_SET 在进程有足够权限的前提下 将共享内存的当前关联值设置为buf所指的数据结构中的值
IPC_RMID 删除共享内存段

将上述代码修改下,继续运行:

#include <stdio.h>    
#include <sys/ipc.h>    
#include <sys/shm.h>    
    
#define PATH_NAME "./test.c"    
#define PROJ_ID 0x666    
#define SIZE 4097    
    
int main()    
{    
  //生成key    
  key_t key = ftok(PATH_NAME,PROJ_ID);    
  if(key<0)    
  {    
    perror("ftok fail\n");    
    return 1;    
  }    
    
  //创建共享内存    
  int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL);    
  if(shmid<0)    
  {    
    perror("shmget fail\n");    
    return 2;    
  }                                                                                           
    
  printf("key:%x ,shmid:%d\n",key,shmid);    
    
  //释放共享内存    
  shmctl(shmid,IPC_RMID,NULL);    
  printf("delete success!\n");    
  return 0;    
}    

【hello Linux】进程间通信——共享内存

 我们可以看到 共享内存 被释放了

3. 共享内存的挂接

我们一般使用 shmat函数来进行共享内存的挂接

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

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

shmid: 待关联共享内存的用户级标识符;

shmaddr:指定共享内存映射到进程地址空间的某一地址(通常设置为NULL,让OS来指定 );
shmflg:挂接共享内存时设置的某些属性(一般设置为0);

返回值:成功则返回共享内存映射到进程地址空间中的起始地址,失败则返回-1;

4. 共享内存的去挂接

我们一般使用shmat函数来进行共享内存和虚拟地址空间的去挂接

int shmdt(const void *shmaddr);

shmaddr:shmat函数时得到的起始地址

返回值:成功返回0,失败返回-1;

4. 共享内存的使用示例

1. 两进程挂接与去挂接演示:

【hello Linux】进程间通信——共享内存

 我们可以看到nattch是由0->1->2->1->0的;

注意:我们在shmget的时候必须设置权限,否则会挂不上去;

  • comm.h:
#include <stdio.h>    
#include <sys/ipc.h>    
#include <sys/shm.h>    
#include <sys/types.h>    
#include <unistd.h>    
    
#define PATH_NAME "./"        
#define PROJ_ID 0x333        
#define SIZE 4097 
  • makefile:
.PHONY:all    
all:server client    
    
server:server.c    
  gcc -o $@ $^    
    
client:client.c    
  gcc -o $@ $^    
    
.PHONY:clean    
clean:    
  rm -f server client     
  • server.c:
#include "comm.h"

int main()
{
  //生成共享内存key
  key_t key = ftok(PATH_NAME,PROJ_ID);
  if(key<0)
  {
    perror("ftok fail");
    return 1;
  }

  //创建共享内存
  int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0666);
  if(shmid<0)
  {
    perror("shmget fail");
    return 2;
  }
  printf("shmget success! key:0x%x shmid:%d\n",key,shmid);
  sleep(3);

  //挂接共享内存
  char* mem = (char*)shmat(shmid,NULL,0);                                                                  
  printf("shmat success!\n");
  sleep(3);

  //去挂接共享内存
  shmdt(mem);
  printf("shmdt success!\n");
  sleep(3);

  //释放共享内存
  shmctl(shmid,IPC_RMID,NULL);
  printf("shmrm success!\n");
  sleep(3);
  return 0;
}
  •  client.c:
#include "comm.h"    
    
int main()    
{    
  //生成key    
  key_t key = ftok(PATH_NAME,PROJ_ID);    
  if(key<0)    
  {    
    perror("ftok fail");    
    return 1;    
  }    
    
  //获取shmid    
  int shmid = shmget(key,SIZE,IPC_CREAT|0666);                                                             
  if(shmid<0)    
  {    
    perror("shmget fail");    
    return 2;    
  }    
    
  //挂接共享内存    
  char* mem = (char*)shmat(shmid,NULL,0);    
  sleep(1);    
  printf("client shmat success!\n");    
    
  //去挂接    
  shmdt(mem);    
  sleep(1);    
  printf("client shmdt success!\n");    
    
  return 0;    
}    

2. 两进程通信演示;

【hello Linux】进程间通信——共享内存

 我们在上述代码中添加了通信的内容,结果如上所示;

  • server.c: 
  //通信    
  while(1)    
  {    
    printf("%s\n",mem);    
    sleep(1);                                                           
  }    
  • client.c:
  //通信    
  char c='A';    
  while(c<'Z')    
  {    
    mem[c-'A']=c;    
    c++;    
    mem[c-'A']='\0';    
    sleep(2);                                                           
  }    

客户端每隔2秒往共享内存中写入数据,服务端每隔1秒读一次;

我们发现在客户端还没有写入数据时,服务端就开始读了;

并且,服务端再读数据之后,并没有把数据拿走;

这两点是区别与管道通信的,管道是具有同步机制(读写两端会相互等待的),读了之后数据就带走了;

坚持打卡!😀文章来源地址https://www.toymoban.com/news/detail-428381.html

到了这里,关于【hello Linux】进程间通信——共享内存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】进程间通信之共享内存

    共享内存比管道快哦~ 文章目录 前言 一、共享内存的实现原理 二、实现共享内存的代码 总结 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的

    2024年02月03日
    浏览(51)
  • Linux 共享内存mmap,进程通信

    进程间通信是操作系统中重要的概念之一,使得不同的进程可以相互交换数据和进行协作。其中,共享内存是一种高效的进程间通信机制,而内存映射(mmap)是实现共享内存的一种常见方法。 存储映射 I/O 是 一个磁盘文件 与 存储空间中的一个缓冲区相映射 。于是, 当从缓

    2024年02月13日
    浏览(45)
  • 【Linux】进程间通信——管道/共享内存

    进程间通信( Inter-Process Communication,简称IPC )是指不同进程之间进行数据交换和共享信息的机制和技术。在操作系统中,每个进程都是独立运行的,有自己的地址空间和数据,因此进程之间需要一种机制来进行通信,以便彼此协调工作、共享数据或者进行同步操作。 进程间

    2024年02月16日
    浏览(45)
  • 进程间通信--共享内存详解【Linux】

    本文详细讲解了共享内存的原理和使用,并且通过实例代码角度来深度理解共享内存,下面就让我们开始吧。 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源。 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(

    2024年02月02日
    浏览(43)
  • Linux--进程间的通信-共享内存

    前文: Linux–进程间的通信-匿名管道 Linux–进程间的通信–进程池 Linux–进程间的通信-命名管道 对于两个进程,通过在内存开辟一块空间(操作系统开辟的),进程的虚拟地址通过页表映射到对应的共享内存空间中,进而实现通信 ; 特点和作用: 高效性: 共享内存是一种

    2024年04月26日
    浏览(43)
  • 【Linux】进程间的通信之共享内存

    利用 内存共享 进行进程间的通信的原理其实分为以下几个步骤: 在物理内存中创建一块共享内存。 将共享内存链接到要通信的进程的页表中,并通过页表进行进程地址空间的映射。 进程地址空间映射完毕以后返回首个虚拟地址,以便于进程之间进行通信。 根据共享内存的

    2024年02月09日
    浏览(51)
  • 【Linux】进程间通信——system V共享内存

    目录  写在前面的话 System V共享内存原理 System V共享内存的建立 代码实现System V共享内存 创建共享内存shmget() ftok() 删除共享内存shmctl() 挂接共享内存shmat() 取消挂接共享内存shmdt() 整体通信流程的实现          上一章我们讲了进程间通信的第一种方式 --- 管道,这一章我

    2024年02月14日
    浏览(43)
  • 【Linux】进程间通信 -- system V共享内存

    共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据 理解: 进程间通信,是专门设计的,用来IPC 共享内存是一种通信方式,所有想通信的进程

    2024年02月16日
    浏览(49)
  • Linux学习记录——이십 进程间通信(2)共享内存

    system是一套标准,独立于文件系统之外,是系统专门为通信而设计出来的内核模块,称之为system V的IPC通信机制。 共享内存的主要做法也是让两个毫不相关的进程看到同一份资源。 进程的地址空间内,栈和堆区之间有个共享区,堆是向上增长,栈是向下增长,重合的那个地方

    2023年04月24日
    浏览(40)
  • 【Linux】进程间通信 --- 管道 共享内存 消息队列 信号量

    等明年国庆去西藏洗涤灵魂,laozi不伺候这无聊的生活了 1. 通过之前的学习我们知道,每个进程都有自己独立的内核数据结构,例如PCB,页表,物理内存块,mm_struct,所以具有独立性的进程之间如果想要通信的话,成本一定是不低的。 2. a.数据传输:一个进程需要将它的数据

    2023年04月17日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包