如果你想要背景,请看这里.简而言之,问题是:" 从bracket (mallocBytes n) free和之间的实际差异是什么".allocaBytesForeign.Marshall.Alloc
通常在C中,alloca在堆栈malloc上分配并在堆上分配.我不确定在Haskell中它发生了什么,但我不希望上述方程式与速度之外的区别.如果你点击后台链接,你知道编译代码bracket (mallocBytes n) free导致"双重免费或腐败",同时allocaBytes工作正常(在GHCi中根本无法看到问题,在这两种情况下一切正常).
到现在为止,我已经花了两天时间进行痛苦的调试,而且我非常有信心bracket (mallocBytes n) free在某种程度上不稳定,其余的代码都是可靠的.我想知道这是什么交易bracket (mallocBytes n) free.
Zet*_*eta 12
bracket (mallocBytes size) free将用C的malloc和free,而allocaBytes size将使用由真实GHCs垃圾回收托管内存.这本身就是一个巨大的差异已经,因为Ptr的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
Run Code Online (Sandbox Code Playgroud)
结果:
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>
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,尽管我们已经使用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
Run Code Online (Sandbox Code Playgroud)
那是什么意思?好吧,allocaBytes不太可能在你的脸上爆炸,但是如果你的C代码变体会导致内存损坏,你不会注意到这个代价.更糟糕的是,只要你在返回的边界之外写字allocaBytes,你可能会破坏其他Haskell的价值.
但是,我们在这里谈论未定义的行为.上面的代码可能会或可能不会在您的系统上崩溃.它也可能在allocaBytes部件中崩溃.
如果我是你,我会跟踪malloc并free打电话.
*我曾经在程序中间出现"双重使用免费"错误.调试了所有内容,重写了大部分"坏"例程.不幸的是,错误在调试版本中消失了,但在发布版本中再次出现.原来,在前十行中main,我不小心写信给b[i - 1]了i = 0.
我能够复制问题,我可以确认存在大量的缓冲区溢出.如果您使用以下分配器(请原谅快速和脏代码),它会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"
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
295 次 |
| 最近记录: |