本文翻译自:https://idea.popcount.org/2017-02-23-socket-api-thoughts/
前一段时间,我写了篇关于select()系统调用发展历史的文章。 该文章引发了一些有趣的讨论。
文章和讨论花了我不少心思,但我最终明白了什么是核心问题:
Unix进程是否是CSP风格的进程? 文件描述符是CSP派生的“通道”吗? “select()”相当于ALT语句吗?
直接的答案是“不”。 CSP比Unix更年轻。 正如Tony Garnock-Jones指出的,关于CSP的第一篇文章发表于1978年,文件描述符自从Unix的早期就已经出现。
文件描述符不是通道,他们应该是什么? CSP channel通常是可组合的,而我认为Unix的select不是。 这是第二点:
使用“select()”的Unix程序不使用组合。 但也许他们可以用? “select()能否以允许组合性的方式实现”?
当在Golang中编程时,可以使用ALT语句和内置channel。 一般来说,golang channel是可组合的。 类似的事情在Erlang中 - 你可以通过巧妙地使用gen_server进程来避免复杂的状态机。
Unix进程模型为何不能如此? 为什么我们不能以组合Golang或Erlang相同的方式来组合Unix进程?
我会直觉地指责select系统调用,但问题更深入。
缓冲代理
我们不能放弃select? 我们可以没有它吗? 在前面的文章中我们提到了两个例子:
inet守护进程
控制台复用
还有很多问题都离不开select。 David Wragg提到“缓冲代理”问题:
想象一个进行缓冲的进程。 考虑一个简单的程序,从一个流读取数据,缓冲它,并写入另一个流。 如果使用它们之间的管道将它拆分为两个进程,则会受到内核管道缓冲区大小的限制。 为了具有任意的缓冲区大小,它必须在单个进程中进行输入流的可读性和输出流的可写性的select操作。
克里斯·西本曼在2010年的博客中提到了同样的问题。
空闲连接的内存消耗
今年早些时候,Chris提到了另一个需要select的问题:“空闲连接的内存消耗”:
这篇文章是Evan Klitzke的文章的后续:
这个内存消耗的论点在Golang Bug跟踪器中提出:
对我来说,这听起来像在介绍回调地狱。
BSD Socket API 改造
最后,Martin Sustrick针对纯粹阻塞套接字API写了这个RFC提案:
本文没有通过推动select问题到下一层来解决问题:
该备忘录假设已经存在有效的并发实现,其创建新的轻量级进程需要最多几百纳秒并且上下文切换需要几十纳秒。 注意,已经部署了这样的并发系统。 一个着名的例子是Golang的goroutine,但也有其他可用的。
我非常喜欢只暴露阻塞API给用户的想法。
最终想法
虽然Unix socket模型和CSP已经存在了将近40年,但它们不是各自封闭的。 在这方面有很多新想法:优化内存使用,找到合适的可组合性抽象,回到暴露纯粹阻塞API。
有疑问加站长微信联系(非本文作者)