Golang坏的文件描述符

52

在我的 Go 协程中尝试向日志文件追加内容时,出现了“bad file descriptor”的错误。

write ./log.log: bad file descriptor

该文件已存在并且权限为 666。一开始我以为可能是因为它们每个都在同时尝试打开文件。我实现了互斥锁来尝试避免这种情况,但仍然遇到了同样的问题,所以我将其删除。

logCh := make(chan string, 150)
go func() {
    for {
        msg, ok := <-logCh
        if ok {
            if f, err := os.OpenFile("./log.log", os.O_APPEND, os.ModeAppend); err != nil {
                panic(err)
            } else {
                logTime := time.Now().Format(time.RFC3339)
                if _, err := f.WriteString(logTime + " - " + msg); err != nil {
                    fmt.Print(err)
                }
                f.Close()
            }
        } else {
            fmt.Print("Channel closed! \n")
            break
        }
    }
}()

我确认我可以在我的电脑上重现。 - HectorJ
3个回答

112

你需要添加 O_WRONLY 标志:

if f, err := os.OpenFile("./log.log", os.O_APPEND|os.O_WRONLY, os.ModeAppend); err != nil { /*[...]*/ }
为了解释,这里是Linux关于open的文档:http://man7.org/linux/man-pages/man2/openat.2.html
引用:

标志参数必须包含以下访问模式之一:O_RDONLY、O_WRONLY或O_RDWR。它们分别请求以只读、只写或读/写方式打开文件。

如果你查看/usr/local/go/src/syscall/zerrors_linux_amd64.go:660,你会发现:
O_RDONLY                         = 0x0
O_RDWR                           = 0x2
O_WRONLY                         = 0x1

因此,默认情况下,您会获得一个只读文件描述符。


2
这解决了我的问题,但在我的情况下,我在包装函数中的os.OpenFile方法中有os.O_WRONLY,我从其他地方调用它并尝试从返回的文件中读取,但当然它被设置为只写,所以我得到了相同的错误。通过改为os.O_RDWR来修复。 - Lansana Camara
近7年过去了,这个答案仍然很有用! - dragonfly02

3
这似乎是Windows和Linux之间的差异。 在Windows操作系统上,O_APPEND意味着写访问权限,正如syscall_windows.go中的代码所示。
if mode&O_APPEND != 0 {
    access &^= GENERIC_WRITE
    access |= FILE_APPEND_DATA
}

在Linux中,openflags会原样传递给Linux系统调用。
因此,在DOS / WINDOWS中,您不需要显式添加一个写入标志和一个追加标志,因为它是隐含的。(自从DOS时代以来,这一直是默认行为)但是在Linux中,需要添加额外的标志才能正常工作。
(...但我认为它不应该需要这个标志,因为追加隐含地意味着我想要写入。我应该向Golang报告这个问题吗?)

1
欢迎来到StackOverflow!那么你的意思是说,在Windows中只需使用os.O_APPEND就足够了,但在Linux中需要显式添加os.O_WRONLY,对吗?请扩展您的答案,以帮助未来的访问者立即看到解决方案并呈现示例代码(例如,这样的代码片段在Windows上工作但在Linux上不起作用,而这样的代码片段会有相同的行为)。 - Maxim Sagaydachny
1
@MaximSagaydachny 是的,在Windows中只需要使用追加标志(在帖子中已更新),谢谢 :) - Trenjeska Schutte

2
"最初的回答":这个对我很有用。
之前的代码:
os.OpenFile(fileName, os.O_CREATE|os.O_APPEND, os.ModePerm)

发生了错误:坏文件描述符,

然后我在函数中添加了os.O_WRONLY

更改后的代码:

os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)

最初的回答,它没有出现问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接