跳转到主要内容

Python的sendfile(2)接口

项目描述

Download this month Latest version License Travis

关于

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的位置。headerstrailers是在从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.53.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最初由Ben Woolley编写,包括Linux、FreeBSD和DragonFly BSD支持。后来,Niklas Edmundsson接管了维护工作,并添加了AIX支持。在项目停滞了几年之后,Giampaolo Rodola’接管了维护工作,并从头开始重写了它,添加了对

  • Python 3

  • 非阻塞套接字

  • 大文件支持

  • Mac OSX

  • Sun OS

  • FreeBSD标志参数

  • 多线程(释放GIL)

  • 一个简单的基准测试套件

  • 单元测试

  • 文档

项目详情


下载文件

下载您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分布

pysendfile-2.0.1.tar.gz (19.3 kB 查看散列

上传时间

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面