我有一个关于我经常面对的情况的问题。有时,我必须实现各种基于TCP的协议。它们中的大多数定义了以公共报头开头的可变长度数据包(数据包ID、长度、有效载荷或其他类似的东西)。显然,可以有两种方法来读取这些数据包:
之后解析缓冲区
显然,第一种方法很简单,但需要对read() (可能更多)进行两次调用。第二个问题稍微复杂一些,但需要较少的调用。
问题是:第一种方法对性能的影响是否严重到足以让人担心?
发布于 2011-02-24 14:40:36
得到你的答案最好的方法是测量。strace程序用于测量系统调用时间是很合适的。使用它本身会增加很多开销,但是如果您只是比较一个recv的成本和两个的成本,那么它应该是相当有意义的。使用-tt选项获取时间。或者您可以使用-c选项来获得用于syscall的时间的概述。
一个更好的测量方法,尽管更多的是一个学习曲线,是oprofile。
还请注意,如果您确实认为缓冲是值得的,您可能可以使用fdopen和stdio函数来处理它。这非常简单,如果您只处理单个连接或每个连接有一个线程/进程,但如果您想使用select/poll-based模型,则会很好地工作。
发布于 2011-02-24 10:31:48
是的,与内存副本相比,系统调用通常比较昂贵。在x86体系结构上尤其如此,在RISC机器(arm、mips、.)上也有争议。
老实说,除非您必须每秒处理数百或数千个请求,否则您将很难注意到两者之间的差别。
根据具体的协议,混合方法可能是最好的。当协议使用大量的小数据包和较小的数据包时,您可以读取报头和部分数据量。当它是一个小包的时候,你通过避免一个大的memcpy而赢,当这个包很大的时候,你只为这种情况发出第二个syscall就赢了。
发布于 2011-02-24 19:52:09
如果您的应用程序是一台能够同时处理多个客户端的服务器,并且使用非阻塞套接字在一个线程中处理多个客户端,那么您别无选择,只能在套接字准备好进行读取时只发出一个recv() syscall。
这样做的原因是,如果您一直在循环中调用recv(),并且客户端发送了大量数据,那么可能发生的情况是,您的recv()循环可能会阻止线程执行任何其他操作。例如,recv()从套接字中读取一定数量的数据,确定缓冲区中现在有一个完整的消息,并将该消息转发给回调。回调以某种方式处理消息并返回。如果您再次调用recv(),那么在回调处理之前的消息时,可能会有更多的消息到达。这将导致一个套接字上的一个繁忙的recv()循环,从而阻止线程处理任何其他挂起的事件。
如果应用程序中的套接字读取缓冲区小于内核套接字接收缓冲区,则此问题更加严重。换句话说,内核接收缓冲区的全部内容不能在一个recv()调用中读取。坊间证据表明,当2Mb内核套接字接收缓冲区有一个16 on的用户空间缓冲区时,我在繁忙的生产系统上遇到了这个问题。连续发送多条消息的客户端会阻塞recv()循环中的线程几分钟,因为在处理刚读取的消息时会出现更多的消息,从而导致服务中断。
在这种事件驱动的体系结构中,最好让用户空间读取缓冲区与内核套接字接收缓冲区的大小相等(或最大消息大小,以较大者为准),这样内核缓冲区中的所有可用数据都可以在一个recv()调用中读取。这可以通过执行一个recv()调用来工作,处理用户空间读取缓冲区中的所有完整消息,然后将控制返回给事件循环。这样,具有大量传入数据的连接并不会阻止线程处理其他事件和连接,而是循环处理所有可用数据的连接。
https://stackoverflow.com/questions/5103282
复制相似问题