Go中的独占锁

huangapple go评论79阅读模式
英文:

Exclusive locking in Go

问题

如何在Golang中打开一个文件,以便只有一个进程可以写入它?

我是Golang的新手,想通过编写一些东西来学习它。我试图创建一个秘密存储工具,将API密钥存储到文件中;在此过程中,我遇到了一个问题 - 如果两个相同程序的实例同时运行(假设),可能会发生竞争条件并损坏文件。

因此,我正在寻找一种锁定文件的方法,以便一次只有一个进程可以写入它。同时读取是可以的。


在stackoverflow上找到了这个答案:https://stackoverflow.com/questions/52986413/how-to-get-an-exclusive-lock-on-a-file-in-go,但在相同的评论中提到:

> your package uses flock() for locking on Unix so doesn't provide OS level mandatory locking


尝试了os.ModeExclusive

f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, os.ModeExclusive)

但这会阻止下次程序运行时访问文件。请解释一下这个模式的作用。从文档中无法理解。


还尝试了:

f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, 0744)

使用上述代码,任何人在打开文件时都可以读取/写入。

英文:

How can a file be opened such that only one process can write to it at a time in Golang?

I am new to Golang and was trying to learn it by writing something. Was attempting to create a secret storage tool which stores, say, api keys to a file; during which I came across an issue - if two instance of same program runs together (hypothetically) a race condition can occur and damage the file.

So, I was checking for a way to lock the file so that only one process can write to it at a time. Reading simultaneously is ok.


Found this answer : https://stackoverflow.com/questions/52986413/how-to-get-an-exclusive-lock-on-a-file-in-go on stackoverflow. but in the comments of the same, it's mentioned

> your package uses flock() for locking on Unix so doesn't provide OS level mandatory locking


Tried os.ModeExclusive.

f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, os.ModeExclusive)

But it prevents the file from accessing the next time program runs. Can you please explain what this mode does. Not able to understand it from the docs.


Also tried:

f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, 0744)

On using the above, anyone can read/write when the file is opened.


答案1

得分: 1

我将尝试解释你提出的关于os.ModeExclusive的问题:

os.ModeExclusive是一个文件打开模式,它会创建一个文件,即使文件所有者也没有读写权限(至少在Unix系统上是这样的)。这就是为什么当程序再次尝试打开该文件时,它“无法工作”的原因:

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, os.ModeExclusive)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	fmt.Fprintf(f, "this is my secret\n")
}

在上面的示例中,当程序第一次运行时,它成功地创建了一个名为.secrets的文件,并向其中写入了内容。但是,当程序再次运行时,它无法打开该文件,因为文件的权限被设置为只允许所有者访问。

你可以使用Chmod()函数来修改已经存在的文件的权限,包括使用os.ModeExclusive模式。

希望这能解答你的问题!

英文:

I'll try to explain the os.ModeExclusive bit of your question:

> Tried os.ModeExclusive.
>
> f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, os.ModeExclusive)
>
> But it prevents the file from accessing the next time program runs.
> Can you please explain what this mode does. Not able to understand it
> from the docs.

OpenFile() with os.ModeExclusive basically creates a file where not even the file owner has read and write permissions on it (at least this is the behavior I observed on unix). that is why it "doesn't work" the next time when it tries to open the file again:

$ cat prog.go
package main

import (
        "fmt"
        "log"
        "os"
)

func main() {
        f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, os.ModeExclusive)
        if err != nil {
                log.Fatal(err)
        }
        defer f.Close()

        fmt.Fprintf(f, "this is my secret\n")
}
$ go build prog.go
$ ./prog
$ ls -al
total 2.1M
drwxr-xr-x 2 marco marco 4.0K Aug 28 13:53 ./
drwxr-xr-x 4 marco marco 4.0K Aug 28 13:22 ../
-rwxr-xr-x 1 marco marco 2.1M Aug 28 13:53 prog*
-rw-r--r-- 1 marco marco  230 Aug 28 13:49 prog.go
---------- 1 marco marco   17 Aug 28 13:53 .secrets    <-- HERE IT IS
$ ./prog
2021/08/28 13:54:27 open .secrets: permission denied
$ cat .secrets
cat: .secrets: Permission denied
$ chmod u+rw .secrets
$ cat .secrets
this is my secret
$

os.ModeExclusive can also be used with Chmod() of course on already existing files.

答案2

得分: 0

以下是翻译好的内容:

惯例(至少对于标准库)如下:除非明确说明(或从上下文中明显),否则没有函数/方法可以安全地并发使用。
在没有外部同步的情况下,通过Write()并发写入os.File是不安全的。

你可以使用sync包中的RMutex来避免数据竞争。*sync.RWMutex

package main

import (
	"fmt"
	"os"
	"sync"
)

type MyFile struct {
	l    *sync.RWMutex
	file *os.File
}

func (f *MyFile) Write(b []byte) (int, error) {
	f.l.Lock()
	defer f.l.Unlock()

	return f.file.Write(b)
}

func (f *MyFile) Read(b []byte) (int, error) {
	f.l.RLock()
	defer f.l.RUnlock()

	return f.file.Read(b)
}

func main() {
	f, err := os.Open("tmp")
	if err != nil {
		// 在这里处理错误
	}
	defer f.Close()

	myfile := &MyFile{
		l:    &sync.RWMutex{},
		file: f,
	}

	myfile.Write([]byte("secret"))

	b1 := make([]byte, 5)
	myfile.Read(b1)

	fmt.Println(string(b1))
}
英文:

> The convention (at least for the standard library) is the following: No function/method is safe for concurrent use unless explicitly stated (or obvious from the context).
> It is not safe to write concurrently to an os.File via Write() without external synchronization.

source: https://stackoverflow.com/a/30746629/13067552

You can use RMutex from sync package to avoid data race. *sync.RWMutex

package main

import (
	"fmt"
	"os"
	"sync"
)

type MyFile struct {
	l    *sync.RWMutex
	file *os.File
}

func (f *MyFile) Write(b []byte) (int, error) {
	f.l.Lock()
	defer f.l.Unlock()

	return f.file.Write(b)
}

func (f *MyFile) Read(b []byte) (int, error) {
	f.l.RLock()
	defer f.l.RUnlock()

	return f.file.Read(b)
}

func main() {
	f, err := os.Open("tmp")
	if err != nil {
		// handle here
	}
	defer f.Close()

	myfile := &MyFile{
		l:    &sync.RWMutex{},
		file: f,
	}

	myfile.Write([]byte("secret"))

	b1 := make([]byte, 5)
	myfile.Read(b1)

	fmt.Println(string(b1))
}

huangapple
  • 本文由 发表于 2021年8月28日 16:15:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/68962540.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定