进程间通信

进程间通信(InterProcess Communication)

讲得很好的链接

进程间通信的概念

  • 每个进程各自有不同的用户地址空间, 任何一个进程的全局变量在另一个进程中都看不到, 所以进程之间要交换数据必须通过内核, 在内核中开辟一块缓冲区, 进程1把数据从用户空间拷贝到内核缓冲区中, 进程2再从内核缓冲区中把数据读走, 内核提供的这种机制称为进程间通信(IPC, InterProcess Communication)

进程间通信的7种方式

  1. 管道/匿名管道(pipe)

    • 管道是半双工的, 数据只能向一个方向流动; 需要双方通信时, 需要建立起两个管道
    • 只能用于父子进程或者兄弟进程之间的通信(具有亲缘关系的进程)
    • 管道单独构成了一种独立的文件系统: 管道对于管道两端的进程而言, 就是一个文件, 但它不是普通的文件, 它不属于某种文件系统, 而是独立存在于内存中的
    • 数据的读出和写入: 一个进程向管道中写的内容被管道另一端的进程读出; 写入的内容每次都添加在管道缓冲区的末尾, 并且每次都是从缓冲区的头部读出数据
    • 管道的实质: 是一个内核缓冲区, 进程以先进先出的方式从缓冲区存取数据; 管道也可以看作是一个循环队列, 读写位置都是自动增长, 不能随意改变, 一个数据只能被读一次, 读出来后在缓冲区就不存在了
    • 管道的缓冲区是有限的(在创建管道时, 为缓冲区分配一个页面大小4K左右)
    • 管道传送的是无格式字节流, 这就要求管道的读写方必须事先约定好数据的格式, 比如多少字节算作一个消息
  2. 有名管道

    • 有名管道的文件形式存在于文件系统中, 只要能够访问该路径, 进程就能够彼此进行通信, 有名管道的名字存在于文件系统中, 内容存放在内存中.
    • 管道是特殊类型的文件, 在满足先入先出的原则条件下可以进行读写, 但不能进行定位读写
    • 匿名管道是特殊类型的文件, 只能在有亲缘关系的进程间通信; 有名管道以磁盘文件的方式存在, 可以实现本机任意两个进程通信
    • 无名管道阻塞问题, 写太大或者读时为空将阻塞, 另一端断开将自动退出
    • 有名管道在打开时需要对方的存在, 否则将阻塞; 以读方式打开某管道, 必须有一个进程以写方式打开管道, 否则阻塞; 当前进程读,当前进程写, 不会阻塞.
  3. 信号(Signal)

    • 信号是Linux系统用于进程间互相通信或者操作的一种机制, 信号可以在任何时候发给某一进程, 而无需知道该进程的状态
    • 如果该进程当前并未处于执行状态, 该信号就由内核保存起来, 直到进程恢复执行并传递给它为止
    • 如果一个信号被进程设置为阻塞, 则该信号的传递被延迟, 直到其阻塞被取消时才被传递给进程
    • 信号是软件层次上对中断机制的一种模拟, 是一种异步通信方式, 信号可以在用户进程和内核之间直接交互, 内核可以利用信号来通知用户空间的发生了那些系统事件, 信号事件主要有两个来源:
      • 硬件来源: 用户按键键入ctrl+c退出, 硬件异常如无效的内存访问
      • 软件终止: 终止进程信号, 其他进程调用kill函数, 软件异常产生信号.
    • 目的进程接收到信号后, 将根据当前进程对此信号设置预处理方式, 暂时终止当前代码的执行, 保护上下文(主要包括: 临时寄存器数据, 当前程序位置以及当前CPU的状态), 转而执行中断服务程序, 执行完成后在恢复中断的位置
    • 对于抢占式内核, 在中断返回时将引发新的调度
  4. 消息队列(Message) system V消息队列和POSIX 消息队列

    • 消息队列是存放在内核中的消息链表, 每个消息队列由消息队列标识符表示
    • 与管道不同的是消息队列存在于内核中(无名管道存在于内存的文件中, 有名管道存在于实际的磁盘或者文件系统中), 只有在内核重启(操作系统重启)或者显示地删除消息队列, 该消息队列才会被真正的删除.
    • 另外与管道不同的是, 消息队列在某一个进程向另一个队列写入消息之前, 并不需要另外某个进程在该队列上等待消息的到达
    • 消息队列特点总结:
      • 消息队列是消息的链表, 具有特定的格式, 存放在内核中, 并有消息队列标识符标识.
      • 消息队列允许一个或多个进程向它写入与读取数据
      • 管道和消息队列的通信数据都是先进先出的原则
      • 消息队列可以实现消息的随机查询, 消息不一定要以先进先出的次序读取, 也可以按消息的类型进行读取, 比FIFO更具有优势
      • 消息队列克服了信号承载信息量少, 管道只能承载无格式字节流以及缓冲区大小有限的问题
      • 目前主要有两种类型的消息队列: POSIX消息队列以及System V消息队列; System V消息队列目前被大量使用, 系统V消息队列是随内核持续的, 只有在内核重启或人工删除时, 该消息队列才会被删除.
  5. 共享内存(share memory)

    • 使得多个进程可以直接读写一块内存空间, 是最快的可用IPC形式, 是针对其他通信机制运行效率较低而设计的.
    • 为了在多个进程间交换信息, 内核专门留出了一块内存区, 可以由需要访问的进程将其映射到自己的私有地址空间
    • 进程就可以直接读写这一块内存而不需要进行数据的拷贝, 从而大大提高了效率
    • 由于多个进程共享一段内存, 因此需要依靠某种同步机制来达到进程的同步及互斥
  6. 信号量(semaphore) -- PV原语

    • 信号量是一个计数器, 用于多进程对共享数据的访问, 信号量的意图在于进程间同步
    • 为了获得共享资源, 进程需要执行一些操作:
      • 创建一个信号量: 调用者指定初始值(二值信号量通常是1)
      • 等待一个信号量: 该操作会测试这个信号量的值, 如果小于0, 就阻塞, 也称为P操作.
      • 挂出一个信号量: 该操作将信号量的值加1, 也称为V操作
    • 信号量通常是在内核中实现:
      • Posix有名信号量
      • Posix基于内存的信号量(存放在共享内存区中)
      • System V信号量(在内核中维护)
    • 信号量与互斥量的区别:
      • 互斥量用于线程的互斥, 信号量用于线程的同步; 互斥量和信号量本质区别是, 互斥和同步之间的区别.
      • 互斥: 是指某一资源同时只允许一个访问者对齐进行访问, 具有唯一性和排它性; 但互斥无法限制访问者对资源访问的顺序, 即访问是无序的;
      • 同步: 是指在互斥的基础上(大多数情况下), 通过其他机制实现访问者对哦资源的有序访问
      • 大多数情况下, 同步已经实现了互斥, 特别是所有写入资源的情况必须是互斥的, 少数情况下是可以允许多个访问者同时访问资源.
  7. 套接字

    • 套接字的特性由3个属性确定: 域, 端口号, 协议类型
  • Linux下的同步机制
    • 原子操作
    • 信号量
    • 读写信号量
    • 自旋锁(spinlock)

死锁

  • 死锁: 死锁是指两个或两个以上的进程(或线程)在执行过程中, 因争夺资源而造成的一种互相等待的现象, 若无外力作用, 他们将无法推进下去
    • 因为系统资源不足
    • 进程运行顺序不合适
    • 资源分配不当等
  • 会因为争夺有限资源陷入死锁: (死锁必要条件)
    • 互斥条件: 一个资源每次只能被一个进程使用
    • 请求与保持条件: 一个进程因请求资源而阻塞时, 对已获得的资源保持不放
    • 不剥夺条件: 进程已获得的资源, 在未使用完之前, 不能强行剥夺
    • 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系
  • 进程A占有对象1的锁,进程B占有对象2的锁,进程A需要对象2的锁才能继续执行,所以进程A会等待进程B释放对象2的锁,而进程B需要对象1的锁才能继续执行,同样会等待进程A释放对象1的锁,由于这两个进程都不释放已占有的锁,所以导致他们进入无限等待中
原文地址:https://www.cnblogs.com/longjiang-uestc/p/9605619.html