[架构之路-60]:目标系统 – 平台软件 – 基础中间件 – Linux进程间通信的主要方式

目录

前言:

第1章 进程

1.1 进程概述

1.2 线程

1.3 进程的资源类型

1.4 Linux进程的特点

第2章 进程间通信

2.1 概述

2.2 进程通信的应用场景

2.3 进程间通信要共享的对象

2.4 进程间通信必须解决问题

2.5 进程间通信的方式概述

第3章  进程间通信的方式详解

3.1 虚拟文件系统通信

3.2 无名管道:pipe 通信

3.4 信 量:semaphore

3.5 消息队列: message queue

3.6 共享内存:shared memory

3.7 套接字:socket

3.8 信 : Signal


前言:

第1章 进程

1.1 进程概述

进程是操作系统内维护程序资源的一个概念。每当我们执行一个可执行程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放。

可以认为进程是一个程序所有资源的统称

1.2 线程

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如Windows 7的线程,进行混合调度。

1.3 进程的资源类型

(1)线程以及线程id

(2)内存地址空间

  • 逻辑地址空间
  • 各种全局变量:普通变量、结构体变量等
  • 各种句柄(文件句柄、socket句柄等)

(3)CPU

  • CPU寄存器上下文
  • CPU运算时隙

1.4 Linux进程的特点

(1)进程间的隔离性

进程与进程之间的资源是相互隔离的。如下图所示:

既然进程间的资源是隔离的, 那就不仅仅隔离的是进程内的逻辑地址空间,不仅仅隔离的用户空间的地址,而且包括地址空间内的一切资源:文件句柄、socket句柄、线程id、CPU上下文等等。

(2)与内核的交互性

虽然用户空间和内核空间也是隔离的,但用户空间的程序可以通过内核提供的系统调用,完成内核空间和进程用户空间的交互。

第2章 进程间通信

2.1 概述

进程用户空间是相互独立的,一般而言是不能相互访问的。

但很多情况下进程间需要互相通信,来完成系统的某项功能。

进程通过与内核及其它进程之间的互相通信来协调它们的行为。

2.2 进程通信的应用场景

(1)数据传输:

一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到N个字节之间,有可能是少量数据,有可能是大量数据。这个过程需要进行数据的拷贝和传送。

(2)数据共享:

多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到

这个过程不需要进行数据的拷贝,不同进程之间共享相同的一份数据。这种情形,常见于两个进程之间有大额数据传输或实时数据传输。

(3)资源共享:

多个进程之间共享同样的资源(不一定是大量的数据),如系统的外设。

Linux通过把外设规划到内核的虚拟文件系统中,实现外设资源在不同用户进程之间的共享。

为了做到这一点,需要内核提供锁和同步机制。

(4)通知事件:

一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程。

(5)进程控制:

有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

2.3 进程间通信要共享的对象

(1)在用户空间的一段内存(共享内存)

(2)在用户空间的一段内存(消息)

(3)由内核空间管理的磁盘文件

(4)由内核空间管理的设备文件(虚拟文件)

(5)由内核空间管理的信 signal

(6)由内核空间管理的socket

2.4 进程间通信必须解决问题

由于用户进程空间是完全隔离的,要解决他们的共享问题,必须解决如下的几个问题:

(1)共享对象在全局的唯一标识问题。

这个标识必须是独立于用户空间进程的,它不属于用户空间的资源,而应该属于内核空间的资源或整个Linux系统的资源。

像文件句柄id,socket id,线程id,他们都是进程内的资源标识不能作为进程间全局性的资源标识

在Linux中,可以利用的全局标识大致有如下几种:

  • 虚拟文件系统的全路径名称,如/tmp/filename.tmp
  • Signal ID
  • 全局性的Key(32bit或64bit的key id)

(2)把全局标识映射到进程空间标识

程序在进程空间执行时,如果要访问某个对象,必须生产进程空间的对象标识,比如消息队列 id,socket id,文件标识id。

这个映射过程由程序在进程空间中创建某个实际对象实现的,并把全局标识和进程内的标识关联起来。如进程内的程序在创建消息队列时,就必须指定消息队列的全局标识key,然后返回一个进程内的标识:消息队列id。

