중요한 점! 파일의 이름은 잊어버리세요!! inode가 파일입니다!

 

파일의 이름을 실제 파일이라고 생각하기 쉽습니다. 하지만 그렇지 않습니다! 대신 inode를 파일처럼 생각하세요. inode는 메타 정보(마지막 접근, 소유권, 크기 등)을 가지고 있고, 파일 내용이 있는 디스크 블록을 가리키고 있습니다.

 

So... How do we implement a directory?

디렉토리는 단지 inode 숫자에 이름을 붙인 것입니다. POSIX에서는 각각의 엔트리에서 파일이름과 inode의 숫자를 읽는 함수의 집합을 제공합니다. 실제 파일 시스템에서는 어떻게 보일지 생각해봅시다. 이론적으로는 디렉토리들은 실제 파일들과 같습니다. 디스크 블록은 디렉토리 항목이나 dirent를 포함하고 있습니다. 즉, 디스크 블록은 아래와 같이 보입니다.

inode_num name
2043567 hi.txt
...  

각각의 디렉토리  엔트리는 고정된 크기거나 c-string 변수일수도 있습니다. 이것은 특정 파일시스템이 저레벨에서 어떻게 수행되는지에 따라 다릅니다.

 

How can I find the inode number of a file?

shell에서 -i 옵션을 사용하여 ls를 실행하면 됩니다.

$ ls -i
12983989 dirlist.c		12984068 sandwich.c

 

아래에서 소개하겠지만, C에서는 stat 함수들 중 하나를 호출하면 됩니다.

 

How do I find out meta-information about a file(or directory?)

파일의 메타정보를 알아내기 위해서 stat 호출을 사용합니다. 예를 들어 notes.txt에 가장 마지막으로 접근한 시간을 알기 위해서는 다음과 같이 사용합니다.

   struct stat s;
   stat("notes.txt", &s);
   printf("Last accessed %s", ctime(&s.st_atime));

stat에는 3가지 종류가 있습니다.

       int stat(const char *path, struct stat *buf);
       int fstat(int fd, struct stat *buf);
       int lstat(const char *path, struct stat *buf);

예를 들어 파일과 연관된 파일 디스크립터를 이미 가지고 있다면 fstat을 사용해 파일에 대한 메타 정보를 알아낼 수 있습니다.

   FILE *file = fopen("notes.txt", "r");
   int fd = fileno(file); /* Just for fun - extract the file descriptor from a C FILE struct */
   struct stat s;
   fstat(fd, & s);
   printf("Last accessed %s", ctime(&s.st_atime));

마지막 lstat은 나중에 symbolic link를 소개하면서 얘기해 보도록 하겠습니다.

접근, 생성, 수정 시간에 추가로 stat 구조체는 inode의 번호, 파일의 길이, 소유자 정보 등을 포함하고 있습니다.

struct stat {
               dev_t     st_dev;     /* ID of device containing file */
               ino_t     st_ino;     /* inode number */
               mode_t    st_mode;    /* protection */
               nlink_t   st_nlink;   /* number of hard links */
               uid_t     st_uid;     /* user ID of owner */
               gid_t     st_gid;     /* group ID of owner */
               dev_t     st_rdev;    /* device ID (if special file) */
               off_t     st_size;    /* total size, in bytes */
               blksize_t st_blksize; /* blocksize for file system I/O */
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
               time_t    st_atime;   /* time of last access */
               time_t    st_mtime;   /* time of last modification */
               time_t    st_ctime;   /* time of last status change */
           };

How do I list the contents of a directory?

디렉토리의 내용물들을 보여주기 위해 ls를 직접 만들어보도록 하겠습니다.

#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
int main(int argc, char **argv) {
    if(argc == 1) {
        printf("Usage: %s [directory]\n", *argv);
        exit(0);
    }
    struct dirent *dp;
    DIR *dirp = opendir(argv[1]);
    while ((dp = readdir(dirp)) != NULL) {
        puts(dp->d_name);
    }

    closedir(dirp);
    return 0;
}

