2020年12月

进程通信的应用场景

数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。

共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。

通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程通信方式

  1. 管道(pipe)

    • 普通管道:单工,只能单向传输;只能在家族进程间使用,由父进程创建。
    • 流管道s_pipe:半双工,可以双向传输;只能在家族进程间使用。
    • 命名管道name_pipe:无限制。
  2. 信号量(semophore)

    • 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某个进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  3. 消息队列(message queue)

    • 消息队列是消息的链表,存在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限制等缺点。
  4. 信号(signal)

    • 信号用于通知接收进程某个事件已经发生。
  5. 共享内存(shared momory)

    • 共享内存是映射一段能被其它进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的ipc方式,与信号量配合使用,实现进程间的同步与通信。
  6. 套接字(socket)

    • 唯一可跨机器的ipc方式。

各通信方式的原理及实现

管道

是内核管理的一个缓冲区,连接进程的输入和输出,linux下为4k,环形结构,以便循环利用。当管道中没有信息的话,读取进程会等待,直到有数据放入;当管道放满信息的时候,放入信息的进程会等待,直到另一端进程取出信息。

细节

linux中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和vfs的索引节点inode。
通过将两个file结构指向同一个临时的vfs索引节点,而这个vfs索引节点又指向一个物理页面而实现的。

读写操作

管道实现的源代码再fs/pipe.c中,在pipe.c中有很多函数,重要的是管道读pipe_read()和管道写pipe_write().
管道写通过将字节复制到vfs索引节点指向的物理内存而写入数据,而管道读则通过复制物理内存中的字节而读出数据。当然,内核必须利用一定的机制同步对管道的访问,为此,内核使用了锁,等待队列和信号。

当写进程向管道中写入时,它利用标准的库函数write(),系统根据库函数传递的文件描述符,可找到该文件的file结构。
file结构中指定了用来进行写操作的函数(即写入函数)地址,于是,内核调用该函数完成写操作。
写入函数在内存中写入数据之前,必须检查vfs索引节点中的信息,同时满足如下条件时,才能进行实际的内存复制工作:
内存中有足够的空间可容纳所有要写入的数据;
内存没有被读程序锁定。

没有实际排查过,找了一篇网上的文章。
https://www.cnblogs.com/grey-wolf/p/10936657.html

  1. close_wait 危害
  2. 问题分析
  3. 解决方案

危害

问题分析

为什么会出现close_wait?
如果服务端处理时间很长,客户端等不到服务端返回,超时了,主动断开连接,发FIN,服务端回复ACK后进入close_wait

解决方案

  1. 减少接口耗时
  2. 收到FIN后,主动发FIN
    反正是服务端的问题。