2012-03-26 7 views
6

मैंने यूनिक्स डिमन लिखा (डेबियन को लक्षित करना, लेकिन इससे कोई फर्क नहीं पड़ता) और मैं "पिड फ़ाइल" बनाने का कोई तरीका प्रदान करना चाहता था (एक फ़ाइल जिसमें डिमन की प्रक्रिया पहचानकर्ता शामिल है)।केवल फ़ाइल बनाने के लिए कैसे करें यदि यह अस्तित्व में नहीं है?

मैं एक फ़ाइल केवल अगर यह मौजूद नहीं है खोलने का एक तरीका के लिए खोज की है, लेकिन एक नहीं मिल सका।

असल में, मैं की तरह कुछ कर सकता है:

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

लेकिन के रूप में यह खड़ा है, इस कोड को एक परमाणु फैशन में कार्य नहीं करता है, और ऐसा करने, खतरनाक हो क्योंकि किसी अन्य प्रक्रिया फ़ाइल बना सकते हैं मेरे परीक्षण के दौरान, और फ़ाइल निर्माण।

क्या आपको लोगों को यह करने का कोई विचार है कि यह कैसे करें?

धन्यवाद।

पीएस: एक समाधान के लिए बोनस बिंदु जिसमें केवल std::streams शामिल है।

+1

संभावित डुप्लिकेट [यह जांचने से पहले कि फ़ाइल बनाने से पहले मौजूद है या नहीं) [http://stackoverflow.com/questions/7863145/how-to-test-if-a-file-exists-before-creating-it) –

+0

शायद आप जो चाहते हैं उसे प्राप्त कर सकते हैं? – Kevin

उत्तर

7

आदमी 2 खुला:

O_EXCL है कि यह सुनिश्चित इस कॉल फ़ाइल बनाता है

यहाँ एक परियोजना से एक प्रासंगिक टुकड़ा है कि मैं में शामिल किया गया था : यदि यह ध्वज O_CREAT के संयोजन के साथ निर्दिष्ट है, और पथनाम पहले से मौजूद है, तो खोलें() विफल हो जाएगा। O_CREAT निर्दिष्ट नहीं है तो O_EXCL का व्यवहार अपरिभाषित है।

तो, आप fd = open(name, O_CREAT | O_EXCL, 0644);/* ओपन() परमाणु पर कॉल कर सकते हैं। (एक कारण के लिए) */

अद्यतन: और आपको निश्चित रूप से या O_RDONLY, O_WRONLY, या O_RDWR ध्वज तर्क में झंडे में से एक होना चाहिए।

+0

धन्यवाद! वास्तव में मुझे क्या चाहिए। – ereOn

4

मैं (वापस दिन में) यहाँ उचित daemonizing के बारे में सीखा:

यह एक अच्छा पढ़ा है। मैंने प्लेटफार्मों पर दौड़ की स्थिति को खत्म करने के लिए लॉकिंग कोड में सुधार किया है जो निर्दिष्ट क्षेत्रों के साथ सलाहकार फ़ाइल लॉकिंग को अनुमति देता है।

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); 
     } 
    } 
} 

भी देखें http://gitweb.zfs-fuse.net/?p=sehe;a=blob;f=src/zfs-fuse/util.c;h=7c9816cc895db4f65b94592eebf96d05cd2c369a;hb=refs/heads/maint

+0

बहुत अच्छा पढ़ा और दिलचस्प लेख। काश मैं इस जवाब को भी स्वीकार कर सकता हूं। निष्पक्षता के लिए उन्नयन। – ereOn

1

इस समस्या से संपर्क करने का एक तरीका है संलग्न करने के लिए फ़ाइल खोलना। यदि फ़ंक्शन सफल होता है और स्थिति 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); 
} 
+1

धारणा है कि फ़ाइल खाली हो जाएगी बल्कि बदनामी है। यह ज्यादातर बार हो सकता है लेकिन विनिर्देशों को जानने के बिना ... –

+0

लेकिन स्थिति हो सकती है जब कुछ धागे इस फ़ाइल को एक साथ खोलते हैं। उनमें से प्रत्येक स्थिति की जांच करेगा। एक मौका है कि प्रत्येक थ्रेड के लिए स्थिति शून्य होगी। और फिर हमारे पास डेटा दौड़ होगी। – Andigor

0

ऐसा प्रतीत होता है कि धाराओं का सख्ती से इसका उपयोग करने का कोई तरीका नहीं है।

आप इसके बजाय, ओपन का उपयोग कर सकते हैं (जैसा कि उपरोक्त वर्णित है) और यदि यह सफल होता है, तो उसी फ़ाइल को स्ट्रीम के रूप में खोलने के लिए आगे बढ़ें। बेशक, यदि आप फ़ाइल में लिख रहे हैं तो एक पीआईडी ​​है, यह स्पष्ट नहीं है कि आप इसे सी-स्टाइल लिखने() का उपयोग करके क्यों नहीं लिखेंगे।

O_EXCL केवल उन अन्य प्रक्रियाओं को शामिल करता है जो O_EXCL का उपयोग करके उसी फ़ाइल को खोलने का प्रयास कर रहे हैं। यह, ज़ाहिर है, इसका मतलब है कि आपके पास कभी भी सही गारंटी नहीं है, लेकिन यदि फ़ाइल का नाम/स्थान कहीं और है तो कोई भी अन्य खुलने की संभावना नहीं है (जो लोग आप जानते हैं, उनके अलावा O_EXCL का उपयोग कर रहे हैं) आपको ठीक होना चाहिए।

+0

असल में 'O_EXCL' * * अन्य सभी प्रक्रियाओं को बहिष्कृत करता है, न केवल उन फ़ाइलों को' O_EXCL' के साथ खोलने का प्रयास करता है। आप शायद 'झुंड' और दोस्तों के बारे में सोच रहे हैं? – zwol

+0

नहीं। मैन पेज पर भरोसा किया। – DRVic

+0

मुझे लगता है कि एक ऐसी भावना है जिसमें आप सही हैं। 'O_EXCL' विफल हो जाएगी यदि फ़ाइल पहले से मौजूद है, भले ही अन्य प्रक्रियाओं ने क्या किया या किया। लेकिन * 'ओपन' के बाद फ़ाइल * बनाता है, एक और प्रक्रिया साथ आ सकती है और बिना 'O_EXCL' (या यहां तक ​​कि' O_CREAT') के बिना इसे खोल सकती है और सफल हो सकती है। आप 'ओपन (fname, O_WRONLY | O_CREAT | O_EXCL, 0000)' - हाँ, शून्य मोड - उस बिंदु पर * आपकी प्रक्रिया * फ़ाइल लिख सकते हैं, लेकिन कोई अन्य गैर-रूट प्रक्रिया खुल सकती है जब तक आप (या कोई और नहीं) 'chmod यह है। – zwol