[英]How to create a file only if it doesn't exist?
我寫了一個 UNIX 守護進程(針對 Debian,但它應該無關緊要),我想提供一些創建“.pid”文件(包含守護進程的進程標識符的文件)的方法。
我搜索了一種僅在文件不存在時打開文件的方法,但找不到。
基本上,我可以這樣做:
if (fileexists())
{
//fail...
}
else
{
//create it with fopen() or similar
}
但就目前而言,這段代碼並沒有以原子方式執行任務,這樣做會很危險,因為另一個進程可能會在我的測試和文件創建過程中創建文件。
你們對如何做到這一點有任何想法嗎?
謝謝你。
PS:僅涉及std::streams
的解決方案的加分點。
人2打開:
O_EXCL 確保此調用創建文件:如果此標志與 O_CREAT 一起指定,並且路徑名已存在,則 open() 將失敗。 如果未指定 O_CREAT,則 O_EXCL 的行為未定義。
所以,你可以調用fd = open(name, O_CREAT | O_EXCL, 0644);
/* Open() 是原子的。 (因為某種原因) */
更新:當然,您應該將 O_RDONLY、O_WRONLY 或 O_RDWR 標志之一 OR 到 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, e.g. 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);
}
}
}
我能想到的唯一方法是使用系統級鎖。 看這個: 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 的人),您應該沒問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.