Linux UDP套接字并发性能分析外文翻译资料

 2022-04-05 09:04

英语原文共 8 页,剩余内容已隐藏,支付完成后下载完整资料


Linux UDP套接字并发性能分析

摘要

几乎所有遍历Internet的DNS查询都通过UDP以自包含的小包进行传输。因此,在没有数据包排序限制的情况下,直觉就会说,向服务器添加线程并行会提高性能,但事实并非如此。本文研究序列化访问UDP套接字的问题,并指出数据包在内核级插入到套接字中的方式,这引入了线程使用的同步基元中的高级争用。作为一个天真的解决方案,我们提出了一个多队列接收器网络堆栈,它可以在多个线程从同一个套接字读取时提高处理UDP小包的性能。

1.简介

随着互联网的不断发展,研究和开发方面的努力一直专注于管理和交流大量(大)数据。其中一些努力包括在服务器端软件中使用并行计算(线程),旨在使服务器适应不断增长的需求。

尽管如此,Facebook [10]和Toshiba [8]已经表明,向服务器端应用程序(如memcached和BIND)分别引入线程并不一定意味着服务器在每个时间单元能够处理的查询数量有所增加。他们的工程师在Linux内核(这是两个研究中使用的操作系统)内部陈述了问题,但由于其复杂性,将分析留在后面的工作中。

MOSBENCH Project的工程师通过使用知名的不可扩展应用程序(如memcached和Apache)进行基准测试[4],分析了相同的行为。这些测试的结果表明,许多优化可以在应用程序本身完成,但其他一些服务器可能会受到I / O操作的瓶颈。进一步分析测试Linux内核中可扩展和不可扩展的锁类型,使得该团队得出结论:“传统的内核设计可能与实现多核计算机的可扩展性相兼容”。此外,这些结果在数学和实验上解释说,使用不可扩展的锁可能会导致整个系统性能崩溃。上述研究导致操作系统中的犯罪嫌疑人成为问题的根源,无法为网络通信提供可扩展的界面。因此,完全可扩展的网络堆栈将解决已经观察到的性能问题。

我们的主要贡献是在近期版本的Linux内核中研究这种不可扩展的线程问题,它指出了数据包在套接字中排队的方式,这引起了线程在同步原语中的高级争用。我们还提供基于每个套接字多个队列的朴素解决方案,适用于查询量小且自包含在唯一数据包(如DNS服务器)中的服务器。我们证明这个解决方案提高了从套接字读取并行线程的性能。

本文的结构如下:第三节简要介绍了操作系统如何从网络接收数据包。第四节重现了这个问题,并在内核中声明它。第五部分研究了套接字使用的自旋锁,它们在通过网络堆栈处理之后立即排入输入数据包,并在数据结构使用的同步模式中陈述问题。最后,介绍了DNS服务解决方案的实现,接下来是我们的结论和未来的工作。

2.相关工作

自20年左右以来,锁定多线程网络协议的影响一直在研究中。例如,Bjorkman和Gunningberg[2]研究了锁定系统在X-kernel仿真器中实现UDP和TCPoverIP和ETH协议时的效果,以前由Hutchinson和Peterson提出[7]。Bjorkman和Gunningberg还实现了该仿真器的并行版本,用于测量在处理传入和传出数据包时使用多个处理器所实现的性能增益。这些测量结果也让他们发现了x内核中的一些瓶颈,例如将信息从设备复制到内核内存。

Nahum等人对上述工作进行了扩展[9],在SilliconGraphics多处理器中运行并行版本的x-kernel,分析并行TCP实现中的校验和,排序和锁定的影响。他们得出结论:一个更简单的锁定系统和原子基元的使用可以在性能上产生很大的差异,它追求最大程度地避免由锁定造成的争用。

这些研究继续进行,Schmidt和Suda [11]使用ASX框架在SunOS中进行了测量。他们模拟了两种类型的消息并行体系结构:连接和基于消息的体系结构。他们得出结论:基于消息的并行性更适合于无连接应用程序,如DNS服务器。此外,他们声明同步成本对性能有重大影响,因此选择不正确的同步原语将大大降低获得的吞吐量。最近,威尔曼等。人。 [12]研究了在FreeBSD操作系统中默认实现的基于连接的并行性的影响,测量系统的吞吐量和可伸缩性以及这些度量如何受到锁定,缓存行为和调度程序开销的影响。这些测量结果让他们得出结论:只要添加了额外的连接,FreeBSD内核的单处理器版本就会降低其性能。

在同一行中,Han等人提出了MegaPipe,一种用于可扩展网络I / O的API [6]。这个新的API允许一些服务器软件(如memcached和nginx)达到吞吐量增长的75%。此外,他们发现面向消息的连接与小消息会产生更大的开销,并与更大的消息进行连接。这类服务的一个典型例子是DNS,它通过小型查询运行在UDP上。

