124

指定されたファイルがその間に取得された場合、Linux で開いているファイル ハンドルはどうなりますか。

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

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

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

4

7 に答える 7

178

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

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

ファイルが新しいファイルに置き換えられた場合、それは正確にその方法によって異なります。ファイルの内容が上書きされた場合でも、ファイル ハンドルは有効であり、新しい内容にアクセスできます。既存のファイルのリンクが解除され、同じ名前で新しいファイルが作成された場合、または を使用して新しいファイルが既存のファイルに移動された場合、rename()削除と同じです (上記を参照)。つまり、ファイル ハンドルは を参照し続けます。ファイルの元のバージョン。

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

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


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

于 2010-01-08T22:09:20.023 に答える
9

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

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

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

于 2010-01-08T16:13:47.697 に答える
5

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

いくつかのアプリ (思い浮かばない) は、ファイルを作成、開き、すぐに削除することで、この動作に依存しています。ファイルは、アプリケーションとまったく同じくらい存続します。他のアプリケーションは、必要なく最初のアプリのライフサイクルを認識することができます。プロセスマップなどを見てください。

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

于 2010-01-08T16:14:21.637 に答える
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;
}
于 2015-02-05T09:47:34.277 に答える
2

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

ハードウェアがホットプラグされることはまったく別の問題であり、ディスク上の inode またはメタデータがまったく変更されていない場合、プログラムが長く存続することを期待するべきではありません。

于 2010-01-08T16:13:24.963 に答える
1

/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;
}
于 2010-01-08T20:57:02.620 に答える