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操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
87 1
|
18天前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
84 20
|
1月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
109 13
|
1月前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
2月前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
193 4
linux进程管理万字详解!!!
|
1月前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
2月前
|
缓存 算法 Linux
Linux内核的心脏:深入理解进程调度器
本文探讨了Linux操作系统中至关重要的组成部分——进程调度器。通过分析其工作原理、调度算法以及在不同场景下的表现,揭示它是如何高效管理CPU资源,确保系统响应性和公平性的。本文旨在为读者提供一个清晰的视图,了解在多任务环境下,Linux是如何智能地分配处理器时间给各个进程的。
|
2月前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
98 8
|
2月前
|
网络协议 Linux 虚拟化
如何在 Linux 系统中查看进程的详细信息?
如何在 Linux 系统中查看进程的详细信息?
298 1
|
2月前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?

热门文章

最新文章