Linux进程间通信(二)

简介: Linux进程间通信

三、System V IPC

3.1 共享内存

3.1.1 共享内存原理

共享内存让不同进程看到同一份资源的方式是:在物理内存中申请一块内存空间,然后将这块内存空间分别与各个进程各自的页表之间进行联系,再在虚拟地址空间当中开辟空间并将虚拟地址填充到各自页表的对应位置,使得虚拟地址和物理地址之间建立起映射关系,至此进程便看到同一份物理内存


b372b02a3ef8452291859893a1ddbf8b.png


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

当申请一块共享内存后,为了要让实现通信的进程能够看到同一个共享内存,因此每个共享内存被申请时都有一个key值,这个key值用于标识系统中共享内存的唯一性。

可以看到上面"描述"共享内存的结构体的第一个成员是shm_perm,shm_perm是一个ipc_perm类型的结构体变量,每个共享内存的key值存储在shm_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;
};

注意:shmid_ds和ipc_perm结构体分别在/usr/include/linux/shm.h和/usr/include/linux/ipc.h中定义


3.1.3 查看共享内存信息

使用ipcs命令可以查看共享内存的信息。但是该命令会默认列出消息队列、共享内存以及信号量相关的信息,若只想查看某一个的相关信息,可以选择携带选项


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

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

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

ipcs命令列出的每列信息的含义如下:

7c8c9d58df7c4e868d3a18730610e81d.png



注意: key是内核层面上保证共享内存唯一性的方式;shmid是在用户层上保证共享内存的唯一性


3.1.4 共享内存的创建

使用shmget()函数进行共享内存的创建


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

返回值:


shmget调用成功,返回一个有效的共享内存标识符(用户层标识符)

shmget调用失败,返回-1

参数:


参数key,表示待创建共享内存在系统当中的唯一标识(需要自行生成)

参数size,表示待创建共享内存的大小

参数shmflg,表示创建共享内存的方式,可添加共享内存权限(按位或)

注意: 具有标定某种资源能力的东西被称作句柄(譬如FILE即文件句柄),而shmget函数的返回值实际上就是共享内存的句柄,其可以在用户层标识共享内存。当共享内存被创建后,在后续使用共享内存的相关接口时,都需要通过这个句柄对指定共享内存进行操作


使用ftok函数获取参数key


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

ftok函数的作用就是,将一个已存在的路径名pathname和一个整数标识符proj_id转换成一个key值,称为IPC键值,在使用shmget函数获取共享内存时,这个key值会被填充进维护共享内存的数据结构当中


注意:


  1. 使用ftok函数生成key值可能会产生冲突,此时可以对传入ftok函数的参数进行修改
  2. 需要进行通信的各个进程,在使用ftok函数获取key值时,都需要采用同样的路径名和和整数标识符,进而生成同一种key值,然后才能找到同一个共享资源
  3. pathname所指定的文件必须存在且可存取(有权限)

参数shmflg



选项(组合) 作用
IPC_CREAT 若不存在键值与key相等的共享内存,则新建一个共享内存并返回该共享内存的句柄;若存在这样的共享内存,则直接返回该共享内存的句柄
IPC_CREAT | IPC_EXCL 若内核中不存在键值与key相等的共享内存,则新建一个共享内存并返回该共享内存的句柄;若存在这样的共享内存,则出错返回
IPC_EXCL 单独使用无任何作用

使用组合IPC_CREAT,一定会获得一个共享内存的句柄,但无法确认该共享内存是否是新建的共享内存

使用组合IPC_CREAT | IPC_EXCL,只有shmget函数调用成功时才会获得共享内存的句柄,并且该共享内存一定是新建的共享内存

3.1.5 共享内存的释放

当进程运行完毕后申请的共享内存依旧存在,并没有被操作系统释放,因为共享内存的生命周期是随内核的。若进程不主动删除创建的共享内存,那么共享内存就会一直存在,直到关机重启(system V IPC都是如此)。

用命令释放共享内存资源

使用ipcrm -m shmid命令

b3a114126a7b49e88e58f39fe5508dbe.png

使用程序接口释放共享内存资源

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

参数shmid,表示所控制共享内存的用户级标识符

参数cmd,表示具体的控制动作

第参数buf,用于获取或设置所控制共享内存的数据结构

参数cmd:


a6354191be5647ccbd2622a3f6f2e36a.png


返回值:


shmctl调用成功,返回0

