İşaretli dosya taşınır veya silinirse, Linux'ta açık bir dosya tutamacına ne olur?


107

Bu arada sivri uçlu dosya şunu alırsa, Linux'ta açık bir dosya tanıtıcısına ne olur:

  • Taşındı -> Dosya tanıtıcısı geçerli kalıyor mu?
  • Silindi -> Bu geçersiz bir dosya tutamacını gösteren bir EBADF'ye yol açar mı?
  • Yeni bir dosya ile değiştirildi -> Dosya tanıtıcısı bu yeni dosyayı gösteriyor mu?
  • Yeni bir dosyaya sabit bir bağlantı ile değiştirildi -> Dosyam bu bağlantıyı "takip" ediyor mu?
  • Yeni bir dosyaya yumuşak bağlantı ile değiştirildi -> Dosya tanıtıcım şimdi bu yumuşak bağlantı dosyasına çarptı mı?

Neden bu tür sorular soruyorum: Çalışır durumda takılı donanım kullanıyorum (USB cihazları vb.). Cihaz (ve ayrıca / dev / dosyası) kullanıcı veya başka bir Gremlin tarafından yeniden bağlanabilir.

Bununla ilgili en iyi uygulama nedir?

Yanıtlar:


159

Dosya taşınırsa (aynı dosya sisteminde) veya yeniden adlandırılırsa, dosya tanıtıcısı açık kalır ve dosyayı okumak ve yazmak için yine de kullanılabilir.

Dosya silinirse, dosya tanıtıcısı açık kalır ve yine de kullanılabilir (Bu, bazı kişilerin beklediği şey değildir). Dosya, son tutamaç kapatılana kadar gerçekten silinmeyecektir.

Dosya yeni bir dosya ile değiştirilirse, tam olarak nasıl olduğuna bağlıdır. Dosyanın içeriğinin üzerine yazılırsa, dosya tanıtıcısı hala geçerli olacak ve yeni içeriğe erişecektir. Mevcut dosyanın bağlantısı kaldırılırsa ve aynı adla yeni bir dosya oluşturulursa veya kullanılarak mevcut dosyaya yeni bir dosya taşınırsa rename(), bu silme işlemiyle aynıdır (yukarıya bakın) - yani, dosya tanıtıcısı başvurmaya devam eder orijinal dosyanın sürümü.

Genel olarak, dosya açıldıktan sonra dosya açıktır ve dizin yapısını değiştiren hiç kimse bunu değiştiremez - dosyayı taşıyabilir, yeniden adlandırabilir veya yerine başka bir şey koyabilir, açık kalır.

Unix'te yalnızca silme işlemi yoktur unlink(), bu mantıklıdır çünkü dosyayı mutlaka silmez - sadece bağlantıyı dizinden kaldırır.