How do I read the contents of a directory?

opendir, readdir, closedir을 사용합니다. 예를 들어, 위의 예에서 보듯이 opendir과 readdir, closedir을 사용합니다. 

주의사항: 포크를 호출한 후, parent나 child중 하나만(XOR) readdir,rewinddir, seekdir을 사용할 수 있습니다. 만약 parent와 child 둘 다 위의 함수를 사용한다면, 정의되지 않은 동작을 합니다.

How do I check to see if a file is in the current directory?

name이라는 이름을 가진 파일을 포함한 특정한 디렉토리를 찾기 위해 아래와 같은 코드를 짤수도 있습니다.(버그를 찾아보세요)

int exists(char *directory, char *name)  {
    struct dirent *dp;
    DIR *dirp = opendir(directory);
    while ((dp = readdir(dirp)) != NULL) {
        puts(dp->d_name);
        if (!strcmp(dp->d_name, name)) {
 	    return 1; /* Found */
        }
    }
    closedir(dirp);
    return 0; /* Not Found */
}

위 코드의 경우 리소스가 새고있습니다. 만약 filename을 찾는다면 빠른 리턴이 발생해 closedir이 호출되지 않습니다. opendir에 의해 어떤 파일 디스크립터가 열리고 메모리가 할당되면 할당해제가 되지 않습니다. 즉 프로세스는 리소스를 다 사용해 open이나 opendir 호출이 실패하게 됩니다.

이를 수정하기 위해서는 가능한 코드 흐름에서 리소스를 할당 해제해주는 것이 필요합니다. 위 코드에서는 return 1을 하기 전에  closedir을 먼저 호출해줘야 합니다. C에서는 모든 리소스가 코드의 흐름에서 할당해제되는 것을 보장하도록 도와주지 않기 때문에 흔히 발생하는 버그입니다.

 

What are the gatcha's of using readdir? For example to recursively search directories?

readdir을 사용하는데 두 가지의 주요한 문제점과 한 개의 고려사항이 있습니다.

readdir 함수는 .(현재 디렉토리)와 ..(부모 디렉토리)를 반환합니다. 만약 하위 디렉토리들을 찾고 싶다면 이러한 디렉토리들을 명시적으로 제외해야 할 필요가 있습니다.

많은 어플리케이션에서 하위 디렉토리를 recursive하게 탐색하기 전에 현재 디렉토리를 체크하는 것이 합리적입니다. linked list에 결과를 저장하거나 디렉토리 구조체를 리셋하여 시작부터 다시 시작하게 함으로써 구현할 수 있습니다.

 

주의: readdir을 스레드세이프가 아닙니다. 멀티스레드에서는 readdir_r을 사용해 호출자는 존재하는 구조체의 주소를 넘겨줘야 합니다.

 

더 상세한 정보는 readdir의 man page를 살펴보세요.

 

How do I determine if a directory entry is a directory?

디렉토리 엔트리가 디렉토리인지 확인하기 이해서는 S_ISDIR을 사용해 stat structure에 있는 mode bit를 체크해야 합니다.

regular file인지 체크하기 위해서는 S_ISREG를 사용합니다.

 

   struct stat s;
   if (0 == stat(name, &s)) {
      printf("%s ", name);
      if (S_ISDIR( s.st_mode)) puts("is a directory");
      if (S_ISREG( s.st_mode)) puts("is a regular file");
   } else {
      perror("stat failed - are you sure I can read this file's meta data?");
   }

Does a directory have an inode too?

디렉토리도 inode를 가지고 있습니다. 더 좋은 생각은 디렉토리도 파일처럼 inode(inode contents와 디렉토리 이름을 가지고있는)라고 생각하는 것입니다. 단지 특별한 종류의 inode라고 생각하셔도 됩니다.

 

wikipedia에서는 UNIX 디렉토리는 하나의 파일 이름과 하나의 inode 번호를 가진 관련 구조체들의 리스트들이라고 정의합니다.

 

