go 中的 Fcntl 不起作用

Fcntl in go doesn't work

我目前正在编写一个 http 服务器,它必须 运行 覆盖文件夹中的所有文件。一个请求可以写入一个文件,而另一个请求可以同时读取文件,所以我需要锁定文件,该文件已打开以供写入。在例程中,将读取文件,我只想跳过被锁定的文件。 为此,我想使用 rubyist or nightlyone.

中的锁库

问题是,我没有让它们工作,所以我开始自己调用 syscall.FcntlFlock() 函数,但它没有像我预期的那样工作。
该程序在 Go Playground 中不起作用,因为 playground 似乎 运行 不在基于 unix 的系统上(syscall.FcntlFlock 未定义)

无效的代码:

func main() {
    time.Sleep(time.Second)

    file, err := os.Open("lockfiletest.lock")
    if err != nil {
        log.Printf("error opening file2: %s", err)
        return
    }
    defer file.Close()

    flockT := syscall.Flock_t{
        Type: syscall.F_WRLCK,
        Whence: io.SeekStart,
        Start: 0,
        Len: 0,
    }
    err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT)
    if err != nil {
        log.Printf("error locking file2: %s", err)
        return
    }

    log.Println("lock2 accessed")

    time.Sleep(time.Second * 5)

    log.Println("func2 finished")

    time.Sleep(time.Second * 15)
}

控制台输出:

2017/10/28 00:18:12 error locking file2: bad file descriptor
Process finished with exit code 0

我做错了什么?系统调用是否损坏?
我也在 C 中尝试过,它工作正常。
我在 ubuntu16.04

上在 Go1.8.3 和 Go1.9.1 中测试了这个程序

PS:该程序还必须在 windows 上 运行,因此仅实现 FcntlLock 是不够的。


我还想过使用 sync.RWMutex,所以它是通过 Mutex 而不是 fileLock 锁定的。这不是我想要的,因为我只想跳过被锁定的文件,而不是等到互斥量被锁定。

如果我使用 touch lockfiletest.lock 创建文件,即没有文件内容,您的程序将失败并显示错误:error locking file2: bad file descriptor

$ rm -f lockfiletest.lock
$ touch lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:17:27 error locking file2: bad file descriptor

我更改了打开的文件以紧密匹配 TestFcntlFlock

$ uname -s
Linux
~/go/src/syscall$ $ go test -v -run=TestFcntlFlock syscall_unix_test.go
=== RUN   TestFcntlFlock
--- PASS: TestFcntlFlock (0.01s)
PASS
ok      syscall 0.008s
~/go/src/syscall$ 

例如,

package main

import (
    "io"
    "log"
    "os"
    "syscall"
    "time"
)

func main() {
    time.Sleep(time.Second)

    name := "lockfiletest.lock"
    file, err := os.OpenFile(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666)
    if err != nil {
        log.Printf("error opening file: %s", err)
        return
    }
    defer file.Close()

    flockT := syscall.Flock_t{
        Type:   syscall.F_WRLCK,
        Whence: io.SeekStart,
        Start:  0,
        Len:    0,
    }
    err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT)
    if err != nil {
        log.Printf("error locking file: %s", err)
        return
    }

    log.Println("lock2 accessed")

    time.Sleep(time.Second * 5)

    log.Println("func2 finished")

    time.Sleep(time.Second * 15)
}

输出:

$ rm -f lockfiletest.lock
$ touch lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:21:56 lock2 accessed
2017/10/27 21:22:01 func2 finished
$ rm -f lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:22:25 lock2 accessed
2017/10/27 21:22:30 func2 finished
$ go run lockfiletest.go
2017/10/27 21:25:40 lock2 accessed
2017/10/27 21:25:45 func2 finished
$ 

您必须以 WRONLY 或 RDWR 打开您的文件,您以只读方式打开它以锁定它,但这是行不通的 - 您将获得一个只读文件描述符。