go 語言中判斷一個文件是否被 flock 上了

【導讀】不同進程之間會有一些場景利用到文件加鎖,本文對這種操作進行了詳細介紹。

首先需要知道的是 linux 存在強制鎖(mandatory lock)和勸告鎖(advisory lock)。勸告鎖是一種協同工作的鎖。對於這種鎖來說,內核只提供加鎖以及檢測文件是否已經加鎖的手段,但是內核並不參與鎖的控制和協調,所以只是個約定,對文件打個標籤的感覺,具體遵守不遵守看自己;強制鎖是一種內核強制採用的文件鎖,每當有系統調用 open()、read() 以及 write() 發生的時候,內核都要檢查並確保這些系統調用不會違反在所訪問文件上加的強制鎖約束。也就是說,如果有進程不遵守遊戲規則,硬要往加了鎖的文件中寫入內容,內核就會加以阻攔。更多參考 https://www.ibm.com/developerworks/cn/linux/l-cn-filelock/

我這裏利用文件鎖來做等待的事情,即前面有一個進程跑着呢,後面這個幹同樣事情的進程就得等着,並且前一個進程無論是正常退出還是異常退出的時候鎖必須釋放掉

看了下,用 flockLOCK_EX 排它鎖或者用 fnctlF_SETLKW 也可以,其實都是相當於對文件加個標記(注意這句話)

flock 可以直接用系統命令測試 一個終端開 touch lockfile && date && flock lockfile sleep 5另外一個終端開 flock lockfile date,另外終端這個 flock 會卡五秒鐘後再輸出結果

fnctl 可以用以下代碼測試

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/file.h>
#include <fcntl.h>
#include <sys/stat.h>

int main() {
    int fd = open("lockfile" ,O_RDWR);
    struct flock lock;
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;
    lock.l_pid = getpid();
    int rd = fcntl(fd,F_SETLKW,&lock);
    printf("%d\n",rd);
    sleep(10);
    printf("%d\n",rd);
        return 0;
}

或者是 go 代碼 可以從 https://golang.org/pkg/syscall/ 這裏看到些提示

setlkw.goimport (
    "fmt"
    "syscall"
    "time"

)

func main() {
    fd,err:= syscall.Open("lockfile",syscall.O_WRONLY,0644)
    fmt.Println(err)
    fstat := syscall.Flock_t{
        Type:  syscall.F_WRLCK,
        Whence:0,
    }
    fgetlk := syscall.Flock_t{}
    //if err := syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
    //    if err == syscall.EWOULDBLOCK {
    //        fmt.Println("blocked")
    //    }
    //    fmt.Println(err)
    //}
    fmt.Println("test")
    err = syscall.FcntlFlock(uintptr(fd),syscall.F_SETLKW,&fstat)
    fmt.Printf("%#v",fstat)
    fmt.Println(err,"\n")
    time.Sleep(10 * time.Second)
    fmt.Printf("%#v",fgetlk)
    err = syscall.FcntlFlock(uintptr(fd),syscall.F_GETLK,&fgetlk)
    fmt.Printf("%#v",fgetlk)
    fmt.Println(err,"\n")

}

另外一個文件來 check 鎖,注意得是 2 個進程,一個進程內會認爲無鎖

getlk.goimport (
    "fmt"
    "syscall"
)

func main() {
    fd,err:= syscall.Open("lockfile",syscall.O_WRONLY,0644)
    fmt.Println(err)
    fgetlk := syscall.Flock_t{}
    err = syscall.FcntlFlock(uintptr(fd),syscall.F_GETLK,&fgetlk)
    //這裏應該是        F_WRLCK                          = 0x1
    fmt.Printf("%#v",fgetlk)
    fmt.Println(err,"\n")
}

另外一個有意思的是如果用 flock 加鎖一個文件,但是用 fcntl getlk 來查看這個鎖,會得到

syscall.Flock_t{Type:2, Whence:0, Pad_cgo_0:[4]uint8{0x0, 0x0, 0x0, 0x0}, Start:0, Len:0, Pid:0, Pad_cgo_1:[4]uint8{0x0, 0x0, 0x0, 0x0}}

一開始以爲是 F_UNLCK = 0x2 沒有鎖上後來才發現其實是 LOCK_EX = 0x2,所以其實都是個標記。

轉自:

blog.spider.im/post/go-flock-fnctl-getlock/

Go 開發大全

參與維護一個非常全面的 Go 開源技術資源庫。日常分享 Go, 雲原生、k8s、Docker 和微服務方面的技術文章和行業動態。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/zY0cGWiJl6HdbwNUAi1Vyg