在我们的工作中,我们的目标是通过研究Linux内核扩展以前的研究,以便在试图扩展BIND服务器时,隔离先前由东芝观察到的UDP套接字的序列化访问根。当添加线程来侦听单个打开的UDP套接字时,这通常被认为是不可扩展的I / O操作的标志。

3. LINUX网络堆叠工作

通过网络设备发送或接收数据包在发送之前或到达主机之后涉及大量处理。在本章中,我们将解释当数据包发送到网络或从数据包接收数据包时Linux使用的机制。 Linux中的套接字是一个内核数据结构,包括:状态字段,类型字段,标志字段,指向结构实例的指针proto_ops(包含指向实现套接字操作的函数的指针)以及指向结构套接字实例的指针。这最后一个结构与本研究相关,因为它定义了与分组的接收和传输相关的字段:

包含用于锁定整个套接字的自旋锁的结构socket_lock。

用于在发送之前(在向其他主机发送信息的情况下)或在接收之后(在来自网络的输入分组的情况下)存储分组的三个分组队列(接收,传输和积压)。 积压队列在接收队列被锁定的特殊情况下使用,其工作将在第V-A节中解释。 这些列表中的每一个都受到一个自旋锁的保护,该自旋锁保证了它与并发访问的一致性。

Memory Accounting字段用于跟踪套接字使用的内存量,特别是队列中的数据量。 此功能用于避免插槽填满整个内存,导致操作系统故障。

A.数据包接收

图1表示当数据包到达网络接口卡(NIC)时由Linux执行的任务。 该过程在图中从下到上进行。

图1:分组接收过程

注:(Main process:主要过程 Performed Actions:执行的操作 Upper Processing:上层处理 Packet enqueue:数据包入队 Lower Processing:底层处理 Layer:层 Raise:提升 Context:层面 Handler:处理程序)

在这个过程中可以确定三个主要任务:(1)IRQ上下文,当数据包到达NIC时启动,产生硬件中断(HardIRQ)并以软件中断(SoftIRQ)的时间表结束; (2)低层处理,包括执行先前调度的SoftIRQ,它通过层2(L2)到层4(L4)处理分组;和(3)高级处理,其中数据包在被应用程序读取之前在套接字队列之一中入队。这种设计旨在最大限度地减少处理HardIRQ的处理器花费的时间,推迟将来即刻的大部分工作[1]。

在下面的章节中,我们将研究同步原语如何影响接收数据包的一般性能。在[1],[13]中可以找到有关如何在发送和接收数据包时每个步骤中处理数据包的更多信息和详细信息。

B.内存记帐

每次内核发送或接收一些信息时,它都必须复制到内核的内存中,以便在通过设备发送或由用户读取之前进行预处理。 Linux限制了单个套接字可用于在内核中存储数据包的内存量,避免了数据包在内核内存中流动的情况。

此功能被称为套接字的记忆计算。在UDP的特殊情况下,它直接从TCP实现克隆,包括用于保持数据一致的锁定系统。这种额外的同步在套接字中引入了一个新的自旋锁,这引发了UDP延迟,特别是在使用多播协议时。社区观察到了这种行为,他们为内核开发了补丁以取消激活UDP套接字的内存记帐[5]。

4.UDP在LINUX中的性能

作为第一步,我们测量UDP套接字针对并发读取访问的实际性能。为了实现这个目标,我们必须隔离我们想要测试的代码部分。

Linux提供了回送虚拟接口,这是一种在同一台机器上使用网络堆栈通信两个应用程序的简单方法,并具有避免HardIRQ引入任何开销的优势。最后一个原因与仅分析网络堆栈性能的目标相结合,导致我们选择回送接口来测量网络堆栈代码所花费的时间和性能。另外,作为参考点,我们将UDP套接字性能与由mkfifo工具提供的内核FIFO队列进行比较。

为了在并行场景中强调结构并确定其行为,并发读取将在应用程序线程之间不同步的情况下执行,因此时间测量将表示线程数量超过可用处理器数量的情况下每个源的行为的系统。

更详细地说,我们测量了读取每个10个字节500,000个信息单元所需的时间,模拟从客户端到服务器的小消息的不断传入; DNS服务器的常见场景。服务器端的读取线程数量将会每次翻倍,直到超过可用处理器的数量,模拟情景从非常少量的读取线程变化到读取线程压倒处理器的情况。这两个应用程序将运行在同一台计算机上,使用不同的机制将信息从客户端传输到服务器:通过回送接口连接的UDP套接字以及在文件系统中创建的FIFO队列。对于这些测试,我们使用了Dell Optiplex 990,Intel Core i5-2400处理器(4个逻辑核心)和8 GB DDR3 RAM以及不同的Linux内核版本,这些版本包含在一些Ubuntu发行版中。

