लिनक्स पर एक ओपन फाइल हैंडल का क्या होता है अगर पॉइंटेड फाइल को स्थानांतरित या हटा दिया जाता है


107

यदि इंगित फ़ाइल को इस प्रकार मिलता है तो लिनक्स पर एक खुले फ़ाइल हैंडल का क्या होता है:

  • हट गए -> क्या फ़ाइल हैंडल वैध रहता है?
  • हटाए गए -> क्या यह एक EBADF के लिए नेतृत्व करता है, एक अवैध फ़ाइल हैंडल का संकेत देता है?
  • एक नई फ़ाइल द्वारा प्रतिस्थापित -> क्या फ़ाइल इस नई फ़ाइल की ओर इशारा करती है?
  • एक नई फ़ाइल के लिए एक कड़ी से बदला -> क्या मेरी फ़ाइल इस लिंक को "फॉलो" करती है?
  • किसी नई फ़ाइल की सॉफ्ट लिंक द्वारा प्रतिस्थापित -> क्या मेरी फाइल हैंडल अब इस सॉफ्ट लिंक फाइल को हिट करती है?

मैं इस तरह के सवाल क्यों पूछ रहा हूं: मैं हॉट-प्लग हार्डवेयर (जैसे यूएसबी डिवाइस आदि) का उपयोग कर रहा हूं। ऐसा हो सकता है, कि डिवाइस (और उसकी / dev / फ़ाइल) भी उपयोगकर्ता या किसी अन्य ग्रेमलिन द्वारा रीटेट की गई हो।

इससे निपटने के लिए सबसे अच्छा अभ्यास क्या है?

जवाबों:


159

यदि फ़ाइल को स्थानांतरित किया जाता है (उसी फाइल सिस्टम में) या नाम बदला जाता है, तो फ़ाइल हैंडल खुला रहता है और अभी भी फ़ाइल को पढ़ने और लिखने के लिए उपयोग किया जा सकता है।

यदि फ़ाइल हटा दी जाती है, तो फ़ाइल हैंडल खुला रहता है और अभी भी इसका उपयोग किया जा सकता है (यह वह नहीं है जो कुछ लोग उम्मीद करते हैं)। अंतिम हैंडल बंद होने तक फ़ाइल को वास्तव में हटाया नहीं जाएगा।

यदि फ़ाइल को नई फ़ाइल से बदल दिया जाता है, तो यह वास्तव में कैसे निर्भर करता है। यदि फ़ाइल की सामग्री ओवरराइट की गई है, तो फ़ाइल हैंडल अभी भी मान्य होगा और नई सामग्री तक पहुंच प्राप्त करेगा। यदि मौजूदा फ़ाइल को अनलिंक किया गया है और एक नया नाम उसी नाम से बनाया गया है या, यदि एक नई फ़ाइल का उपयोग करके मौजूदा फ़ाइल पर ले जाया जाता है rename(), तो यह विलोपन (ऊपर देखें) के समान है - अर्थात, फ़ाइल हैंडल को संदर्भित करना जारी रहेगा मूल फ़ाइल का संस्करण।

सामान्य तौर पर, एक बार फ़ाइल खोलने के बाद, फ़ाइल खुली होती है, और निर्देशिका संरचना को बदलने वाला कोई भी नहीं बदल सकता है - वे स्थानांतरित कर सकते हैं, फ़ाइल का नाम बदल सकते हैं या इसके स्थान पर कुछ और डाल सकते हैं, यह बस खुला रहता है।

यूनिक्स में कोई डिलीट नहीं है, केवल unlink(), जो समझ में आता है क्योंकि यह जरूरी नहीं कि फाइल डिलीट हो जाए - बस डायरेक्टरी से लिंक हटा देता है।


यदि दूसरी ओर अंतर्निहित डिवाइस गायब हो जाता है (जैसे USB अनप्लग) तो फ़ाइल हैंडल किसी भी अधिक मान्य नहीं होगा और किसी भी ऑपरेशन पर IO / त्रुटि देने की संभावना है। हालांकि आपको इसे अभी भी बंद करना है। यदि डिवाइस वापस प्लग किया जाता है, तो भी यह सही होने वाला है, क्योंकि इस मामले में किसी फ़ाइल को खुला रखना समझदारी नहीं है।


मुझे लगता है कि अगर फ़ाइल की निर्देशिका को हटा दिया जाता है तो आपका दूसरा बिंदु समान रूप से लागू होता है। ऐसा क्या?
ड्रू नॉक ने

