Linux 内核提供了不少进程间通信的方式,其中最简单的方式就是管道,管道分为「匿名管道」和「命名管道」。
匿名管道
- 顾名思义它没有名字标识,匿名管道是特殊文件,只存在于内存,没有存在于文件系统中
- shell 命令中的「|」竖线就是匿名管道,通信的数据是无格式的流并且大小受限
- 通信的方式是单向的,数据只能在一个方向上流动,如果要双向通信,需要创建两个管道
- 匿名管道只能用于存在父子关系的进程间通信
- 匿名管道的生命周期随着进程创建而建立,随着进程终止而消失
命名管道
- 突破了匿名管道只能在亲缘关系进程间的通信限制,因为使用命名管道的前提,需要在文件系统创建一个类型为 p 的设备文件,那么毫无关系的进程就可以通过这个设备文件进行通信
- 不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中的,另一个进程读取数据时自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持 lseek 之类的文件定位操作
消息队列
- 克服了管道通信的数据是无格式的字节流的问题
- 消息队列实际上是保存在内核的「消息链表」,消息队列的消息体是可以用户自定义的数据类型,发送数据时,会被分成一个个独立的消息体,当然接收数据时,也要与发送方发送的消息体的数据类型保持一致,这样才能保证读取的数据是正确的
- 消息队列通信的速度不是最及时的,毕竟每次数据的写入和读取都需要经过用户态与内核态之间的拷贝过程
共享内存
- 可以解决消息队列通信中,用户态与内核态之间数据拷贝过程带来的开销
- 它直接分配一个共享空间,每个进程都可以直接访问,就像访问进程自己的空间一样快捷方便,不需要陷入内核态或者系统调用,大大提高了通信的速度,有最快的进程间通信方式之名
- 但是便捷高效的共享内存通信,会带来新的问题,多进程竞争同个共享资源会造成数据的错乱
信号量
- 保护共享资源,以确保在任何时刻只能有一个进程访问共享资源,这种方式就是互斥访问
- 信号量不仅可以实现访问的互斥性,还可以实现进程间的同步,信号量其实是一个计数器,表示的是资源个数,其值可以通过两个原子操作来控制,分别是 P 操作和 V 操作
信号
- 与信号量名字很相似,但功能不一样
- 信号是异步通信机制,信号可以在应用进程和内核之间直接交互,内核也可以利用信号来通知用户空间的进程发生了哪些系统事件
- 信号事件的来源主要有硬件来源(如键盘 Cltr + C )和软件来源(如 kill 命令)
- 一旦有信号发生,进程有三种方式响应信号:执行默认操作、捕捉信号、忽略信号。其中有两个信号是应用进程无法捕捉和忽略的,即 SIGKILL 和 SIGSTOP,这是为了方便我们能在任何时候结束或停止某个进程
Socket
- 前面说到的通信机制,都是工作于同一台主机,如果要与不同主机的进程间通信,那么就需要 Socket 通信了
- Socket 实际上不仅用于不同的主机进程间通信,还可以用于本地主机进程间通信,可根据创建 Socket 的类型不同,分为三种常见的通信方式:基于 TCP 协议的通信方式、基于 UDP 协议的通信方式、本地进程间通信方式
假如 CPU 跑到 100%,你的解决思路是什么?
- 先通过 top 命令,定位到占用 CPU 高的进程
- 然后通过 ps -T -p <进程ID> 命令找到进程中占用比较高的线程(用于显示指定进程的线程信息,包括线程ID、优先级、状态等)
- 然后通过 jstack 命令去查看该线程的堆栈信息
- 根据输出的堆栈信息,去项目中定位代码,看是否发生了死循环而导致 CPU 跑到 100%