[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)

Posted TT在长大

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)相关的知识,希望对你有一定的参考价值。

本文详解了通过共享内存进行进程间通信的方法,并对消息队列,信号量做了简单介绍。

另一种进程间通信--管道,见前文:[OS-Linux]详解Linux的进程间通信1------管道_RMA515T的博客-CSDN博客

管道通信本质是基于文件,OS没有过多的设计。

system V进程间通信是OS特地设计的通信方式,让不同进程看到同一份资源。system V进程间通信包括了共享内存、消息队列、信号量。共享内存与消息队列以传输数据为目的,信号量则是保证进程的同步与互斥设计的,属于通信的范畴。

目录

一、system V共享内存

二、共享内存原理

1. 共享内存的通信原理

2. 共享内存数据结构

 3.共享内存的建立过程

三、共享内存函数

1.shmget函数

2.shmat函数

3.shmdt函数

4. shmctl函数

三、共享内存实例

makefile

server.c

client.c

结果

四、system V消息队列

 五、system V信号量

1.进程互斥

总结


一、system V共享内存

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

共享内存并未提供同步机制,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。

共享内存示意图

二、共享内存原理

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

1. 共享内存的通信原理

当两个进程通过页表将虚拟地址映射到物理地址时,共享内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但要确保一个进程在写的时候不能被读,所以通过信号量来实现同步与互斥。

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

2. 共享内存数据结构

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 */
};

 3.共享内存的建立过程

       (1)申请共享内存(物理内存已经开辟好了);

       (2)共享内存挂接到地址空间(建立映射关系);

       (3)去关联共享内存(修改页表,取消映射关系);

       (4)释放共享内存(内存归还给系统)。

三、共享内存函数

1.shmget函数

功能:用来创建共享内存

原型:

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

参数:
        key:这个共享内存段名字
        size:共享内存大小
        shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

2.shmat函数

功能:将共享内存段连接到进程地址空间
原型:

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

参数:
        shmid:共享内存标识
        shmaddr:指定连接的地址
        shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

另外:

shmaddr为NULL,核心自动选择一个地址。
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA整数倍。公式:shmaddr -(shmaddr % SHMLBA)。
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存。

3.shmdt函数

功能:将共享内存段与当前进程脱离
原型:

int shmdt(const void *shmaddr);

参数:shmaddr: 由shmat所返回的指针

返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

4. shmctl函数

功能:用于控制共享内存
原型:

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

参数:
        shmid:由shmget返回的共享内存标识码
        cmd:将要采取的动作(有三个可取值)
        buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够的权限的前提下,把共享内存当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

三、共享内存实例

server通过共享内存给client发送数据。、

ftok函数:把一个已经存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键

makefile

.PHONY:all
all:client server

client: client.c
	gcc -o $@ $^
server: server.c
	gcc -o $@ $^

.PHONY:clear
clear:
	rm client server

server.c

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

#define SIZE 4096
#define PATHNAME "/home/Zht/Linux/SHM"
#define PROJ_ID 0x66

int main()
{
  key_t k = ftok(PATHNAME, PROJ_ID);
  if(k < 0){
    perror("ftok");
    return 1;
  }

  int shmid = shmget(k, SIZE, IPC_CREAT | IPC_EXCL | 0644);    //开辟
  if(shmid < 0){
    perror("shmget");
    return 2;
  }

  char *mem = shmat(shmid, NULL, 0);    //挂接

  int i = 0;
  while(1)
  {
    mem[i] = 'A' + i;
    i++;
    sleep(1);
    mem[i] = '\\0';
  }

  shmdt(mem);

  //shmctl(shmid, IPC_RMID, NULL);  //释放
  return 0;
}

client.c

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


#define SIZE 4096
#define PATHNAME "/home/Zht/Linux/SHM"
#define PROJ_ID 0x66


int main()
{
  key_t k = ftok(PATHNAME, PROJ_ID);
  if(k < 0){
    perror("ftok");
    return 1;
  }


  int shmid = shmget(k, SIZE, IPC_CREAT);    //开辟
  if(shmid < 0){
    perror("shmget");
    return 2;
  }


  char *mem = shmat(shmid, NULL, 0);    //挂接


  int i = 0;
  while(1)
  {
    printf("server meg# %s\\n", mem);    //读取
    sleep(1);
  }


  shmdt(mem);


  //shmctl(shmid, IPC_RMID, NULL);  //释放
  return 0;
}

结果

运行时

正常接收

 挂接也为2

终止后挂接为0。 

四、system V消息队列

消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值特性方面。
IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核

 五、system V信号量

信号量主要用于同步和互斥的。

1.进程互斥

由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。

系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源
进程中涉及到互斥资源的程序段叫临界区
IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核

总结

进程间的通信主要分为管道和system V,管道有可分为匿名管道命名管道;system V进程间通信包括了共享内存、消息队列、信号量。两篇文章对他们进行了介绍。

以上是关于[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)的主要内容,如果未能解决你的问题,请参考以下文章

[OS-Linux]详解Linux的进程间通信1------管道

[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)

[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)

[OS-Linux]详解Linux的进程间通信2------system V共享内存(Shared Memory)

[OS-Linux]详解Linux进程控制

[OS-Linux]详解Linux进程控制