2
मुझे एक बात में दिलचस्पी है: यदि आप किसी फ़ाइल को अधिलेखित करने के लिए cp कमांड का उपयोग करते हैं, तो क्या यह पहला मामला है या दूसरा मामला है?
xuhdev

1
" अंतिम हैंडल बंद होने तक फ़ाइल को वास्तव में हटाया नहीं जाएगा। " दिलचस्प है। धन्यवाद
गेरीमिया

8

फ़ाइल हैंडल एक मार्ग को इंगित नहीं करता है, इसलिए आपके अधिकांश परिदृश्य अभी भी आपके मान के अनुसार काम करते हैं, क्योंकि हैंडल अभी भी फ़ाइल को इंगित करता है।

विशेष रूप से, हटाए गए परिदृश्य के साथ - फ़ंक्शन को एक कारण के लिए "अनलिंक" कहा जाता है, यह फ़ाइल नाम (डेंट्री) और एक फ़ाइल के बीच एक "लिंक" को नष्ट कर देता है। जब आप किसी फ़ाइल को खोलते हैं, तो उसे अनलिंक करते हैं, फ़ाइल तब भी मौजूद रहती है जब तक कि उसकी संदर्भ संख्या शून्य पर नहीं जाती, जब आप हैंडल को बंद करते हैं।

संपादित करें: हार्डवेयर के मामले में, आपने एक विशिष्ट डिवाइस नोड के लिए एक हैंडल खोला है, यदि आप डिवाइस को अनप्लग करते हैं, तो कर्नेल सभी एक्सेस को विफल कर देगा, भले ही डिवाइस वापस आ जाए। आपको डिवाइस को बंद करना होगा और इसे फिर से खोलना होगा।


5

मैं अन्य प्रचालनों के बारे में निश्चित नहीं हूं, लेकिन हटाने के लिए: विलोपन बस तब तक नहीं होता है (जब तक कि फ़ाइल सिस्टम के अंतिम खुले हैंडल बंद नहीं हो जाते)। इस प्रकार यह आपके आवेदन के तहत एक फ़ाइल को हटाने के लिए संभव नहीं होना चाहिए।

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

अन्य सामानों पर लागू होने वाले समान विचार संभव है।


4

यदि आप जांचना चाहते हैं कि फ़ाइल हैंडलर (फ़ाइल डिस्क्रिप्टर) ठीक है या नहीं, तो आप इस फ़ंक्शन को कॉल कर सकते हैं।

/**
 * version : 1.1
 *    date : 2015-02-05
 *    func : check if the fileDescriptor is fine.
 */

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

/**
 * On success, zero is returned.  On error, -1  is  returned,  and  errno  is  set
 *      appropriately.
 */
int check_fd_fine(int fd) {
    struct stat _stat;
    int ret = -1;
    if(!fcntl(fd, F_GETFL)) {
        if(!fstat(fd, &_stat)) {
            if(_stat.st_nlink >= 1)
                ret = 0;
            else
                printf("File was deleted!\n");
        }
    }
    if(errno != 0)
        perror("check_fd_fine");
    return ret;
}

int main() {
    int fd = -1;
    fd = open("/dev/ttyUSB1", O_RDONLY);
    if(fd < 0) {
        perror("open file fail");
        return -1;
    }
    // close or remove file(remove usb device)
//  close(fd);
    sleep(5);
    if(!check_fd_fine(fd)) {
        printf("fd okay!\n");
    } else {
        printf("fd bad!\n");
    }
    close(fd);
    return 0;
}