Öte yandan, temeldeki cihaz kaybolursa (örn. USB'nin bağlantısını keser), dosya tanıtıcısı artık geçerli olmaz ve herhangi bir işlemde GÇ / hata verebilir. Yine de kapatmanız gerekiyor. Bu durumda bir dosyayı açık tutmak mantıklı olmadığından, cihaz tekrar takılsa bile bu doğru olacaktır.


Sanırım, dosyanın içeren bir dizini silindiğinde ikinci noktanız da aynı şekilde geçerlidir. Öyle mi?
Drew Noakes

2
Bir şeyle ilgileniyorum: Bir dosyanın üzerine yazmak için cp komutunu kullanırsanız, bu ilk durum mu yoksa ikinci durum mu?
xuhdev

1
" Dosya, son tutamaç kapatılana kadar gerçekten silinmeyecek. " İlginç. teşekkürler
Geremia

8

Dosya tutamaçları bir yola değil bir inode'a işaret eder, bu nedenle tutamaç hala dosyaya işaret ettiğinden senaryolarınızın çoğu varsaydığınız gibi çalışır.

Spesifik olarak, silme senaryosunda - işlev, bir nedenle "bağlantıyı kaldır" olarak adlandırılır, bir dosya adı (dişçilik) ve dosya arasındaki "bağlantıyı" yok eder. Bir dosyayı açıp bağlantısını kaldırdığınızda, dosya referans sayısı sıfıra gidene kadar, yani tutamacı kapattığınızda gerçekte varlığını sürdürür.

Düzenleme: Donanım söz konusu olduğunda, belirli bir aygıt düğümüne bir tutamaç açtınız, aygıtın fişini çekerseniz, çekirdek aygıt geri gelse bile ona tüm erişimlerinde başarısız olur. Cihazı kapatıp yeniden açmanız gerekecek.


5

Diğer işlemlerden emin değilim, ancak silme konusunda: Dosyanın son açık tutamacı kapanana kadar silme işlemi (fiziksel olarak, yani dosya sisteminde) gerçekleşmez. Bu nedenle, uygulamanızın altından bir dosyayı silmek mümkün olmamalıdır.

Birkaç uygulama (akla gelmeyen) dosyaları oluşturarak, açarak ve hemen silerek bu davranışa güvenir ve bunlar tam olarak uygulama ile aynı uzunluktadır - diğer uygulamaların ilk uygulamanın yaşam döngüsünden haberdar olmasına gerek kalmadan süreç haritalarına ve benzerlerine bakın.

Diğer şeyler için de benzer düşünceler geçerli olabilir.


4

dosya işleyicisinin (dosya tanımlayıcı) iyi olup olmadığını kontrol etmek istiyorsanız, bu işlevi çağırabilirsiniz.

/**
 * 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)) {Kontrol etmenin amacı nedir ? Sanırım EBADForayı arıyorsunuz . (Ayrıca muhtemelen errno0'a başlatmayı unuttunuz ).
woky

Bu benim için çalışmıyor. Bu yaklaşımı open(O_WRONLY|O_APPEND)- st_nlink ile birlikte kullanmayı denedim, tanımlayıcım açıkken daima> = 1 kalsın.
imbearr

2

Silinen bir dosyanın bellek içi bilgileri (verdiğiniz tüm örnekler, silinmiş bir dosyanın örnekleridir) ve diskteki düğümler, dosya kapatılana kadar varlığını sürdürür.

Donanımın takılı olması tamamen farklı bir sorundur ve disk üzerindeki düğümler veya meta veriler hiç değiştiyse programınızın uzun süre hayatta kalmasını beklememelisiniz .


2

Aşağıdaki deney, MarkR'nin cevabının doğru olduğunu göstermektedir .

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

veri:

1234
1234
1234
1234
1234

gcc code.cÜretmek için kullanın a.out. Çalıştır ./a.out. Aşağıdaki çıktıyı gördüğünüzde:

line: 1234

rm dataSilmek için kullanın data. Ancak ./a.outhatasız çalışmaya devam edecek ve aşağıdaki tüm çıktıyı üretecektir:

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

Deneyimi Ubuntu 16.04.3 üzerinde yaptım.


1

/ Proc / dizini altında şu anda aktif olan tüm işlemlerin bir listesini bulacaksınız, sadece PID'nizi bulun ve ilgili tüm veriler oradadır. İlgi çekici bir bilgi fd / klasörüdür, şu anda işlem tarafından açılmış olan tüm dosya işleyicilerini bulacaksınız.

Sonunda cihazınıza sembolik bir bağlantı bulacaksınız (/ dev / veya hatta / proc / bus / usb / altında), eğer cihaz askıda kalırsa bağlantı kesilir ve bu tanıtıcıyı yenilemek imkansız olur, işlem kapanmalı ve tekrar açın (yeniden bağlansa bile)

Bu kod, PID'nizin bağlantı mevcut durumunu okuyabilir

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

Bu son kod basittir, linkat işlevi ile oynayabilirsiniz.

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.