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 打开您的文件,您以只读方式打开它以锁定它,但这是行不通的 - 您将获得一个只读文件描述符。
我目前正在编写一个 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
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 打开您的文件,您以只读方式打开它以锁定它,但这是行不通的 - 您将获得一个只读文件描述符。