shmctl调用失败,返回-1

3.1.6 共享内存的关联

建立共享内存与进程地址空间的映射关系时需要使用shmat函数

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

参数shmid,表示待关联共享内存的用户级标识符

参数shmaddr,指定共享内存映射到进程地址空间的某一地址,通常设置为NULL,表示让内核自行决定合适的地址位置

参数shmflg,表示关联共享内存时设置的某些属性

参数shmflg:


b64fd89bcbc94ba2988eaf6c5d8fd14f.png


返回值:


shmat调用成功,返回共享内存映射到进程地址空间中的起始地址

shmat调用失败,返回(void*)-1

3.1.7 共享内存的去关联

取消共享内存与进程地址空间之间的关联需使用shmdt()函数

int shmdt(const void *shmaddr);

shmaddr参数:


待去关联共享内存的起始地址,即调用shmat函数时得到的起始地址

返回值:


shmdt调用成功,返回0

shmdt调用失败,返回-1

3.1.8 利用共享内存实现serve&&client通信

服务端创建一个新的共享内存并建立连接

//my_server.cxx
#include "com.h"
int main()
{
  key_t key = ftok(PATHNAME, PROJ_ID); //获取key值
  if (key < 0) {
    perror("ftok");
    return 1;
  }
  int shm = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0666); //创建新的共享内存
  if (shm < 0) {
    perror("shmget");
    return 2;
  }
  char* mem = (char*)shmat(shm, NULL, 0); //关联共享内存
    //服务端不断读取共享内存当中的数据并输出
    while (1) {
      printf("client# %s\n", mem);
      sleep(1);
    }
  shmdt(mem); //共享内存去关联
  shmctl(shm, IPC_RMID, NULL); //释放共享内存
  return 0;
}


客户端连接的是已创建好的共享内存,并往其中不断写入数据

//my_client.cxx
#include "com.h"
int main()
{
  key_t key = ftok(PATHNAME, PROJ_ID); //获取与server进程相同的key值
  if (key < 0) {
    perror("ftok");
    return 1;
  }
  int shm = shmget(key, SIZE, IPC_CREAT); //获取server进程创建的共享内存的用户层id
  if (shm < 0) {
    perror("shmget");
    return 2;
  }
  char* mem = (char*)shmat(shm, NULL, 0); //关联共享内存
    //客户端向共享内存写入数据
    int i = 0;
    while (1) {
      mem[i++] = 'A' + i;
      mem[i] = '\0';
      sleep(1);
    }
  shmdt(mem); //共享内存去关联
  return 0;
}

服务端和客户端代码中都包含该头文件,确保生成的key、路径、连接的共享内存相同


#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#define PATHNAME "/home/bjy/BaoLinux/code/signal_com/shm/shm_2" //路径名
#define PROJ_ID 0x6666 //整数标识符
#define SIZE 4096 //共享内存的大小


4513c4096c5040e18ac3d00b1e07c4b8.png

3.1.9 共享内存与管道对比

共享内存创建好后就不需要调用系统接口进行通信了,而管道创建好后仍需要read、write等系统接口进行通信。共享内存是所有进程间通信方式中最快的一种通信方式

980e3480b30b4a2c8d026c5e23f4e8c7.png



使用管道通信的方式,将文件中的数据从一个进程传输到另一个进程需要进行四次拷贝操作:


  1. 服务端将信息从输入文件复制到服务端的临时缓冲区中
  2. 将服务端临时缓冲区的信息复制到管道中
  3. 客户端将信息从管道复制到客户端的缓冲区中
  4. 将客户端临时缓冲区的信息复制到输出文件中

4eb1c03645b0484c9527783306984ca4.png


使用共享内存进行通信,将文件中的数据从一个进程传输到另一个进程只需要进行两次拷贝操作:


1.从输入文件到共享内存

2.从共享内存到输出文件

共享内存同样也存在问题,其并没有提供任何的保护机制,类似于同步与互斥


3.2 消息队列

该技术已面临淘汰,等博主有时间会进行更新


3.3 信号量

后续博主有时间会进行更新


相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
1月前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
37 0
|
18天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
51 4
linux进程管理万字详解!!!
|
9天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
48 8
|
6天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
17天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
52 4
|
18天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
20天前
|
消息中间件 存储 Linux
|
26天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
33 1
|
1月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
25 1
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
31 0
Linux c/c++之IPC进程间通信

热门文章

最新文章

下一篇
无影云桌面