golang: fcntl文件锁
进程级强制锁,ubuntu的apt和dpkg进程使用此锁
1 锁结构
type Flock_t struct {
Type int16 //锁的类型: 读锁、写锁、解锁
Whence int16 //锁的范围 SEEK_SET, SEEK_CUR, SEEK_END 文件开头,当前位置,结尾。基准位置
Pad_cgo_0 [4]byte
Start int64 //相对于基准的偏移值。定义了锁的起始
Len int64 //锁字节即从偏移开始的连续字节数
Pid int32 //获取哪个进程处于阻塞(F_GETLK)
Pad_cgo_1 [4]byte
}
对于一个文件,flock加锁的范围是整个文件内容,而fcntl能对文件的任意部分加锁。
2 获取锁状态
package main
import (
"fmt"
"syscall"
)
func main() {
fd, err := syscall.Open("/var/lib/dpkg/lock-frontend", syscall.O_WRONLY, 0644)
if err != nil {
fmt.Println(err)
}
defer syscall.Close(fd)
// 声明一个锁结构体用于写入锁状态
fgetlk := syscall.Flock_t{}
/*
FcntlFlock(fd uintptr, cmd int, lk *Flock_t)
cmd: 使用F_GETLK获取锁状态并写入lk中
*/
err = syscall.FcntlFlock(uintptr(fd), syscall.F_GETLK, &fgetlk)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v\n", fgetlk)
/*
F_RDLCK = 0x0 读锁
F_WRLCK = 0x1 写锁
F_UNLCK = 0x2 解锁
*/
switch fgetlk.Type {
case syscall.F_WRLCK:
fmt.Println("write lock")
case syscall.F_RDLCK:
fmt.Println("read lock")
case syscall.F_UNLCK:
fmt.Println("unlock")
default:
}
}
3 加写锁
package main
import (
"fmt"
"io"
"os"
"syscall"
"time"
)
func main() {
fp := "/var/lib/dpkg/lock-frontend"
file, err := os.OpenFile(fp, os.O_WRONLY, os.ModePerm)
if err != nil {
fmt.Println(err)
return
}
fd := file.Fd()
flockt := syscall.Flock_t{
Type: syscall.F_WRLCK,
Whence: io.SeekStart,
}
/*
F_GETLK: 获取当前锁的状态
F_SETLK: 给当前文件上锁(非阻塞)
F_SETLKW: 给当前文件上阻塞锁(类似自旋锁)
读/写、写/写互斥, 读/读不互斥
*/
err = syscall.FcntlFlock(fd, syscall.F_SETLK, &flockt)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("加锁成功")
time.Sleep(time.Second * 15)
flockt.Type = syscall.F_UNLCK
syscall.FcntlFlock(fd, syscall.F_SETLK, &flockt)
fmt.Println("解锁成功")
// 这是其他进程即可获取锁
time.Sleep(time.Second * 15)
}
读锁
打开文件方法使用读方法打开os.Open() 或者os.OpenFile(fp, os.O_RDONLY, perm)
锁类型使用读锁, syscall.Flock_t