使用sendfile让数据传输得到最优化11页文档资料Word文档下载推荐.docx
- 文档编号:18782544
- 上传时间:2023-01-01
- 格式:DOCX
- 页数:9
- 大小:27.13KB
使用sendfile让数据传输得到最优化11页文档资料Word文档下载推荐.docx
《使用sendfile让数据传输得到最优化11页文档资料Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《使用sendfile让数据传输得到最优化11页文档资料Word文档下载推荐.docx(9页珍藏版)》请在冰豆网上搜索。
在现实中,对于这个有关数据传输的问题是没有一个统一的答案的。
绝大多数的人在评价数据传输的快慢时,还是会使用"
每秒钟传输的兆字节数"
来作为一种衡量的标准。
但是,数据传输快慢的真正衡量标准却是CPU对于每一个传输的兆字节所花费占用的时间。
对于实时应用软件,尤其是那些传输视频或者音频数据流的软件,最不想出现的一种状况就是所谓的"
延迟"
。
CPU执行任务的时候如果没有任何的效率,那么,要实现对协议层(protocol-level)的负载平衡(loadbalancing)以及将主机的IP名私人化的支持(一种叫做Virtuozzo的操作系统虚拟化技术)都是不太可能的。
一个加装了Virtuozzo的主机内能够装得下数以千计的网络站点,所以,尽可能的减少用来处理数据传输占据的CPU时间是非常重要的。
Sendfile()是一种崭新的操作系统核心,这种新核心能够帮助人们解决上边所描述的那些问题。
而且,这种新内核对于UNIX,Linux,Solaris8操作系统来说都是适用的。
从技术角度来看,sendfile()是磁盘和传输控制协议(TCP)之间的一种系统呼叫,但是sendfile()还能够用来在两个文件夹之间移动数据。
在各种不同的操作系统上实现sendfile()都会有所不同,当然这种不同只是极为细微的差别。
通常来说,我们会假定所使用的操作系统是Linux核心2.4版本。
系统呼叫的原型有如下几种:
ssize_tsendfile(intout_fd,intin_fd,off_t*offset,size_tcount)in_fd是一种用来读文件的文件描述符。
out_fd是一种用来写文件的描述符。
Offset是一种指向被输入文件变量位置的指针,sendfile()将会从它所指向的位置开始数据的读取。
Count表示的是两个文件描述符之间数据拷贝的字节数。
sendfile()的威力在于,它为大家提供了一种访问当前不断膨胀的Linux网络堆栈的机制。
这种机制叫做"
零拷贝(zero-copy)"
这种机制可以把"
传输控制协议(TCP)"
框架直接的从主机存储器中传送到网卡的缓存块(networkcardbuffers)中去。
为了更好的理解"
以及sendfile(),让我们回忆一下以前我们在传送文件时所需要执行的那些步骤。
首先,一块在用户机器存储器内用于数据缓冲的位置先被确定了下来。
然后,我们必须使用read()这条系统呼叫来把数据从文件中拷贝到前边已经准备好的那个缓冲区中去。
(在通常的情况下,这个操做会把数据从磁盘上拷贝到操作系统的高速缓冲存储器中去,然后才会把数据从高速缓冲存储器中拷贝至用户空间中去,这种过程就是所谓的"
上下文切换"
)在完成了上述的那些步骤之后,我们得使用write()系统呼叫来将缓冲区中的内容发送到网络上去,程序段如下所示:
intout_fd,intin_fd;
charbuffer[BUFLEN];
/*unsubstantialcodeskippedforclarity*/
read(in_fd,buffer,BUFLEN);
/*syscall,makecontextswitch*/
write(out_fd,buffer,BUFLEN);
操作系统核心不得不把所有的数据至少都拷贝两次:
先是从核心空间到用户空间的拷贝,然后还得再从用户空间拷贝回核心空间。
每一次操做都需要上下文切换(context-switch)的这个步骤,其中包含了许多复杂的高度占用CPU的操作。
系统自带的工具vmstat能够用来在绝大多数UNIX以及与其类似的操作系统上显示当前的"
上下文切换(context-switch)"
速率。
请看叫做"
CS"
的那一栏,有相当一部分的上下文切换是发生在取样期间的。
用不同类型的方式进行装载可以让使用者清楚的看到使用这些参数进行装载时的不同效果。
关于切换过程的一些具体细节
让我们向着有关上下文切换过程的更深层次挖掘,这样做能够让我们更好的理解有关切换的一些问题。
这里有许多种有关从用户空间呼叫系统的操作的方法。
举个例子来说,将虚拟内存页面从用户空间中切换到核心,然后再切换回去的这种操作是必不可少的。
这种操作过程需要的系统花销是相当大的(尤其是在CPU周期的占用方面)。
这种操做是通过使用叫做全局描述符台面(GlobalDescriptorTable)以及局部描述符台面(LocalDescriptorTable)的存储器控制台来实现的。
另外的一种结构被称之为TSS(TaskStatusSegment任务状态段)的工具也需要大家给予足够的重视。
此外,还有一些隐藏的非常"
昂贵"
的操作没有被上下文切换程序呼叫出来。
我们能够通过虚拟内存需要虚拟物理地址翻译操作的支持才能实现的例子来说明这些隐藏操作的存在。
这种翻译所需要的数据也是存储于存储器中的,所以,CPU每一次对这些数据存储位置的请求都需要对主存储器进行一次或多次的访问(这是为了读取翻译台入口),这是除了需要获取的那些数据外还要进行的一些操作。
现在的CPU通常都包含了一个翻译缓存,其缩写为TLB。
TLB是作为页面入口来工作的,其中存储了最近访问过的对象。
(这是对TLB最为简单的解释,其具体的解释应为:
OLE库文件,其中存放了OLE自动化对象的数据类型、模块和接口定义,自动化服务器通过TLB文件就能了解自动化对象的使用方法。
)TLB高速缓冲存储器拥有巨大的潜在花费,其中包括了几个存储器的访问操作以及页面错误处理器的实行操作。
在拷贝大量数据的时候,会对TLB高速缓冲存储器产生大量的消耗。
在这个时候,TLB高速缓冲存储器里边会被要拷贝的页面数据占据的容不下任何别的其它数据。
在有了sendfile()零拷贝(zero-copy)之后,如果可能的话,通过使用直接存储器访问(DirectMemoryAccess)的硬件设备,数据从磁盘读取到操作系统高速缓冲存储器中会变得非常之迅速。
而TLB高速缓冲存储器则被完整无缺的放在那里,没有充斥任何有关数据传输的文件。
应用软件在使用sendfile()primitive的时候会有很高的性能表现,这是因为系统呼叫没有直接的指向存储器,因此,就提高了传输数据的性能。
通常来说,要被传输的数据都是从系统缓冲存储器中直接读取的,其间并没有进行上下文切换的操作,也没有垃圾数据占据高速缓冲存储器。
因此,在服务器应用程序中使用sendfile()能够显著的减少对CPU的占用。
在我们的这个例子中取代带有mmap()的read()不会让事情有什么显著的变化。
然而,mmap系统呼叫的请求是从文件中(或者从其它的一些对象中)生成一些连接信息,这些都是从在虚拟内存中的文件描述符中指定的。
试图从虚拟内存中读取数据会产生一些磁盘操作。
因为系统会把含有映射内容的存储器直接的写入而不会调用read()以及缓存定位,所以我们能够消除磁盘读取操作。
然而,这种操作会导致TLB高速缓冲存储器的过热,所以CPU在装载每个字节的传输时的负荷会稍微加大一些。
零拷贝的方法以及应用软件的开发
只要有可能,在任何时候,对性能要求非常苛刻的客户服务器应用程序的开发都会使用到零拷贝方法(Thezero-copyapproach)。
设想一下,当我们需要在一个单独的服务器上运行超过一千个分散的拥有私人IP地址的Apache网络服务器时,就可以使用到Virtuozzo技术了。
为了达到这个目的,我们每一秒钟都不得不在传输控制协议的层面上处理数以千计的请求来列出客户的请求并且把主机名页开列出来。
如果没有经过最优化的处理和零拷贝(zero-copy)支持的话,这对于处理器来说将是一项繁重而且复杂的任务,甚至都超过了网络对它的负荷。
零拷贝sendfile()的实现是基于9K-http-requests-per-second速率的,甚至在速度相对较慢的PentiumII350MHz的处理器上也是一样。
然而,零拷贝(zero-copy)sendfile()并不是能解决所有问题的"
万能药"
部分的,减少网络操作的数量,sendfile()系统呼叫应该和TCP/IP中的TCP_CORK选项在一起共同的使用。
在我们以后的文章中将会讨论应用软件从此选项中获得的好处,以及讨论其它的一些相关问题。
下面再附上两篇文章供参考。
促进高效数据传输的TCP/IP选项
作者:
ZDNETCHINA特稿
2019-07-2204:
11PM
在前一篇文章里,我们讨论了以下问题:
如何采用sendfile()系统函数降低从磁盘到网络的数据传输负载。
接下来我们继续讨论涉及网络连接控制的另一问题,同时希望通过对这一问题的讨论能有助于在实际环境下把sendfile()的功能最大化,这就是如何设置TCP/IP选项来控制套接字的行为。
TCP/IP数据传输
TCP/IP网络的数据传输通常建立在数据块的基础之上。
从程序员的观点来看,发送数据意味着发出(或者提交)一系列"
发送数据块"
的请求。
在系统级,发送单个数据块可以通过调用系统函数write()或者sendfile()来完成。
在网络级可以看到更多的数据块,通常把它们叫做帧,帧再被包装上一定字节长度的报头然后通过线路在网络上传输。
帧及其报头内部的信息是由若干协议层定义的,从OSI参考模型的物理层到应用层都可能会牵扯到。
因为在网络连接中是由程序员来选择最适当的应用协议,所以网络包的长度和顺序都在程序员的控制之下。
同样的,程序员还必须选择这个协议在软件中得以实现的方式。
TCP/IP协议自身已经有了多种可互操作的实现,所以在双方通信时,每一方都有它自身的低级行为,这也是程序员所应该知道的情况。
通常情况下,程序员不必关心操作系统和网络协议栈发送和接收网络数据的方法。
系统内置算法定义了低级的数据组织和传输方式;
然而,影响这些算法的行为以及对网络连接施加更大强度控制能力的方法也是有的。
例如,如果某个应用协议使用了超时和重发机制,程序员就可以采取一定措施设定或者获取超时参数。
他或她还可能需要增加发送和接收缓冲区的大小来保证网络上的信息流动不会中断。
改变TCP/IP协议栈行为的一般的方法是采用所谓的TCP/IP选项。
下面就让我们来看一看你该如何使用这些选项来优化数据传输。
TCP/IP选项
有好几种选项都能改变TCP/IP协议栈的行为。
使用这些选择能对在同一计算机上运行的其他应用程序产生不利的影响,因此普通用户通常是不能使用这些选项的(除了root用户以外)。
我们在这里主要讨论能改变单个连接操作(用TCP/IP的术语来说就是套接字)的选项。
ioctl风格的getsockopt()和setsockopt()系统函数都提供了控制套接字行为的方式。
比方说,为了在Linux上设置TCP_NODELAY选项,你可以如下编写代码:
intfd,on=1;
/*此处是创建套接字等操作,出于篇幅的考虑省略*/
setsockopt(fd,SOL_TCP,TCP_NODELAY,&
on,sizeof(on));
尽管有许多TCP选项可供程序员操作,而我们却最关注如何处置其中的两个选项,它们是TCP_NODELAY和TCP_CORK,这两个选项都对网络连接的行为具有重要的作用。
许多UNIX系统都实现了TCP_NODELAY选项,但是,TCP_CORK则是Linux系统所独有的而且相对较新;
它首先在内核版本2.4上得以实现。
此外,其他UNIX系统版本也有功能类似的选项,值得注意的是,在某种由BSD派生的系统上的TCP_NOPUSH选项其实就是TCP_CORK的一部分具体实现。
TCP_NODELAY和TCP_CORK基本上控制了包的"
Nagle化"
,Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。
JohnNagle是Nagle算法的发明人,后者就是用他的名字来命名的,他在1984年首次用这种方法来尝试解决福特汽车公司的网络拥塞问题(欲了解详情请参看IETFRFC896)。
他解决的问题就是所谓的sillywindowsyndrome,中文称"
愚蠢窗口症候群"
,具体含义是,因为普遍终端应用程序每产生一次击键操作就会发送一个包,而典型情况下一个包会拥有一个字节的数据载荷以及40个字节长的包头,于是产生4000%的过载,很轻易地就能令网络发生拥塞,。
Nagle化后来成了一种标准并且立即在因特网上得以实现。
它现在已经成为缺省配置了,但在我们看来,有些场合下把这一选项关掉也是合乎需要的。
现在让我们假设某个应用程序发出了一个请求,希望发送小块数据。
我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。
如果我们马上发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。
例如,当我们正在发送一个较短的请求并且等候较大的响应时,相关过载与传输的数据总量相比就会比较低,而且,如果请求立即发出那么响应时间也会快一些。
以上操作可以通过设置套接字的TCP_NODELAY选项来完成,这样就禁用了Nagle算法。
另外一种情况则需要我们等到数据量达到最大时才通过网络一次发送全部数据,这种数据传输方式有益于大量数据的通信性能,典型的应用就是文件服务器。
应用Nagle算法在这种情况下就会产生问题。
但是,如果你正在发送大量数据,你可以设置TCP_CORK选项禁用Nagle化,其方式正好同TCP_NODELAY相反(TCP_CORK和TCP_NODELAY是互相排斥的)。
下面就让我们仔细分析下其工作原理。
假设应用程序使用sendfile()函数来转移大量数据。
应用协议通常要求发送某些信息来预先解释数据,这些信息其实就是报头内容。
典型情况下报头很小,而且套接字上设置了TCP_NODELAY。
有报头的包将被立即传输,在某些情况下(取决于内部的包计数器),因为这个包成功地被对方收到后需要请求对方确认。
这样,大量数据的传输就会被推迟而且产生了不必要的网络流量交换。
但是,如果我们在套接字上设置了TCP_CORK(可以比喻为在管道上插入"
塞子"
)选项,具有报头的包就会填补大量的数据,所有的数据都根据大小自动地通过包传输出去。
当数据传输完成时,最好取消TCP_CORK选项设置给连接"
拔去塞子"
以便任一部分的帧都能发送出去。
这同"
塞住"
网络连接同等重要。
总而言之,如果你肯定能一起发送多个数据集合(例如HTTP响应的头和正文),那么我们建议你设置TCP_CORK选项,这样在这些数据之间不存在延迟。
能极大地有益于WWW、FTP以及文件服务器的性能,同时也简化了你的工作。
示例代码如下:
setsockopt(fd,SOL_TCP,TCP_CORK,&
/*cork*/
write(fd,…);
fprintf(fd,…);
sendfile(fd,…);
on=0;
/*拔去塞子*/
不幸的是,许多常用的程序并没有考虑到以上问题。
例如,EricAllman编写的sendmail就没有对其套接字设置任何选项。
ApacheHTTPD是因特网上最流行的Web服务器,它的所有套接字就都设置了TCP_NODELAY选项,而且其性能也深受大多数用户的满意。
这是为什么呢?
答案就在于实现的差别之上。
由BSD衍生的TCP/IP协议栈(值得注意的是FreeBSD)在这种状况下的操作就不同。
当在TCP_NODELAY模式下提交大量小数据块传输时,大量信息将按照一次write()函数调用发送一块数据的方式发送出去。
然而,因为负责请求交付确认的记数器是面向字节而非面向包(在Linux上)的,所以引入延迟的概率就降低了很多。
结果仅仅和全部数据的大小有关系。
而Linux在第一包到达之后就要求确认,FreeBSD则在进行如此操作之前会等待好几百个包。
在Linux系统上,TCP_NODELAY的效果同习惯于BSDTCP/IP协议栈的开发者所期望的效果有很大不同,而且在Linux上的Apache性能表现也会更差些。
其他在Linux上频繁采用TCP_NODELAY的应用程序也有同样的问题。
相得益彰
你的数据传输并不需要总是准确地遵守某一选项或者其它选择。
在那种情况下,你可能想要采取更为灵活的措施来控制网络连接:
在发送一系列当作单一消息的数据之前设置TCP_CORK,而且在发送应立即发出的短消息之前设置TCP_NODELAY。
把零拷贝和sendfile()系统函数结合起来(前文有述)可以显著地提升系统整体效率并且降低CPU负载。
我们采用这一技术为Swsoft'
sVirtuozzo公司开发了基于名称的主机托管子系统,实践经验表明,该技术可以在装备350-MHzPentiumIICPU的PC上实现每秒9000个HTTP请求,这一成绩在以前几乎是不可能实现的。
性能上的提高显而易见。
利用TCP/IP选项优化数据传输(第2部分)
BUILDER2019-07-2204:
15PM
上回,我们对TCP_CORK选项如何减少网络传输包的数量做了一番原理性的解释。
减少网络流量当然是非常重要的优化举措之一,不过这种手段也仅仅是实现高性能网络数据传输领域的一个方面。
其他TCP选项也可能显著提供传输性能同时在某些条件下减少服务器的响应时间延迟。
下面就让我们来了解一些此类选项。
TCP_DEFER_ACCEPT
我们首先考虑的第1个选项是TCP_DEFER_ACCEPT(这是Linux系统上的叫法,其他一些操作系统上也有同样的选项但使用不同的名字)。
为了理解TCP_DEFER_ACCEPT选项的具体思想,我们有必要大致阐述一下典型的HTTP客户/服务器交互过程。
请回想下TCP是如何与传输数据的目标建立连接的。
在网络上,在分离的单元之间传输的信息称为IP包(或IP数据报)。
一个包总有一个携带服务信息的包头,包头用于内部协议的处理,并且它也可以携带数据负载。
服务信息的典型例子就是一套所谓的标志,它把包标记代表TCP/IP协议栈内的特殊含义,例如收到包的成功确认等等。
通常,在经过"
标记"
的包里携带负载是完全可能的,但有时,内部逻辑迫使TCP/IP协议栈发出只有包头的IP包。
这些包经常会引发讨厌的网络延迟而且还增加了系统的负载,结果导致网络性能在整体上降低。
现在服务器创建了一个套接字同时等待连接。
TCP/IP式的连接过程就是所谓"
3次握手"
首先,客户程序发送一个设置SYN标志而且不带数据负载的TCP包(一个SYN包)。
服务器则以发出带SYN/ACK标志的数据包(一个SYN/ACK包)作为刚才收到包的确认响应。
客户随后发送一个ACK包确认收到了第2个包从而结束连接过程。
在收到客户发来的这个SYN/ACK包之后,服务器会唤醒一个接收进程等待数据到达。
当3次握手完成后,客户程序即开始把"
有用的"
的数据发送给服务器。
通常,一个HTTP请求的量是很小的而且完全可以装到一个包里。
但是,在以上的情况下,至少有4个包将用来进行双向传输,这样就增加了可观的延迟时间。
此外,你还得注意到,在"
数据被发送之前,接收方已经开始在等待信息了。
为了减轻这些问题所带来的影响,Linux(以及其他的一些操作系统)在其TCP实现中包括了TCP_DEFER_ACCEPT选项。
它们设置在侦听套接字的服务器方,该选项命令内核不等待最后的ACK包而且在第1个真正有数据的包到达才初始化侦听进程。
在发送SYN/ACK包之后,服务器就会等待客户程序发送含数据的IP包。
现在,只需要在网络上传送3个包了,而且还显著降低了连接建立的延迟,对HTTP通信而言尤其如此。
这一选项在好些操作系统上都有相应的对等物。
例如,在FreeBSD上,同样的行为可以用以下代码实现:
/*为明晰起见,此处略去无关代码*/
structaccept_filter_argaf={"
dataready"
"
"
};
setsockopt(s,SOL_SOCKET,SO_ACCEPTFILTER,&
af,sizeof(af));
这个特征在FreeBSD上叫做"
接受过滤器"
,而且具有多种用法。
不过,在几乎所有的情况下其效果与TCP_DEFER_ACCEPT是一样的:
服务器不等待最后的ACK包而仅仅等待携带数据负载的包。
要了解该选项及其对高性能Web服务器的重要意义的更多信息请参考Apache文档上的有关内容。
就HTTP客户/服务器交互而言,有可能需要改变客户程序的行为。
客户程序为什么要发送这种"
无用的"
ACK包呢?
这是因为,TCP协议栈无法知道ACK包的状态。
如果采用FTP而非HTTP,那么客户程序直到接收了FTP服务器提示的数据包之后才发送数据。
在这种情况下,延迟的ACK将导致客户/服务器交互出现延迟。
为了确定ACK是否必要,客户程序必须知道应用程序协议及其当前状态。
这样,修改客户行为就成为必要了。
对Linux客户程序来说,我们还可以采用另一个选项,它也被叫做TCP_DEFER_ACCEPT。
我们知道,套接字分成两种类型,侦听套接字和连接套接字,所以它们也各自具有相应的TCP选项集合。
因此,经常同时采用的这两类选项却具有同样的名字也是完全可能的。
在连接套接
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用sendfile 让数据传输得到最优化11页文档资料 使用 sendfile 数据传输 得到 优化 11 文档 资料