英文:
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))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论