使用GNU / Linux系统调用`splice`在Haskell中进行零拷贝Socket到Socket数据传输
|
更新:Nemo先生的回答有助于解决问题!下面的代码包含修复程序!请参阅下面的nb False和nb True调用. 还有一个名为 我有以下(Haskell)代码: #ifdef LINUX_SPLICE
#include <fcntl.h>
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
#endif
module Network.Socket.Splice (
Length,zeroCopy,splice
#ifdef LINUX_SPLICE,c_splice
#endif
) where
import Data.Word
import Foreign.Ptr
import Network.Socket
import Control.Monad
import Control.Exception
import System.Posix.Types
import System.Posix.IO
#ifdef LINUX_SPLICE
import Data.Int
import Data.Bits
import Unsafe.Coerce
import Foreign.C.Types
import Foreign.C.Error
import System.Posix.Internals
#else
import System.IO
import Foreign.Marshal.Alloc
#endif
zeroCopy :: Bool
zeroCopy =
#ifdef LINUX_SPLICE
True
#else
False
#endif
type Length =
#ifdef LINUX_SPLICE
(#type size_t)
#else
Int
#endif
-- | The 'splice' function pipes data from
-- one socket to another in a loop.
-- On Linux this happens in kernel space with
-- zero copying between kernel and user spaces.
-- On other operating systems,a portable
-- implementation utilizes a user space buffer
-- allocated with 'mallocBytes'; 'hGetBufSome'
-- and 'hPut' are then used to avoid repeated
-- tiny allocations as would happen with 'recv'
-- 'sendAll' calls from the 'bytestring' package.
splice :: Length -> Socket -> Socket -> IO ()
splice l (MkSocket x _ _ _ _) (MkSocket y _ _ _ _) = do
let e = error "splice ended"
#ifdef LINUX_SPLICE
(r,w) <- createPipe
print ('+',r,w)
let s = Fd x -- source
let t = Fd y -- target
let c = throwErrnoIfMinus1 "Network.Socket.Splice.splice"
let u = unsafeCoerce :: (#type ssize_t) -> (#type size_t)
let fs = sPLICE_F_MOVE .|. sPLICE_F_MORE
let nb v = do setNonBlockingFD x v
setNonBlockingFD y v
nb False
finally
(forever $do
b <- c $c_splice s nullPtr w nullPtr l fs
if b > 0
then c_splice r nullPtr t nullPtr (u b) fs)
else e
(do closeFd r
closeFd w
nb True
print ('-',w))
#else
-- ..
#endif
#ifdef LINUX_SPLICE
-- SPLICE
-- fcntl.h
-- ssize_t splice(
-- int fd_in,-- loff_t* off_in,-- int fd_out,-- loff_t* off_out,-- size_t len,-- unsigned int flags
-- );
foreign import ccall "splice"
c_splice
:: Fd
-> Ptr (#type loff_t)
-> Fd
-> Ptr (#type loff_t)
-> (#type size_t)
-> Word
-> IO (#type ssize_t)
sPLICE_F_MOVE :: Word
sPLICE_F_MOVE = (#const "SPLICE_F_MOVE")
sPLICE_F_MORE :: Word
sPLICE_F_MORE = (#const "SPLICE_F_MORE")
#endif
注意:上面的代码现在正常工作!由于Nemo,以下不再有效! 我用上面定义的splice调用了两个打开并连接的套接字(它们已经用于使用套接字API发送和接收调用传输最少量的握手数据或转换为句柄并与hGetLine和hPut一起使用)并且我一直得到: Network.Socket.Splice.splice: resource exhausted (Resource temporarily unavailable) 在第一个c_splice调用站点:c_splice返回-1并将一些errno设置为一个读取资源耗尽的值(可能是EAGAIN)查找时资源暂时不可用. 我测试了不同长度值的调用splice:1024,8192. 解决方法我不知道Haskell,但“资源暂时不可用”是EAGAIN.它默认看起来像Haskell sets its sockets to non-blocking mode.因此,如果您在没有数据时尝试从一个读取数据,或者在其缓冲区已满时尝试写入一个数据,那么您将失败并使用EAGAIN. 弄清楚如何将套接字更改为阻止模式,我打赌你会解决你的问题. [更新] 或者,在尝试读取或写入套接字之前调用select或poll.但是你仍然需要处理EAGAIN,因为有一些罕见的极端情况,其中Linux选择将指示套接字已准备就绪,而实际上并非如此. (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