图2显示了每个测试中FIFO队列和UDP套接字的平均测量时间(以秒为单位)。通过观察我们的结果,我们可以看到,在两个结构上添加线程并行读取都会导致所有内核版本的执行时间增加。换句话说,将线程添加到从单个FIFO队列或UDP套接字同时读取的应用程序不会带来任何性能增益;此外,两个数据源的行为类似于单个处理器的情况,并且读取一次处理一个。需要注意的是,较新版本的绝对时间比旧版本更糟糕,但对并发访问显示更多线程容错性能:在添加并发访问时,新版内核版本的执行时间不会像旧版本那样快。我们可以注意到,在UDP套接字的特殊情况下,属于3.X系列的所有内核的时间值均高于2.6系列,这可能是由于2.6.25版中引入的内存记帐功能。

A.系统调用开销

正如我们之前所说的,这项工作旨在自行确定网络堆栈的性能,因此在此分析中应考虑由系统调用(和libc)引入的任何开销。然而,Linux系统调用的设计表明,它们是在它们发布的相同处理器中处理和执行的,因此不会干扰并行性能。这一事实导致我们将这种开销视为每个处理器中的一个恒定值,因为这项工作的范围是分析网络堆栈的可扩展性,而不是详细描述Linux Network API。

5.KERNEL隔离

从上一节我们可以推断出Linux内核的瓶颈,因此我们详细分析了内核如何接收数据包以检查我们的假设是否正确。

A.数据包接收分析

正如我们在第三节所述,数据包的接收过程由三个主要子过程组成:(1)升级HardIRQ并调度SoftIRQ的执行,(2)执行SoftIRQ和过程数据包通过网络堆栈,(3)将数据包排入套接字队列。第一个和第二个是高度并行化的,是第一个由NIC在一个处理器中异步触发的[1],[3],第二个由于使用软件中断而完全并行化,允许网络堆栈中的代码重入[1] ,[3],[11],[12]。是第三个(排队过程),序列化可以被定位,并且它将被更深入地分析,因为它基本上是修改内核中的共享数据结构。当一个数据包完成其通过网络堆栈的旅程时,必须按照下一个逻辑(从图3中表示)从左到右排列在接收队列或积压队列中:

(1)通过锁定结构的全局螺旋锁(红色实例)来锁定整个套接字。这不允许其他SoftIRQ同时入列其他数据包。

(2)检查某个用户空间应用程序是否正在使用该锁。如果是这样的话,这个数据包会直接排入队列中(确保它与它自己的自旋锁一致),并跳到下一步。数据包通过netfilter进行处理,(2)更新内存计费统计信息,(3)数据包在接收队列中排队(由自己的自旋锁保护,图3中的绿色实例),以及(4)系统调度器为调用以唤醒等待套接字中的数据的任何睡眠任务。

(3)最后,释放全局套接字自旋锁,允许其他数据包按照相同的逻辑排入队列。

另一方面,当应用程序尝试从套接字读取数据时,它会执行一个类似的过程,如下所述,并从右向左表示在图3中:

(1)使用相应的自旋锁(绿色)从接收队列中取出一个或多个数据包。

(2)将信息复制到用户空间内存。

(3)释放数据包使用的内存。这可能会改变套接字的状态,因此可能会发生两种锁定套接字的方法:快速和慢速。在这两种情况下,数据包都与套接字断开连接,“记忆计数”统计信息将更新,并根据所采用的锁定路径释放套接字。

由于排队数据包的过程可能会改变套接字的状态,所以内核必须通过引入前面所述的两种套接字锁定方法来避免与其他线程的竞争状态。快速方式发生在没有其他线程正在改变套接字状态时,并且在于保持套接字全局自旋锁,其停用下半部分控制器并且避免任何其他并发地将数据包插入到队列中。在数据包使用的存储器是释放之后,自旋锁释放以允许其他提取。相反,当其他线程处理套接字状态时会发生缓慢的方式,因此,出队数据包将与处理套接字状态的其他线程冲突,特别是在更新内存记帐统计信息时。在最后一种情况下,调用线程将重新调度其执行情况,直到状态锁被释放。

B.套接字自旋锁统计

前面的分析表明,两点可能会串行化对套接字的并发访问:套接字的全局螺旋锁或用于保持套接字接收队列一致的自旋锁。 通过对第IV节中使用的UDP套接字运行相同的压力测试来测试这两个实例,并使用lockdep Linux内核工具收集关于它们的统计信息。 收集的数据包括有关争用,获取,总等待时间和总锁定时间的信息。

为了测试每个

全文共5912字,剩余内容已隐藏,支付完成后下载完整资料


资料编号:[14521],资料为PDF文档或Word文档,PDF文档可免费转换为Word

原文和译文剩余内容已隐藏,您需要先支付 30元 才能查看原文和译文全部内容!立即支付

以上是毕业论文外文翻译,课题毕业论文、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。