linux用户间通信命令的简单介绍( 三 )


你操作了后,你会发现命令执行后就停在这了,这是因为管道里的内容没有被读取,只有当管道里的数据被读完后,命令才可以正常退出 。
于是 , 我们执行另外一个命令来读取这个管道里的数据:
可以看到 , 管道里的内容被读取出来了,并打印在了终端上,另外一方面,echo 那个命令也正常退出了 。
我们可以看出 , 管道这种通信方式效率低,不适合进程间频繁地交换数据 。当然,它的好处,自然就是简单 , 同时也我们很容易得知管道里的数据已经被另一个进程读取了 。
前面说到管道的通信方式是效率低的,因此管道不适合进程间频繁地交换数据 。
对于这个问题,消息队列的通信模式就可以解决 。比如 , A 进程要给 B 进程发送消息,A 进程把数据放在对应的消息队列后就可以正常返回了,B 进程需要的时候再去读取数据就可以了 。同理,B 进程要给 A 进程发送消息也是如此 。
再来,消息队列是保存在内核中的消息链表 , 在发送数据时,会分成一个一个独立的数据单元,也就是消息体(数据块),消息体是用户自定义的数据类型,消息的发送方和接收方要约定好消息体的数据类型,所以每个消息体都是固定大小的存储块,不像管道是无格式的字节流数据 。如果进程从消息队列中读取了消息体,内核就会把这个消息体删除 。
消息队列生命周期随内核,如果没有释放消息队列或者没有关闭操作系统 , 消息队列会一直存在,而前面提到的匿名管道的生命周期,是随进程的创建而建立,随进程的结束而销毁 。
消息这种模型,两个进程之间的通信就像平时发邮件一样 , 你来一封,我回一封,可以频繁沟通了 。
但邮件的通信方式存在不足的地方有两点,一是通信不及时,二是附件也有大小限制 , 这同样也是消息队列通信不足的点 。
消息队列不适合比较大数据的传输,因为在内核中每个消息体都有一个最大长度的限制 , 同时所有队列所包含的全部消息体的总长度也是有上限 。在 Linux 内核中,会有两个宏定义 MSGMAX 和 MSGMNB,它们以字节为单位,分别定义了一条消息的最大长度和一个队列的最大长度 。
消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销,因为进程写入数据到内核中的消息队列时,会发生从用户态拷贝数据到内核态的过程,同理另一进程读取内核中的消息数据时 , 会发生从内核态拷贝数据到用户态的过程 。
消息队列的读取和写入的过程,都会有发生用户态与内核态之间的消息拷贝过程 。那共享内存的方式,就很好的解决了这一问题 。
现代操作系统,对于内存管理 , 采用的是虚拟内存技术,也就是每个进程都有自己独立的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中 。所以,即使进程 A 和 进程 B 的虚拟地址是一样的 , 其实访问的是不同的物理内存地址,对于数据的增删查改互不影响 。
用了共享内存通信方式,带来新的问题,那就是如果多个进程同时修改同一个共享内存 , 很有可能就冲突了 。例如两个进程都同时写一个地址 , 那先写的那个进程会发现内容被别人覆盖了 。
为了防止多进程竞争共享资源,而造成的数据错乱 , 所以需要保护机制 , 使得共享的资源,在任意时刻只能被一个进程访问 。正好,信号量就实现了这一保护机制 。
信号量其实是一个整型的计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据 。

推荐阅读