web-dev-qa-db-ja.com

ポイントされたファイルが移動、削除された場合、Linuxで開いているファイルハンドルはどうなりますか

Linuxで開かれているファイルハンドルは、その間にポイントされたファイルが次のようになるとどうなりますか。

  • 移動->ファイルハンドルは有効のままですか?
  • 削除->これは、無効なファイルハンドルを示すEBADFにつながりますか?
  • 新しいファイルに置き換えられました->ファイルはこの新しいファイルを指し示していますか?
  • 新しいファイルへのハードリンクに置き換えられます->ファイルはこのリンクを「フォロー」しますか?
  • 新しいファイルへのソフトリンクに置き換えられました->ファイルハンドルはこのソフトリンクファイルにヒットしますか?

このような質問をする理由:ホットプラグされたハードウェア(USBデバイスなど)を使用しています。デバイス(およびその/ dev/file)がユーザーまたは別のGremlinによって再接続されることがあります。

これに対処するベストプラクティスは何ですか?

92
Maus

ファイルが(同じファイルシステム内で)移動または名前変更された場合、ファイルハンドルは開いたままで、ファイルの読み取りと書き込みに引き続き使用できます。

ファイルが削除されても、ファイルハンドルは開いたままであり、引き続き使用できます(これは一部の人々が期待するものではありません)。最後のハンドルが閉じられるまで、ファイルは実際には削除されません。

ファイルが新しいファイルで置き換えられる場合、それは正確にどのように依存します。ファイルのコンテンツが上書きされた場合、ファイルハンドルは引き続き有効であり、新しいコンテンツにアクセスします。既存のファイルがリンク解除され、同じ名前で新しいファイルが作成された場合、またはrename()を使用して新しいファイルが既存のファイルに移動された場合、削除と同じです(上記を参照)ファイルハンドルは、引き続きoriginalバージョンのファイルを参照します。

一般に、ファイルが開かれると、ファイルが開かれ、ディレクトリ構造を変更した人は誰も変更できません。移動、ファイル名の変更、またはその場所に何かを置くことができますが、開いたままです。

Unixには削除はありません。unlink()のみです。これはファイルを必ずしも削除するわけではないので理にかなっています。ディレクトリからリンクを削除するだけです。


一方、基になるデバイスが消えた場合(USBの取り外しなど)、ファイルハンドルはこれ以上有効ではなくなり、操作でIO /エラーが発生する可能性があります。ただし、まだ閉じる必要があります。この場合、ファイルを開いたままにしておくのは賢明ではないため、これはデバイスが再び接続された場合でも当てはまります。

133
MarkR

ファイルハンドルはパスではなくinodeを指しているため、ハンドルはまだファイルを指しているため、ほとんどのシナリオは想定どおりに機能します。

具体的には、削除シナリオでは、この関数は理由により「リンク解除」と呼ばれ、ファイル名(デントリ)とファイル間の「リンク」を破棄します。ファイルを開いてリンクを解除すると、参照カウントがゼロになるまで、つまりハンドルを閉じるまで、ファイルは実際に存在します。

編集:ハードウェアの場合、特定のデバイスノードへのハンドルを開いているため、デバイスのプラグを抜くと、デバイスが戻ってもカーネルはすべてのアクセスに失敗します。デバイスを閉じて再度開く必要があります。

7
Ana Betts

他の操作についてはわかりませんが、削除に関しては、ファイルの最後に開いているハンドルが閉じられるまで、削除は(物理的に、つまりファイルシステムで)行われません。したがって、アプリケーションの下からファイルを削除することはできません。

いくつかのアプリ(気にならない)は、ファイルを作成、開く、すぐに削除することにより、この動作に依存します。ファイルは、アプリケーションとまったく同じ期間存続します-他のアプリケーションがプロセスマップなどを見てください。

同様の考慮事項が他のものにも当てはまる可能性があります。

4
Carl Smotricz

ファイルハンドラー(ファイル記述子)が正常かどうかを確認する場合は、この関数を呼び出すことができます。

/**
 * 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;
}
3
kangear

削除されたファイルのメモリ内情報(指定した例はすべて削除されたファイルのインスタンスです)およびディスク上のiノードは、ファイルが閉じられるまで存在し続けます。

ハードウェアのホットプラグはまったく別の問題であり、ディスク上のiノードまたはメタデータが変更された場合、プログラムが長く生き続けることを期待しないでくださいat)

次の実験は、 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 datadataを削除します。しかし、./a.outはエラーなしで実行を継続し、次の出力全体を生成します。

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

Ubuntu 16.04.3で実験を行いました。

2
Jingguo Yao

/ proc /ディレクトリの下に、現在アクティブなすべてのプロセスのリストがあります。PIDと関連するすべてのデータを見つけてください。興味深い情報はfd /フォルダーです。プロセスによって現在開かれているすべてのファイルハンドラーが見つかります。

最終的に、デバイスへのシンボリックリンク(/ dev /または/ proc/bus/usb /の下)が見つかります。デバイスがリンクをハングさせ、このハンドルを更新できない場合、プロセスを閉じて、再度開きます(再接続しても)

このコードは、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;
}

この最後のコードは単純で、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;
}
1
Douglas L