기억하세요. inode는 파일 이름을 가지고 있지 않습니다. 오직 파일의 메타정보만을 가지고 있습니다.

 

How can I have the smae file appear in two different places in my file system?

가장 먼저 기억해야 할 점은 파일 이름과 파일은 같지 않다는 것입니다(file name != file). inode는 파일이라고 생각하고, 디렉토리는 이름들의 리스트라고 생각하시면 됩니다. 각각의 이름은 inode number와 매칭됩니다. 이러한 inode들중 일부는 regular file의 inode이고, 나머지는 디렉토리의 inode들입니다.

 

만약 파일시스템에 파일을 이미 가지고 있다면 ln 명령어를 통해 같은 inode에 다른 link를 생성할 수 있습니다.

$ ln file1.txt blip.txt

하지만 blip.txt는 file1과 동일한 파일입니다. 만약 blip를 수정한다면 file1.txt도 수정하는 것입니다. 두 파일의 inode를 확인하여 같은 inode를 가지는 것을 확인할 수 있습니다.

$ ls -i file1.txt blip.txt
134235 file1.txt
134235 blip.txt

이러한 종류의 연결(디렉토리 엔트리라고 알려진)을 hard link라고 합니다.

동일한 C 호출은 link입니다.

link(const char *path1, const char *path2);

link("file1.txt", "blip.txt");

단순한 예를 보이기 위해 위 예는 같은 디렉토리 안에 hard link를 만들었지만 hard link는 같은 파일시스템 어디서든 만들어질 수 있습니다.

 

What happens when I rm(remove) a file?

rm 또는 unlink를 사용해 파일을 살제할 때 디렉토리로부터 inode 참조를 제거합니다. 하지만 inode는 여전히 다른 디렉토리들에 의해서 참조되고 있습니다. 파일의 내용이 여전히 필요한지 아닌지를 결정하기 위해서 각각의 inode는 reference count를 가지고 있습니다. 이 reference count는 새로운 링크가 생기거나 제거될 때마다 업데이트됩니다.

 

Case study:Backup software that minimizes file duplication

hard-link의 사용의 예로 파일 시스템의 다른 지점에 여러개의 아카이브를 효율적으로 생성하는 것입니다. 일단 아카이브의 area가 특정 파일의 copy를 가지고 있다면 미래의 아카이브는 복사한 파일을 생성하는 대신에 이러한 아카이브 파일을 다시 사용할 수 있습니다. 애플의 time machine 소프트웨어가 이 일을 해줍니다.

 

Can I create hard links to directories as well as regular files?

디렉토리는 hard link를 만들 수 없습니다. 아니, 사실 가능은 합니다. 사실 이 동작을 하길 원하진 않을 겁니다. POSIX standard는 이러한 동작을 하지 말라고 합니다. ln 명령어는 -d 옵션을 주었을 때 오직 root만이 가능하도록 허락하고 있습니다. 하지만 대부분의 filesystem이 이 동작을 막기 때문에 root도 이러한 동작을 수행하지 못할 수 도 있습니다.

 

Why does POSIX prevent hard links to directories?

파일 시스템의 무결성은 디렉토리 구조체가 루트 디렉토리에서 도달할 수 있는 비순환 트리라고 가정합니다(나중에 이야기할 soft link를 제외하고). directory link가 허용된다면 제약 조건을 확인하거나 시행하는 데 많은 비용이 들게 됩니다. 이러한 가정을 파괴하면 파일 무결성 도구가 파일 시스템을 복구하지 못할 수도 있습니다. recursive 검색은 영원히 종료되지 않을 가능성이 있고 하나 이상의 부모 디렉토리가 생기지만 ..은 오직 하나의 부모만 가리킬 수 있습니다. 아무튼 이러한 이유로 디렉토리의 hard link는 좋지 못한 생각입니다.

'Angrave System Programming > File Systems' 카테고리의 다른 글

File System: Permissions  (0) 2019.05.08
File System: Introduction  (0) 2019.05.02
Posted by 몰랑&봉봉
,