2.5 进程间通信的方式概述

在Linux中,用户空间的进程之间的所有资源是相互隔离的,因此,他们之间是不能直接通信的。

Linux提供了用户空间与内核空间数据的传递。

因此,用户空间的进程之间的通信,本质上,用户空间的进程借助Linux内核之间进行相互的通信。

第3章  进程间通信的方式详解

3.1 虚拟文件系统通信

        这种方式,通过独立于用户进程之外的、由内核管辖的虚拟文件系统中的文件进行通信。

(1)虚拟文件的类型

  • 磁盘文件
  • 内存文件
  • 设备文件

(2)标识

  • 全局性标识:文件路径path

        fd =  creat(const char *filename,mode_t mode)

        fd =  open(const char *pathname, int oflag, … /* mode_t mode */);

  • 进程内标识:文件描述标识fd

(3)通信的过程

  1. 进程A和B创建和打开相同的虚拟文件,并在各自的内存空间中获得文件句柄id
  2. 进程A通过自己的文件句柄向虚拟文件中写入内容
  3. 进程B通过自己的文件句柄从虚拟文件中读入内容

(4)适用范围

  • 不同进程共享配置文件
  • 不同进程共享硬件外设

(5)优点:

  • 简单
  • 直观
  • 内核文件通信的基本思想被其他通信方式所借鉴。

(6)缺点

  • 数据传输效率低:数据需要经过复杂的虚拟文件系统,数据的传输效率低下。
  • 数据访问效率低:文件中的数据只能顺序访问,不能按照随机的方式访问。
  • 实用范围有限:不适合大规模内存数据的传送。

3.2 无名管道:pipe 通信

(1)管道的类型

        管道分为无名管道,有名管道,有名管道又称为FIFO.

  • 无名管道:

        是一种半双工的通信方式, 数据只能单向流动, 而且只能在具有亲缘关系的进程间使用. 进程的亲缘关系一般指的是父子关系。当一个进程创建了一个管道, 并调用fork创建自己的一个子进程后,父进程关闭读管道端,子进程关闭写管道端,这样提供了两个进程之间数据流动的一种方式。无名管道的标识可以在父子进程之间传递。

  • 有名管道:

        也是一种半双工的通信方式, 但是它允许无亲缘关系进程间的通信。不同进程之间可以通过一个全局性的key来标识。

(2)说明

  • 管道由pipe函数创建
  • 管道的本质是伪文件(不占用磁盘空间,只占用内存)
  • 管道由两个文件描述符的引用,一个fd[0]读,一个fd[1]写
  • 数据从管道的一端fd[1]写入,从管道的另一端fd[0]读出。fd的方向是固定的。

  • 管道的原理是内核缓冲区(4k)借助环形队列机制实现,一个读环形队列,一个写环形队列。

(3)无名管道案例

3.3 有名管道:FIFO 通信

(1)概述

FIFO(First In First Out)一种先入先出(读写数据是只能顺序写入顺序读出)的数据缓存器读写数据时,其内部读写指针自动加1,因此没有外部地址线,使用简单。读写指针是分开的。

在上图中, CPU#1代码写进程;CPU#2代表读进程。D-IN和D-OUT代表要写入FIFO的内存数据。

FIFO使用管道文件来标识管道,文件的内容被放进了内存缓冲区,通过read write函数来对管道文件进行读写。

不像pipe仅能用在父子进程这种有亲缘关系的进程之间, FIFO则可以用于任意两个进程之间。

两种管道目前只是实现了半双工的通信模式, 一侧写的时候另外一侧读就会阻塞等待。如果FIFO里无数据, 读进程会阻塞, FIFO里数据满了写进程同样会阻塞。

(2)全局标识: 全路径文件名

创建管道文件 int mkfifo(const char *name,mode_t mode)

(3)有名管道FIFO案例 – 写进程


  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #define FIFO_NUM1 "/tmp/fifonum1" //FIFO跨进程标识
  10. #define MAX_BUFFER_SIZE 100
  11. int main(

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年9月26日
下一篇 2022年9月26日

相关推荐