O_EXCL的真正含义是“error if create and file exists”但是名字来源于“EXCLusive”,有点意思虽然具有误导性,并导致许多人误解该标志。
使用O_EXCL 打开文件将不会像某些人错误地认为的那样授予您对它的独占访问权限。一个进程用O_EXCL打开的文件,可以同时被其他进程打开读写,没问题,所以访问显然不是独占的。
此标志存在的主要原因是“锁定文件”。早在文件咨询锁定(使用fcntl)和可以在进程之间共享的信号量/互斥体之前很久,就需要一种简单的方法来确保跨多个进程对某些系统资源进行原子访问。实现锁定文件的方法。第一个想要访问资源的进程将在/var/lock 中创建一个锁定文件,以声明对该资源的所有权,并在完成后再次将其删除。其他进程将监视该目录,从而知道资源是否可用。
问题:如果两个进程查看目录并且都看到某个文件不存在,因此资源可用,现在都尝试创建这个文件,如何确保只有一个会成功?这就是O_EXCL 发挥作用的地方。如果他们都尝试创建设置了O_EXCL 的文件,则此操作只会对其中一个成功,而这就是现在拥有资源锁的进程。
所以O_EXCL 不是为了获得对文件的独占访问权限,而是为了制作独占访问文件,其目的是规范对某种资源的独占访问。
O_EXCL 今天的第二个最重要的用途是文件访问安全。考虑这种情况:一个根进程想要创建一个文件,并用只有根用户才能看到的内容填充它,而不是普通用户,但是它正在一个普通用户确实有写权限的目录中创建它(例如/tmp)。如果进程会按如下方式创建文件
open("/tmp/root-only", O_CREAT | O_WRONLY, 0600);
并且文件不存在,它是由root创建,拥有,只有root有读写权限,所以普通用户看不到它的内容。任务完成。
但是如果一个普通用户之前已经创建了/tmp/root-only呢?然后这个文件归那个用户所有,这个用户可以读取它,上面的 open 调用只会打开现有文件。即使根进程在打开文件后直接更改了所有权和文件权限,这对之前已经打开过文件的进程(例如tail -f)没有影响。
所以实现那个case的正确方法其实是:
unlink("/tmp/root-only"); // best effort, may not even exist
open("/tmp/root-only", O_CREAT | O_EXCL | O_WRONLY, 0600);
如果open 确实成功了,进程可以依赖该文件归进程所有者所有,并且没有其他人有权读取/写入它。