如何创build一个文件,只有当它不存在?

我编写了一个UNIX守护进程(针对Debian,但不应该),我想提供一些方法来创build一个“pid文件”(一个包含守护进程进程标识符的文件)。

我search了一种只有在文件存在的情况下才打开文件的方法,但找不到它。

基本上,我可以做这样的事情:

if (fileexists()) { //fail... } else { //create it with fopen() or similar } 

但就目前而言,这段代码并不是以primefaces的方式执行任务,而且这样做会很危险,因为另一个进程可能会在testing过程中创build文件,并创build文件。

你们有什么想法如何做到这一点?

谢谢。

PS:一个只涉及std::streams的解决scheme的奖励点。

男子2打开:

O_EXCL确保该调用创建文件:如果此标志与O_CREAT一起指定,且路径名已存在,则open()将失败。 如果没有指定O_CREAT,则O_EXCL的行为是不确定的。

所以,你可以调用fd = open(name, O_CREAT | O_EXCL, 0644); / *打开()是原子。 (因为某种原因) */

更新:你当然还是应该将O_RDONLY,O_WRONLY或O_RDWR标志之一放入flags参数中。

我在这里了解了适当的守护进程(当天回来):

这是一个很好的阅读。 自那之后,我改进了锁定代码,以消除平台上的争用条件,允许在指定的特定区域进行咨询文件锁定

以下是我参与的一个项目的相关片段:

 static int zfsfuse_do_locking(int in_child) { /* Ignores errors since the directory might already exist */ mkdir(LOCKDIR, 0700); if (!in_child) { ASSERT(lock_fd == -1); /* * before the fork, we create the file, truncating it, and locking the * first byte */ lock_fd = creat(LOCKFILE, S_IRUSR | S_IWUSR); if(lock_fd == -1) return -1; /* * only if we /could/ lock all of the file, * we shall lock just the first byte; this way * we can let the daemon child process lock the * remainder of the file after forking */ if (0==lockf(lock_fd, F_TEST, 0)) return lockf(lock_fd, F_TLOCK, 1); else return -1; } else { ASSERT(lock_fd != -1); /* * after the fork, we instead try to lock only the region /after/ the * first byte; the file /must/ already exist. Only in this way can we * prevent races with locking before or after the daemonization */ lock_fd = open(LOCKFILE, O_WRONLY); if(lock_fd == -1) return -1; ASSERT(-1 == lockf(lock_fd, F_TEST, 0)); /* assert that parent still has the lock on the first byte */ if (-1 == lseek(lock_fd, 1, SEEK_SET)) { perror("lseek"); return -1; } return lockf(lock_fd, F_TLOCK, 0); } } void do_daemon(const char *pidfile) { chdir("/"); if (pidfile) { struct stat dummy; if (0 == stat(pidfile, &dummy)) { cmn_err(CE_WARN, "%s already exists; aborting.", pidfile); exit(1); } } /* * info gleaned from the web, notably * http://www.enderunix.org/docs/eng/daemon.php * * and * * http://sourceware.org/git/?p=glibc.git;a=blob;f=misc/daemon.c;h=7597ce9996d5fde1c4ba622e7881cf6e821a12b4;hb=HEAD */ { int forkres, devnull; if(getppid()==1) return; /* already a daemon */ forkres=fork(); if (forkres<0) { /* fork error */ cmn_err(CE_WARN, "Cannot fork (%s)", strerror(errno)); exit(1); } if (forkres>0) { int i; /* parent */ for (i=getdtablesize();i>=0;--i) if ((lock_fd!=i) && (ioctl_fd!=i)) /* except for the lockfile and the comm socket */ close(i); /* close all descriptors */ /* allow for airtight lockfile semantics... */ struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 200000; /* 0.2 seconds */ select(0, NULL, NULL, NULL, &tv); VERIFY(0 == close(lock_fd)); lock_fd == -1; exit(0); } /* child (daemon) continues */ setsid(); /* obtain a new process group */ VERIFY(0 == chdir("/")); /* change working directory */ umask(027); /* set newly created file permissions */ devnull=open("/dev/null",O_RDWR); /* handle standard I/O */ ASSERT(-1 != devnull); dup2(devnull, 0); /* stdin */ dup2(devnull, 1); /* stdout */ dup2(devnull, 2); /* stderr */ if (devnull>2) close(devnull); /* * contrary to recommendation, do _not_ ignore SIGCHLD: * it will break exec-ing subprocesses, eg for kstat mount and * (presumably) nfs sharing! * * this will lead to really bad performance too */ signal(SIGTSTP,SIG_IGN); /* ignore tty signals */ signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); } if (0 != zfsfuse_do_locking(1)) { cmn_err(CE_WARN, "Unexpected locking conflict (%s: %s)", strerror(errno), LOCKFILE); exit(1); } if (pidfile) { FILE *f = fopen(pidfile, "w"); if (!f) { cmn_err(CE_WARN, "Error opening %s.", pidfile); exit(1); } if (fprintf(f, "%d\n", getpid()) < 0) { unlink(pidfile); exit(1); } if (fclose(f) != 0) { unlink(pidfile); exit(1); } } } 

另见http://gitweb.zfs-fuse.net/?p=sehe;a=blob;f=src/zfs-fuse/util.c;h=7c9816cc895db4f65b94592eebf96d05cd2c369a;hb=refs/heads/maint

我能想到的唯一方法是使用系统级锁。 看到这个: C ++如何检查文件是否在使用中 – 多线程多进程系统

解决这个问题的一种方法是打开文件进行追加。 如果函数成功,位置为0,那么你可以相当肯定这是一个新的文件。 可能仍然是一个空文件,但这种情况可能不重要。

 FILE* pFile = fopen(theFilePath, "a+"); if (pFile && gfetpos(pFile) == 0) { // Either file didn't previously exist or it did and was empty } else if (pFile) { fclose(pFile); } 

看来,没有办法严格使用流。

你可以使用open(如上面的wildplasser所提到的),如果成功的话,继续打开同一个文件作为流。 当然,如果你正在写的文件是一个PID,不清楚为什么你不用C风格的write()来编写它。

O_EXCL仅排除尝试使用O_EXCL打开同一文件的其他进程。 这当然意味着你永远不会有完美的保证,但是如果文件名/位置是别人可能打开的地方(除了你知道使用O_EXCL的人以外),你应该没问题。