我的选择是选择
![](https://i-blog.csdnimg.cn/blog_migrate/bab5984e27b9562e1bda6301310c1f23.png)
Select,poll和epoll都是实现类似目的的Linux系统调用,它们提供了一种执行异步I / O的有效方法。
换句话说,它们等待某个事件在文件描述符上发生。 通常,该文件描述符代表一个网络套接字,我们正在等待数据从另一台计算机通过TCP或UDP传递。
在这个时代,我们要做的很多事情都是通过网络进行的,这些异步系统调用对于大多数程序员而言至关重要。
实际上,很少有开发人员听说过其中任何一个。 实际上仍然很少直接使用它们。 尽管如此,几乎每个使用Node,Ruby,Go或Python的人都依赖于它们,而并不了解。
我们大多数人可能更喜欢避免大部分时间直接使用系统调用。 但是,我们使用的库包装程序通常是微不足道的,只要需要降低性能或灵活性,我们可以在任何时候依赖本机接口。
我敢打赌,几乎所有体面的开发人员都会对Linux线程的知识有足够的信心,可以不加思索地使用pthread.h。 我还要下注,如果他们必须快速创建基于epoll的无错误应用程序,那么将有近100%的开发人员不寒而栗。
首先,我认为我们应该看看Linux并发最佳实践中的另一个生物线程。
让我们看一下使用线程的3个示例。 首先,使用Python(“高级”语言)和Rust(“低级”语言)的标准库,然后使用Linux为其一流公民C提供的本机接口。
蟒蛇
注意:虽然可能会听到由于GIL导致Python线程不是“真正的线程”,但它们在实现中仍使用pthread。 他们在python的非GIL实现中像“真实”线程一样工作没有问题。
锈
C(POSIX API)
显然,使用本机pthread API有点不方便,但是pthread的包装仍然与直接使用它非常相似,只是将其简化了一点。 如果您了解如何使用任何语言的标准库中的线程,那么您将了解如何使用本机API。
接下来,让我们看看Python和Rust中异步I / O的“最低级别”方法,然后让我们看看如何使用本机API来实现。
在此示例中,我们将研究通过TCP与2个不同地址并发通信。
蟒蛇
锈
在这里我应该提到我作弊了一点。 由于Future和Tokio不在Rust的标准库中。 但是,rust还没有asyncio标准,关于实现一个标准的讨论主要围绕将tokio和Futures集成到标准库中。 因此,极有可能在rust中使用asyncio TCP客户端的最终版本与上述代码非常相似。
C(POSIX API)
问题来了。
我很想向您展示一种使用民意测验的方法,该方法以轮询,epoll或select的方式通过TCP同步完成两个请求,但我可能会出错。 此外,该代码将很长并且很难浏览。
这是一个使用轮询的非常全面的TCP客户端。 注意到问题了吗? 它跨越700行代码。
当然,部分原因是C的故障和本机网络功能的故障。 但是编写一个简单的TCP客户端并不难,这是我们必须包含asyncio的部分,这使得不可能正确。
我会挑战任何其他认为不这样的读者,让我发现使用民意测验的惯用法,甚至接近Rust和Python方法的简单性。
![](https://i-blog.csdnimg.cn/blog_migrate/79e6041faedb3929a243146d8a77dfc9.png)
如何修复异步系统调用
如果上面的C代码对于您来说太难读了,让我用一些较小的代码片段来分解异步syscall的用法:
- 使文件描述符不阻塞, 如此处所示 。
- 循环浏览非阻塞文件描述符,看看是否有任何事件发生在它们上面。 如此处所示,尽管只有一个fd 。
- 如果和事件已发生,请对其进行处理,然后进行循环。 如果没有任何反应,请继续。
这似乎不是一个糟糕的界面,除了以下事实:了解如何编写基于轮询/ epoll /选择的代码以及如何编写是两个完全不同的概念。
即使假设接口更简单,对于使用任何其他asyncio库的任何人来说,它仍然是完全陌生的。 如果要使用本机API,则实际上是从零开始。
如果有更简单的方法……
好吧,实际上所有其他库都有一种使用回调和/或期货处理异步的方式。 这是其他所有支持本地异步的语言(例如Go,Python和Node)的实现方式。 这是所有asyncio库执行此操作的方式,可以是C,C ++,Java,Scala,Ruby,Perl,Php,Rust或任何其他语言的库。
但是,Linux是否掩盖了所有这些复杂性,难道不是犹太人吗? 要拥有用户友好的API,可以使用对真正的编程美学不了解的肮脏农民。
我对此答复,看看pthread。 我们使用pthread_create
代替clone
syscall的方法相同。 通过函数指针的魔力,我们可以在异步scyall上提供一个用户友好的包装,而不会失去任何功能。
当然,在某些情况下可能需要使用原始syscall,这与在某些情况下可能需要使用clone
而不是fork
或pthread
情况类似。
但是对于99.9%的情况,我认为我们对以下方面的包装感到满意:
poll_events(underlying_syscall, fds_array, callback)
回调函数将被标准化以接收文件描述符和特定于基础syscall的任何其他args。 例如,两次poll
短片(事件和回避)。
也许此接口甚至可以为同一功能提供一种标准化方法,以使其可与3个syscall一起使用。
无论哪种方式,我都不打算在这里规定本机Linux异步接口的设计。 我只是建议我们可能需要一个更简单的方法。
如果人们不能使用您的API,则可能是您的API垃圾的信号,而不是您的用户是愚蠢的信号。 如果只有少数几个库维护人员可以理解和使用面包和黄油的系统调用,则意味着您需要更好的API。
自从select出现以来,就有两次机会提供更好的界面。 一种是在引入民意测验时,另一种是在引入epoll测验时。 但是,我们陷入了3种繁琐的异步机制,它们的API和实现略有不同,但都无法使用。
如果您喜欢这篇文章,您可能还会喜欢:
From: https://hackernoon.com/if-i-were-to-select-the-worst-linux-syscall-669529b233e9