目录
前言:
第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)通信的过程
- 进程A和B创建和打开相同的虚拟文件,并在各自的内存空间中获得文件句柄id
- 进程A通过自己的文件句柄向虚拟文件中写入内容
- 进程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案例 – 写进程
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define FIFO_NUM1 "/tmp/fifonum1" //FIFO跨进程标识
- #define MAX_BUFFER_SIZE 100
-
- int main(
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!