뾰족한 파일이 이동되거나 삭제되면 Linux에서 열린 파일 핸들은 어떻게 되나요?


107

뾰족한 파일이 다음과 같은 경우 Linux에서 열린 파일 핸들은 어떻게됩니까?

  • 멀리 이동-> 파일 핸들이 유효합니까?
  • 삭제됨-> 잘못된 파일 핸들을 나타내는 EBADF로 이어 집니까?
  • 새 파일로 대체-> 파일 핸들이이 새 파일을 가리키는가?
  • 새 파일에 대한 하드 링크로 대체 됨-> 내 파일이이 링크를 "팔로우"처리합니까?
  • 새 파일에 대한 소프트 링크로 대체 됨-> 내 파일 핸들이 지금이 소프트 링크 파일에 도달합니까?

이러한 질문을하는 이유 : 핫 플러그 ​​하드웨어 (예 : USB 장치 등)를 사용하고 있습니다. 사용자 또는 다른 Gremlin이 장치 (및 해당 / dev / 파일)를 다시 연결할 수 있습니다.

이것을 다루는 가장 좋은 방법은 무엇입니까?

답변:


159

파일이 이동 (동일한 파일 시스템에서)되거나 이름이 변경된 경우 파일 핸들은 열린 상태로 유지되며 파일을 읽고 쓰는 데 계속 사용할 수 있습니다.

파일이 삭제되면 파일 핸들은 열린 상태로 유지되며 계속 사용할 수 있습니다 (일부 사람들이 예상하는 것과 다릅니다). 마지막 핸들이 닫힐 때까지 파일은 실제로 삭제되지 않습니다.

파일이 새 파일로 대체되는 경우 정확히 방법에 따라 다릅니다. 파일의 내용을 덮어 쓰더라도 파일 핸들은 여전히 ​​유효하며 새 내용에 액세스합니다. 기존 파일이 연결 해제되고 동일한 이름으로 새 파일이 생성되거나를 사용하여 새 파일이 기존 파일로 이동되는 경우 rename()삭제와 동일합니다 (위 참조). 즉, 파일 핸들이 계속 참조합니다. 파일 의 원래 버전.

일반적으로 파일이 열리면 파일이 열리 며 디렉토리 구조를 변경하는 사람은이를 변경할 수 없습니다. 파일을 이동하거나 파일 이름을 바꾸거나 그 자리에 다른 것을 넣을 수 있습니다.

Unix에서는 unlink()파일을 반드시 삭제하지 않고 디렉토리에서 링크를 제거하기 때문에.


반면에 기본 장치가 사라지면 (예 : USB 분리) 파일 핸들이 더 이상 유효하지 않으며 모든 작업에서 IO / 오류가 발생할 수 있습니다. 그래도 닫아야합니다. 이 경우 파일을 열어 두는 것이 합리적이지 않기 때문에 장치가 다시 연결되어 있어도 마찬가지입니다.


파일의 포함 디렉터리가 삭제되면 두 번째 요점이 동일하게 적용된다고 가정합니다. 그렇습니까?
Drew Noakes 2014 년

2
한 가지 관심이 있습니다. cp 명령을 사용하여 파일을 덮어 쓰면 첫 번째 경우입니까, 두 번째 경우입니까?
xuhdev 2014

1
" 마지막 핸들이 닫힐 때까지 파일은 실제로 삭제되지 않습니다. "흥미 롭습니다. 감사합니다
Geremia

8

파일 핸들은 경로가 아닌 inode를 가리 키므로 핸들이 여전히 파일을 가리 키기 때문에 대부분의 시나리오는 가정대로 작동합니다.

특히, 삭제 시나리오에서이 기능은 이유 때문에 "연결 해제"라고 불리며 파일 이름 (덴 트리)과 파일 사이의 "연결"을 파괴합니다. 파일을 연 다음 링크를 해제하면 참조 횟수가 0이 될 때까지, 즉 핸들을 닫을 때까지 파일은 실제로 여전히 존재합니다.

편집 : 하드웨어의 경우 특정 장치 노드에 대한 핸들을 열었습니다. 장치를 분리하면 장치가 다시 돌아 오더라도 커널이 모든 액세스에 실패합니다. 장치를 닫았다가 다시 열어야합니다.


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

삭제 된 파일 (사용자가 제공하는 모든 예는 삭제 된 파일의 인스턴스)의 메모리 내 정보와 디스크상의 inode는 파일이 닫힐 때까지 존재합니다.

핫 플러그되는 하드웨어는 완전히 다른 문제이며 디스크상의 inode 또는 메타 데이터가 전혀 변경된 경우 프로그램이 오래 지속될 것으로 기 대해서는 안됩니다 .


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

Ubuntu 16.04.3에서 실험을 수행했습니다.


1

/ proc / 디렉토리에서 현재 활성화 된 모든 프로세스의 목록을 찾을 수 있으며 PID와 관련된 모든 데이터를 찾을 수 있습니다. interresting 정보는 폴더 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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.