文章目录
一 进程通信概念
进程间为什么需要通信?计算机给进程在内存中分配的位置是不一样的,正是因为这种隔离,不像线程那样共享资源,因此需要通信。但是进程之间互相并不知道其他进程的位置,所以进程之间交换数据必须通过内核,在内核中开辟一块缓冲区,进程一把数据从用户空间拷贝到缓冲区,进程二从缓冲区把数据读走,内核提供的这种机制称为进程间通信。
举个例子:上古时期有一个村落,地广人稀,每一户人家间隔都很远,互相不知道住在哪(进程之间不知道位置)。但是村子有村长管理,村长住的地方是村子的集会中心(内核),村长知道所有人的地址,因此不同家庭之间想要交流就需要先告诉村长,由村长把他们要讲的消息存起来(缓存区),让另一户人家来读。
二 进程间通信方式
2.1 管道/匿名管道
- 是一种半双功的通信方式,只能单向传输数据,如果双方需要通信,那么需要开两个管道
- 对于管道两边的进程来说,就是一种文件,是一种独立的文件系统,不属于任何文件系统,自成一脉并且只存在于内存中
- 由于没有名字,所以只能在具有亲缘关系的进程间通信
- 读写时,进程A将数据写入管道尾,进程B将数据从管道头读出
管道的实质
- 说白了,管道还是一种内核缓存区
- 可以将管道看成一个循环队列,首尾都是自动增加的,不可以随意修改,读空之后管道的数据就不存在了
- 如果将管道读空或者写满,读进程或者写进程就会进入等待队列,当管道中新数据写入或者有空余位置时,就唤醒读进程或者写进程
局限
- 半双工,只支持单向数据流
- 只能用于亲缘进程间
- 没有名字
- 大小有限
- 管道传送的是无格式字节流,要求进程双方传输前就约定好数据格式
2.2 有名管道(FIFO)
- 为客服无名管道的缺点而提出的有名管道
- 提供了一个路径名与之关联,以有名管道的文件形式存在内存中,通信双方只要知道该路径名便可通过路径名访问该管道实现通信
- 有名管道的名字存在文件系统中,内容存在内存中,严格遵循FIFO
- 一个进程以读方式打开管道时,另一个进程必须以写方式打开此管道,反之亦然,否则进程会阻塞
2.3 信号
- 是linux系统中进程间用于通信的一种常见方式,可以在任意时刻由一个进程发送给任意一个进程,不用管该进程是否在运行
- 如果目标进程不在执行状态,则内核会先保存信号,等该进程执行并通知内核后内核才会将信号发给该进程
- 同时目标进程还可以设置是否阻塞信号,如果阻塞,那么该信号的传递被延迟,直到阻塞取消该信号才会被发送
来源
信号主要有来源:
- 硬件层面:ctrl+c退出和硬件异常
- 软件层面:终止进程,其他进程调用kill函数,软件异常产生
2.4 消息队列
- 与管道不同,只存在于内核中,由链表组成
- 只有在内核重启或者显示删除队列时才会真正消失
- 在写入时不需要有一个进程开启同步地读出
特点
- 是消息的链表,存在于内存中并由消息队列标识符标记,有自己的格式
- 和管道一样遵循先进先出策略
- 允许多个进程同时读写消息
- 不一定要按照先进先出读取,也可以按照消息类型读取,更加灵活
- 缓存大小不受限,并且写入的消息可以带类型,因此承载更多的信息
- 目前主要有两种消息队列,一种是POSIX消息队列,一种是System V消息队列。目前System V消息队列被广泛使用。存在于系统内核,只有内核重启或者人工删除时才会队列才会被删除
2.5 共享内存
- 为了解决各种通信方式效率不高的问题,提出来一种共享内存方式,多个进程可以共享一块内存,是目前效率最高的通信方式
- 在内核中开辟一块内存,需要访问的进程可以将其映射为自己的私有地址。进程就可以直接读写这块空间,不需要拷贝数据,效率大大提高
- 多进程共享内存后,就容易出现互相干扰的问题,就和多线程共享进程内存是一个道理,因此常需要信号量来进行进程的同步和互斥
2.6 信号量
互斥
是指某资源同时只允许一个访问者进行访问,具有唯一性和排他性。但是互斥无法限制访问者对资源的访问顺序。
同步
在互斥的基础上(大部分情况下),实现访问者对资源的有序访问
信号量的实现
- 信号量的目的就是为了实现同步
- 实现方式是计数器,通过计数来保证多访问的有序性
2.7 Socket套接字
- 用于不在一台计算机,但可以通过网络连接的两台计算机上的进程实现通信
- 是tcp/ip协议中实现网络进程通信的基本单元,可以看做不同主机进程间进行通信的端点
套接字三个参数
- 本机ip地址和目标ip地址
- 使用的传输层协议(tcp/udp)
- 端口号
连接过程(类比于tcp三次握手)
- 服务器监听:服务器端并不会定位客户端套接字,而是监听自己的套接字
- 客户端请求:客户端要定义好想要通信的服务端套接字,包括ip和端口号,然后提出连接请求
- 连接确认:服务器端监听到了来自客户端的请求后,开一个新的线程把服务器端套接字描述发送给客户端,客户端确认此描述后就连接好了。然后服务器端重新进入监听状态,监听来自其他客户端套接字的请求