如果您想要背景,请参阅here。简而言之,问题是:“来自Foreign.Marshall.Alloc
的bracket (mallocBytes n) free
和allocaBytes
之间的实际差异是什么。”
通常在C中,alloca
在堆栈上分配,malloc
在堆上分配。我不确定在Haskell中它发生了什么,但我不希望上述方程式与速度之外的区别。如果您单击了后台链接,您知道编译代码bracket (mallocBytes n) free
导致“双重免费或损坏”,而allocaBytes
工作正常(在GHCi中根本无法看到问题,一切正常两种情况都很好。)
到现在为止,我花了两天时间进行痛苦的调试,我非常确信bracket (mallocBytes n) free
在某种程度上是不稳定的,其余的代码是可靠的。我想知道与bracket (mallocBytes n) free
有什么关系。
答案 0 :(得分:12)
bracket (mallocBytes size) free
将使用C malloc
和free
,而allocaBytes size
将使用由GHC垃圾回收管理的内存。这本身就是一个巨大的差异,因为Ptr
的{{1}}可能被未使用(但已分配)的内存所包围:
allocaBytes
结果:
import Control.Exception
import Control.Monad (forM_)
import Foreign.Marshal.Alloc
import Foreign.Ptr
import Foreign.Storable
-- Write a value at an invalid pointer location
hammer :: Ptr Int -> IO ()
hammer ptr = pokeElemOff ptr (-1) 0 >> putStrLn "hammered"
main :: IO ()
main = do
putStrLn "Hammer time! Alloca!"
forM_ [1..10] $ \n ->
print n >> allocaBytes 10 hammer
putStrLn "Hammer time! Bracket"
forM_ [1..10] $ \n ->
print n >> bracket (mallocBytes 10) free hammer
正如您所看到的,虽然我们已经使用Hammer time! Alloca!
1
hammered
2
hammered
3
hammered
4
hammered
5
hammered
6
hammered
7
hammered
8
hammered
9
hammered
10
hammered
Hammer time! Bracket
1
hammered
<program crashes>
,但arr[-1] = 0
很高兴地忽略了该错误。但是,如果您写到职位allocaBytes
,free
会(经常)在您的脸上爆炸。如果另一个分配的内存区域存在内存损坏,它也会在你的脸上爆炸*。
此外,对于-1
,指针很可能指向已经分配的内存,而不是指向一个内存的开头,例如。
allocaBytes
这是什么意思?好吧,nursery = malloc(NURSERY_SIZE);
// ...
pointer_for_user = nursery + 180;
// pointer_for_user[-1] = 0 is not as
// much as a problem, since it doesn't yield undefined behaviour
不太可能在你的脸上爆炸,但是你不会注意到你的C代码变体是否会导致内存损坏。更糟糕的是,只要你在allocaBytes
返回的边界之外写字,你可能会破坏其他 Haskell 值。
但是,我们在这里谈论未定义的行为。上面的代码可能会或可能不会在您的系统上崩溃。它也可能在allocaBytes
部分崩溃。
如果我是你,我会trace the malloc
and free
calls。
*我曾经在程序中间出现“双重使用免费”错误。调试了所有内容,重写了大部分“坏”例程。不幸的是,错误在调试版本中消失了,但在发布版本中再次出现。事实证明,在allocaBytes
的前十行中,我偶然使用main
写信给b[i - 1]
。
答案 1 :(得分:5)
我能够复制问题,我可以确认存在大量缓冲区溢出。如果您使用以下分配器(请原谅快速和脏代码),它会在缓冲区之后添加页面的0xa5
s并将其转储(如果已修改),您可以在几个测试中看到超过几百个字节:
withBuffer :: Int -> (Ptr a -> IO b) -> IO b
withBuffer n = bracket begin end
where begin = do
a <- mallocBytes (n + 4096)
mapM_ (\i -> pokeByteOff (a `plusPtr` n) i (0xa5 :: Word8)) [0..4095]
return a
end = \a -> do
page <- mapM (\i -> peekByteOff (a `plusPtr` n) i) [0..4095]
when (any (/= (0xa5 :: Word8)) page) $ do
putStrLn $ unlines $ map (hexline page) [0,16..4095]
error "corruption detected"
free a
hexline bytes off = unwords . map hex . take 16 . drop off $ bytes
hex = printf "%02x"