1
if(!fcntl(fd, F_GETFL)) {चेक की क्या बात है ? मुझे लगता है कि आप EBADFवहां खोज रहे हैं। (आप भी शायद errno0 को इनिशियलाइज़ करना भूल गए )।
woky

यह मेरे लिए काम नहीं है। मैंने इस दृष्टिकोण का उपयोग करने की कोशिश की है open(O_WRONLY|O_APPEND)- st_nlink हमेशा रहें> = 1 जब मेरा डिस्क्रिप्टर खोला जाता है।
imbearr

2

हटाए गए फ़ाइल की मेमोरी मेमोरी जानकारी (आपके द्वारा दिए गए सभी उदाहरण एक हटाई गई फ़ाइल के उदाहरण हैं) और साथ ही जब तक फ़ाइल बंद नहीं होती तब तक ऑन-डिस्क अस्तित्व में रहते हैं।

हार्डवेयर को हॉटप्लग किया जाना एक पूरी तरह से अलग मुद्दा है, और आपको अपने प्रोग्राम को लंबे समय तक जीवित रहने की उम्मीद नहीं करनी चाहिए अगर ऑन-डिस्क इनोडेस या मेटाडेटा बिल्कुल बदल गया है ।


2

निम्न प्रयोग से पता चलता है कि MarkR का उत्तर सही है।

code.c:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <stdio.h>

void perror_and_exit() {
  perror(NULL);
  exit(1);
}

int main(int argc, char *argv[]) {
  int fd;
  if ((fd = open("data", O_RDONLY)) == -1) {
    perror_and_exit();
  }
  char buf[5];
  for (int i = 0; i < 5; i++) {
    bzero(buf, 5);
    if (read(fd, buf, 5) != 5) {
      perror_and_exit();
    }
    printf("line: %s", buf);
    sleep(20);
  }
  if (close(fd) != 0) {
    perror_and_exit();
  }
  return 0;
}

डेटा:

1234
1234
1234
1234
1234

gcc code.cउत्पादन करने के लिए उपयोग करें a.out। दौड़ो ./a.out। जब आप निम्न आउटपुट देखें:

line: 1234

rm dataहटाने के लिए उपयोग करें data। लेकिन ./a.outत्रुटियों के बिना चलना जारी रखेगा और निम्न संपूर्ण आउटपुट का उत्पादन करेगा:

line: 1234
line: 1234
line: 1234
line: 1234
line: 1234

मैंने उबंटू 16.04.3 पर प्रयोग किया है।


1

के तहत / proc / निर्देशिका आप वर्तमान में सक्रिय हर प्रक्रिया की एक सूची मिल जाएगा, बस अपने PID और संबंधित सभी डेटा वहाँ है। एक इंटररस्टिंग जानकारी फ़ोल्डर fd / है, आपको वर्तमान में प्रक्रिया द्वारा खोले गए सभी फ़ाइल हैंडलर मिल जाएंगे।

आखिरकार आपको अपने डिवाइस के लिए एक प्रतीकात्मक लिंक मिलेगा (अंडर / देव / या यहां तक ​​कि / बस / यूएसबी /), अगर डिवाइस लटका हुआ है तो लिंक मृत हो जाएगा और इस हैंडल को रीफ्रेश करना असंभव होगा, प्रक्रिया को बंद करना होगा और इसे फिर से खोलें (यहां तक ​​कि पुन: संयोजन के साथ)

यह कोड आपके PID के लिंक करंट स्टेटस को पढ़ सकता है

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>

int main() {
    // the directory we are going to open
    DIR           *d;

    // max length of strings
    int maxpathlength=256;

    // the buffer for the full path
    char path[maxpathlength];

    // /proc/PID/fs contains the list of the open file descriptors among the respective filenames
    sprintf(path,"/proc/%i/fd/",getpid() );

    printf("List of %s:\n",path);

    struct dirent *dir;
    d = opendir(path);
    if (d) {
        //loop for each file inside d
        while ((dir = readdir(d)) != NULL) {

            //let's check if it is a symbolic link
            if (dir->d_type == DT_LNK) {

                const int maxlength = 256;

                //string returned by readlink()
                char hardfile[maxlength];

                //string length returned by readlink()
                int len;

                //tempath will contain the current filename among the fullpath
                char tempath[maxlength];

                sprintf(tempath,"%s%s",path,dir->d_name);
                if ((len=readlink(tempath,hardfile,maxlength-1))!=-1) {
                    hardfile[len]='\0';
                        printf("%s -> %s\n", dir->d_name,hardfile);

                } else
                    printf("error when executing readlink() on %s\n",tempath);

            }
        }

        closedir(d);
    }
    return 0;
}

यह अंतिम कोड सरल है, आप लिंकेट फ़ंक्शन के साथ खेल सकते हैं।

int
open_dir(char * path)
{
  int fd;

  path = strdup(path);
  *strrchr(path, '/') = '\0';
  fd = open(path, O_RDONLY | O_DIRECTORY);
  free(path);

  return fd;
}

int
main(int argc, char * argv[])
{
  int odir, ndir;
  char * ofile, * nfile;
  int status;

  if (argc != 3)
    return 1;

  odir = open_dir(argv[1]);
  ofile = strrchr(argv[1], '/') + 1;

  ndir = open_dir(argv[2]);
  nfile = strrchr(argv[2], '/') + 1;

  status = linkat(odir, ofile, ndir, nfile, AT_SYMLINK_FOLLOW);
if (status) {
  perror("linkat failed");
}


  return 0;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.