Python的sendfile(2)接口
项目描述
快速链接
关于
sendfile(2) 是一种系统调用,它提供了一种从文件描述符到另一个(套接字)的“零拷贝”数据复制方式。 “零拷贝”一词指的是两个描述符之间的所有数据复制都完全由内核完成,不涉及用户空间缓冲区的数据复制。这在通过套接字(例如FTP)发送文件时特别有用。通过套接字发送文件的传统方法涉及从文件中读取数据到用户空间缓冲区,然后通过 send() 或 sendall() 将该缓冲区写入套接字。
# how a file is tipically sent
import socket
file = open("somefile", "rb")
sock = socket.socket()
sock.connect(("127.0.0.1", 8021))
while True:
chunk = file.read(65536)
if not chunk:
break # EOF
sock.sendall(chunk)
将数据复制两次(一次进入用户空间缓冲区,一次从用户空间缓冲区出来)会对性能和资源造成一些惩罚。sendfile(2) 系统调用通过避免使用用户空间缓冲区来避免这些惩罚;它还导致单个系统调用(因此只有一个上下文切换),而不是用于数据复制的内部使用的read(2) / write(2) 系统调用系列(每个系统调用都需要上下文切换)。
import socket
from sendfile import sendfile
file = open("somefile", "rb")
blocksize = os.path.getsize("somefile")
sock = socket.socket()
sock.connect(("127.0.0.1", 8021))
offset = 0
while True:
sent = sendfile(sock.fileno(), file.fileno(), offset, blocksize)
if sent == 0:
break # EOF
offset += sent
一个简单的基准测试
这个基准脚本实现了上述两个示例,并比较了plain socket.send()和sendfile()的性能,从CPU时间和每秒传输的字节数来看,sendfile()大约快了2.5倍。这些是我在我基于Linux 2.6.38的机器上得到的结果,AMD双核1.6 GHz。
send()
CPU时间 |
28.84微秒/次 |
传输速率 |
359.38 MB/s |
sendfile()
CPU时间 |
11.28微秒/次 |
传输速率 |
860.88 MB/s |
你什么时候想使用它?
基本上,任何通过网络发送文件的应用程序都可以利用sendfile(2)。HTTP和FTP服务器是一个典型的例子。《a href="http://www.proftpd.org/" rel="nofollow">proftpd 和 vsftpd 都已知道使用它,同样pyftpdlib 也一样。
API文档
sendfile模块提供了一个函数:sendfile()。
sendfile.sendfile(out, in, offset, nbytes, header="", trailer="", flags=0)
从文件描述符in(一个普通文件)复制nbytes字节到文件描述符out(一个套接字),从offset开始。返回刚刚发送的字节数。当文件末尾到达时返回0。在Linux上,如果offset被指定为None,则从in的当前位置读取字节,并更新in的位置。headers和trailers是在从in写入数据之前和之后写入的字符串。在跨平台应用程序中,不建议使用它们(可以使用send()或sendall()代替)。在Solaris上,_out_可以是普通文件的文件描述符或套接字的文件描述符。在其他所有平台上,out必须是打开的套接字的文件描述符。flags参数仅在FreeBSD上受支持。
sendfile.SF_NODISKIO
sendfile.SF_MNOWAIT
sendfile.SF_SYNC
如果实现支持,则用于_flags_参数的参数。它们在FreeBSD平台上可用。请参阅FreeBSD的man sendfile(2)。
与send()的区别
sendfile(2)只与普通(mmap-like)文件一起工作(例如,您不能与StringIO对象一起使用)。
此外,必须清楚,文件只能“原样”发送(例如,在传输过程中不能修改内容)。与非常规文件系统(如NFS、SMBFS/Samba和CIFS)可能存在问题。有关此问题,请参阅proftpd文档。
OSError代替socket.error引发。伴随的错误代码具有相同的意义:EAGAIN、EWOULDBLOCK、EBUSY表示你应该重试,ECONNRESET、ENOTCONN、ESHUTDOWN、ECONNABORTED在断开连接的情况下。一些示例:基准脚本、测试套件、pyftpdlib包装器。
支持的平台
此模块与从Python 2.5到3.4的版本兼容。支持的平台包括
Linux
Mac OSX
FreeBSD
Dragon Fly BSD
Sun OS
AIX(未经充分测试)
支持
请随时通过电子邮件发送至 g.rodola [AT] gmail [DOT] com 或在邮件列表上发帖: http://groups.google.com/group/py-sendfile。
状态
截至目前,代码包含一个可靠的测试套件,并已准备好投入生产使用。它已被包含在pyftpdlib项目中,并在生产环境中使用近一年,至今未报告任何问题。
项目详情
pysendfile-2.0.1.tar.gz的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 510a414b270986fba3c79cb76d90a4c910c701bfb43ff983a5d4e92846050e17 |
|
MD5 | e7b301eddd703ab74a48c59a8fda1f97 |
|
BLAKE2b-256 | cd3f4aa268afd0252f06b3b487c296a066a01ddd4222a46b7a3748599c8fc8c3 |