Go中的独占锁

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

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

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

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


还尝试了:

  1. 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.

  1. 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:

  1. 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系统上是这样的)。这就是为什么当程序再次尝试打开该文件时,它“无法工作”的原因:

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. )
  7. func main() {
  8. f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, os.ModeExclusive)
  9. if err != nil {
  10. log.Fatal(err)
  11. }
  12. defer f.Close()
  13. fmt.Fprintf(f, "this is my secret\n")
  14. }

在上面的示例中,当程序第一次运行时,它成功地创建了一个名为.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:

  1. $ cat prog.go
  2. package main
  3. import (
  4. "fmt"
  5. "log"
  6. "os"
  7. )
  8. func main() {
  9. f, err := os.OpenFile(".secrets", os.O_RDWR|os.O_CREATE, os.ModeExclusive)
  10. if err != nil {
  11. log.Fatal(err)
  12. }
  13. defer f.Close()
  14. fmt.Fprintf(f, "this is my secret\n")
  15. }
  16. $ go build prog.go
  17. $ ./prog
  18. $ ls -al
  19. total 2.1M
  20. drwxr-xr-x 2 marco marco 4.0K Aug 28 13:53 ./
  21. drwxr-xr-x 4 marco marco 4.0K Aug 28 13:22 ../
  22. -rwxr-xr-x 1 marco marco 2.1M Aug 28 13:53 prog*
  23. -rw-r--r-- 1 marco marco 230 Aug 28 13:49 prog.go
  24. ---------- 1 marco marco 17 Aug 28 13:53 .secrets <-- HERE IT IS
  25. $ ./prog
  26. 2021/08/28 13:54:27 open .secrets: permission denied
  27. $ cat .secrets
  28. cat: .secrets: Permission denied
  29. $ chmod u+rw .secrets
  30. $ cat .secrets
  31. this is my secret
  32. $

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

答案2

得分: 0

以下是翻译好的内容:

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

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

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "sync"
  6. )
  7. type MyFile struct {
  8. l *sync.RWMutex
  9. file *os.File
  10. }
  11. func (f *MyFile) Write(b []byte) (int, error) {
  12. f.l.Lock()
  13. defer f.l.Unlock()
  14. return f.file.Write(b)
  15. }
  16. func (f *MyFile) Read(b []byte) (int, error) {
  17. f.l.RLock()
  18. defer f.l.RUnlock()
  19. return f.file.Read(b)
  20. }
  21. func main() {
  22. f, err := os.Open("tmp")
  23. if err != nil {
  24. // 在这里处理错误
  25. }
  26. defer f.Close()
  27. myfile := &MyFile{
  28. l: &sync.RWMutex{},
  29. file: f,
  30. }
  31. myfile.Write([]byte("secret"))
  32. b1 := make([]byte, 5)
  33. myfile.Read(b1)
  34. fmt.Println(string(b1))
  35. }
英文:

> 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

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "sync"
  6. )
  7. type MyFile struct {
  8. l *sync.RWMutex
  9. file *os.File
  10. }
  11. func (f *MyFile) Write(b []byte) (int, error) {
  12. f.l.Lock()
  13. defer f.l.Unlock()
  14. return f.file.Write(b)
  15. }
  16. func (f *MyFile) Read(b []byte) (int, error) {
  17. f.l.RLock()
  18. defer f.l.RUnlock()
  19. return f.file.Read(b)
  20. }
  21. func main() {
  22. f, err := os.Open("tmp")
  23. if err != nil {
  24. // handle here
  25. }
  26. defer f.Close()
  27. myfile := &MyFile{
  28. l: &sync.RWMutex{},
  29. file: f,
  30. }
  31. myfile.Write([]byte("secret"))
  32. b1 := make([]byte, 5)
  33. myfile.Read(b1)
  34. fmt.Println(string(b1))
  35. }

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:

确定