【问题标题】:Unable to read from temporary file无法从临时文件中读取
【发布时间】:2020-07-25 09:24:36
【问题描述】:

我正在尝试调用一个外部进程,该进程写入一个我使用withSystemTempFile 获得的临时文件。进程退出后,我可以cat 该文件的内容,但应用程序本身在尝试使用readFile 读取该文件时失败并出现openFile: resource busy (file is locked) 错误。我在回答this 问题时遵循了建议,并使用了readFile 的惰性版本。这是一个示例代码:

module Main where

import Prelude hiding (readFile)
import System.Process (createProcess, shell)
import System.IO (Handle, hShow)
import System.IO.Temp (withSystemTempFile)
import System.IO.Strict (readFile)

main :: IO ()
main = withSystemTempFile "temp.txt" doSomeStuff
    where
        doSomeStuff :: FilePath -> Handle -> IO ()
        doSomeStuff tempFilePath tempFilePathHandle = do
            putStrLn tempFilePath
            createProcess $ shell $ "echo \"test\" >> " ++ tempFilePath
            getLine -- It's here just to stop program while I check that I can actually "cat" the temp file
            -- here I'm able to cat the temp file
            contents <- readFile tempFilePath -- here the command fails with /tmp/temp23900-0.txt: openFile: resource busy (file is locked)
            putStrLn contents

我仍然对 Haskell 有所了解,所以我希望这不是显而易见的事情,因为我已经没有什么想法了。什么给了?

【问题讨论】:

    标签: haskell tmp


    【解决方案1】:

    这个错误是因为withSystemTempFile在进入时锁定了文件。作为回报,它会为您提供其句柄(在您的代码中称为 tempFilePathHandle),因此您可以使用句柄读取文件,如下所示:

    contents <- hGetContents tempFilePathHandle
    

    编辑

    这是因为 GHC 在内部实现了一个readers-writer 锁来跟踪它打开了哪些文件,以及需要哪些权限。读写器锁只允许一个写入器(独占访问)或多个读取器(共享访问)。

    在这种情况下,withSystemTempFile 在临时文件上获得了写入者锁,因此readFile 无法获得它需要的读取器锁(因为,写入器锁再次阻止 GHC 获得任何读取器锁)文件)。

    这是实现锁的 C 代码的link。正如@luqui 在下面的评论中所建议的那样,它可能不是一个最佳解决方案,特别是因为 GHC 不请求任何操作系统级别的锁,所以像 cat 这样的进程仍然可以访问和修改文件。在惰性环境中可能更有意义,在这种情况下读取文件写入会产生难以预测的结果。

    【讨论】:

    • @luqui 感谢您的编辑,但我想知道为什么操作系统会允许其他进程(例如cat)访问该文件,而不是 Haskell。
    • @luqui 在 GHC 中查看the code responsible for obtaining the lock,我可以看到它是使用读写锁实现的,但看起来该锁仅在内部使用,看起来不像 Haskell从操作系统请求锁定,至少不是从我看到的源代码和lslocks 的输出中看到的。
    • 对不起,这是一个“直观”的更正,因为我很难相信 GHC 会记录它已经打开的文件(尤其是因为您可能会打开两个“不同”的文件它们是彼此的硬链接等)。但我感谢您深入挖掘胆量并遵从您更有证据的推理。
    【解决方案2】:

    withSystemTempFile 会打开它为您制作的临时文件,因此您无需再次打开它。 readFile 获取文件名而不是其句柄,因此您知道它一定是试图自己打开它。 readFile 的等价物但对于您已经打开的文件是 hGetContents,因此要解决此问题,请将 readFile tempFilePath 替换为 hGetContents tempFilePathHandle(并相应地更新您的 import)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-20
      • 1970-01-01
      • 2015-10-21
      • 1970-01-01
      • 2020-03-06
      • 1970-01-01
      相关资源
      最近更新 更多