Apa yang terjadi pada pegangan file terbuka di Linux jika file yang ditunjuk dipindahkan atau dihapus

107

Apa yang terjadi pada pegangan file terbuka di Linux jika file yang ditunjuk sementara itu mendapatkan:

  • Dipindahkan -> Apakah pegangan file tetap valid?
  • Dihapus -> Apakah ini mengarah ke EBADF, menunjukkan pegangan file yang tidak valid?
  • Diganti dengan file baru -> Apakah pegangan file mengarah ke file baru ini?
  • Diganti oleh tautan keras ke file baru -> Apakah file saya menangani tautan "ikuti" ini?
  • Diganti dengan tautan lunak ke file baru -> Apakah pegangan file saya mengenai file tautan lunak ini sekarang?

Mengapa saya mengajukan pertanyaan seperti itu: Saya menggunakan perangkat keras yang sudah dicolokkan panas (seperti perangkat USB, dll.). Ini bisa terjadi, bahwa perangkat (dan juga / dev / filenya) dilampirkan kembali oleh pengguna atau GREMLIN lain.

Apa praktik terbaik dalam menangani ini?

Maus
sumber

Jawaban:

159

Jika file dipindahkan (dalam filesystem yang sama) atau diganti namanya, maka file handle tetap terbuka dan masih dapat digunakan untuk membaca dan menulis file.

Jika file dihapus, pegangan file tetap terbuka dan masih dapat digunakan (Ini bukan yang diharapkan sebagian orang). File tidak akan benar-benar dihapus sampai pegangan terakhir ditutup.

Jika file diganti dengan file baru, itu tergantung bagaimana caranya. Jika konten file ditimpa, file handle akan tetap valid dan mengakses konten baru. Jika file yang ada dibatalkan tautannya dan yang baru dibuat dengan nama yang sama atau, jika file baru dipindahkan ke file yang sudah ada menggunakan rename(), itu sama dengan penghapusan (lihat di atas) - artinya, pegangan file akan terus merujuk ke yang asli versi file.

Secara umum, setelah file dibuka, file terbuka, dan tidak ada yang mengubah struktur direktori dapat mengubahnya - mereka dapat memindahkan, mengganti nama file, atau meletakkan sesuatu yang lain pada tempatnya, itu tetap terbuka.

Di Unix tidak ada penghapusan, hanya unlink(), yang masuk akal karena tidak selalu menghapus file - hanya menghapus tautan dari direktori.


Jika di sisi lain perangkat yang mendasari menghilang (misalnya pencabutan USB) maka pegangan file tidak akan valid lagi dan kemungkinan akan memberikan IO / kesalahan pada operasi apa pun. Anda masih harus menutupnya. Ini akan menjadi kenyataan bahkan jika perangkat dicolokkan kembali, karena tidak masuk akal untuk membiarkan file tetap terbuka dalam kasus ini.

MarkR
sumber
Saya kira poin kedua Anda berlaku sama jika direktori yang berisi file dihapus. Apakah begitu?
Drew Noakes
2
Saya tertarik pada satu hal: jika Anda menggunakan perintah cp untuk menimpa file, apakah itu kasus pertama atau kedua?
xuhdev
1
" File tidak akan benar-benar dihapus sampai pegangan terakhir ditutup. " terima kasih
Geremia
8

Tuas file mengarah ke inode bukan ke jalur, jadi sebagian besar skenario Anda masih berfungsi seperti yang Anda asumsikan, karena gagang masih mengarah ke file.

Secara khusus, dengan skenario hapus - fungsi ini disebut "unlink" karena suatu alasan, ini menghancurkan "link" antara nama file (dentry) dan file. Saat Anda membuka file, lalu membatalkan tautannya, file tersebut sebenarnya masih ada hingga jumlah referensinya mencapai nol, yaitu saat Anda menutup pegangannya.

Sunting: Dalam kasus perangkat keras, Anda telah membuka pegangan ke node perangkat tertentu, jika Anda mencabut perangkat, kernel akan gagal semua akses ke sana, bahkan jika perangkat kembali. Anda harus menutup perangkat dan membukanya kembali.

Ana Betts
sumber
5

Saya tidak yakin tentang operasi lain, tetapi untuk penghapusan: Penghapusan tidak terjadi (secara fisik, yaitu dalam sistem file) sampai pegangan terbuka terakhir ke file ditutup. Oleh karena itu, tidak mungkin untuk menghapus file dari bawah aplikasi Anda.

Beberapa aplikasi (yang tidak terlintas dalam pikiran) mengandalkan perilaku ini, dengan membuat, membuka, dan segera menghapus file, yang kemudian hidup persis selama aplikasi - memungkinkan aplikasi lain untuk mengetahui siklus hidup aplikasi pertama tanpa perlu lihat peta proses dan semacamnya.

Mungkin pertimbangan serupa berlaku untuk hal-hal lain.

Carl Smotricz
sumber
4

jika Anda ingin memeriksa apakah penangan file (deskriptor file) baik-baik saja, Anda dapat memanggil fungsi ini.

/**
 * 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;
}
Kangear
sumber
1
Apa gunanya if(!fcntl(fd, F_GETFL)) {cek? Saya kira Anda sedang mencari di EBADFsana. (Anda mungkin juga lupa menginisialisasi errnoke 0).
woky
Ini tidak berhasil untuk saya. Saya telah mencoba menggunakan pendekatan ini dengan open(O_WRONLY|O_APPEND)- st_nlink always stay> = 1 saat deskriptor saya dibuka.
imbearr
2

Informasi dalam memori dari file yang dihapus (semua contoh yang Anda berikan adalah contoh file yang dihapus) serta inode pada disk tetap ada sampai file ditutup.

Perangkat keras yang sedang dicolokkan adalah masalah yang sama sekali berbeda, dan Anda seharusnya tidak mengharapkan program Anda tetap hidup jika inode atau metadata pada disk telah berubah sama sekali .

Ignacio Vazquez-Abrams
sumber
2

Eksperimen berikut menunjukkan bahwa jawaban MarkR benar.

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

data:

1234
1234
1234
1234
1234

Gunakan gcc code.cuntuk menghasilkan a.out. Lari ./a.out. Ketika Anda melihat output berikut:

line: 1234

Gunakan rm datauntuk menghapus data. Tetapi ./a.outakan terus berjalan tanpa kesalahan dan menghasilkan seluruh keluaran berikut:

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

Saya telah melakukan percobaan di Ubuntu 16.04.3.

Jingguo Yao
sumber
1

Di bawah / proc / direktori Anda akan menemukan daftar setiap proses yang sedang aktif, cukup temukan PID Anda dan semua data tentang ada di sana. Info yang menarik adalah folder fd /, Anda akan menemukan semua penangan file yang sedang dibuka oleh proses tersebut.

Akhirnya Anda akan menemukan tautan simbolis ke perangkat Anda (di bawah / dev / atau bahkan / proc / bus / usb /), jika perangkat hang tautannya akan mati dan tidak mungkin untuk menyegarkan pegangan ini, prosesnya harus ditutup dan buka lagi (bahkan dengan koneksi ulang)

Kode ini dapat membaca status link PID Anda saat ini

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

Kode terakhir ini sederhana, Anda dapat bermain dengan fungsi linkat.

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;
}
Douglas L.
sumber