File and Directory

난1렙이요·2024년 12월 15일

시스템 프로그래밍

목록 보기
16/22

File System

  • OS는 의미있는 정보들을 파일 단위로 모아서 정리해준다.
  • File System은 파일과 속성들의 집합이다.
  • 파일의 위치나 이름 등을 별도로 관리한다.
  • 디스크에 새 파일을 만들면 파일의 위치를 직접 접근하는 대신 파일 이름과 offset을 통해서 접근할 수 있게 해준다.
  • 대부분의 파일 시스템은 트리 구조를 가지고 있다.

Directory

  • 디렉토리는 파일의 한 종류이다.
  • 디렉토리는 directory entrie를 포함하는 파일을 의미한다.
    • directory entrie는 파일 이름과 offset의 쌍을 의미한다.
  • 디렉토리는 안에 다른 파일을 포함할 수 있다.

Directory의 종류

  • Root directory
    • ‘/’
    • 파일 시스템 트리에서 맨 위에 있는 디렉토리다.
    • 모든 파일은 루트 디렉토리 아래(안)에 있다.
  • Parent directory
    • “..”
    • 현재 작업중인 디렉토리의 부모 디렉토리를 나타낸다.
  • Current working directory
    • “.”
    • 현재 작업중인 디렉토리를 나타낸다.
  • Home directory
    • "~"
    • 사용자 아이디와 암호를 가지고 시스템에 로그인 한 후 제일 처음으로 들어가는 디렉토리를 나타낸다.
    • 보통 사용자 이름의 디렉토리다.
  • Sub-directory
    • 다른 디렉토리 안에 속해 있는 디렉토리를 나타낸다.

Pathname

  • 작업 경로를 나타낼 수 있는 방식은 두가지가 있다.
    • 절대 경로 : 무조건 "/"로 시작하며 루트부터 기준으로 파일 위치를 나타낸다.
    • 상대 경로 : 현재 작업 디렉토리를 기준으로 파일 위치를 나타낸다.

Change Directory

#include <unistd.h> 
int chdir(const char *path);
  • chdir은 현재 디렉토리를 지정한 디렉토리로 바꾸는 명령어다.
  • path : 내가 가고 싶은 디렉토리
  • Return
    • 만약 성공적으로 변경했으면 0을 반환한다.
    • 만약 실패했으면 -1을 반환한다.

Get Current Directory

#include <unistd.h> 
char *getcwd(char *buf, size_t size);
  • getcwd는 현재 디렉토리를 알려주는 명령어다.
  • buf : 현재 디렉토리를 buf에 저장한다(output).
  • size : buffer의 크기
  • Return
    • 만약 성공적으로 변경했으면 buf를 반환한다.
    • 만약 실패했으면 NULL을 반환한다

Buffer Size

  • 위 함수에서 buf의 크기를 생각해보자.
  • 만약 디렉토리 위치가 절대경로에서 짧으면 buf에 많이 담을 필요가 없다.
  • 만약 디렉토리 위치가 절대경로에서 길면 buf에 다 못 담게 된다.
  • 하나의 보장할 수 있는 방법은 PATH_MAX를 이용하는 방법이다.
  • PATH_MAX는 파일 디렉토리의 최대 위치를 보장해주는 숫자다.
    • 시스템에서 PATH_MAX가 정의가 되어있지 않을 수 있다.
    • PATH_MAX가 정해져 있지 않을 수 있기 때문에 다음과 같은 구문과 같이 사용한다.
    #ifndef PATH_MAX
    #define PATH_MAX 255

Search Paths

  • 실행 파일의 이름만 주게 되면, OS는 실행 파일의 이름을 PATH라는 환경 변수에 지정된 경로에서부터 찾는다.
  • "which" 명령을 통해서 파일 이름에 따른 경로를 찾을 수 있다.
  • 현재 작업 디렉토리를 추가하는, 다시 말해 PATH에 "."을 추가하는 방법은 편리하지만 보안상의 문제가 존재한다.
    • 만약 악성 파일 "ls"를 누가 추가해놓는다.
    • 사용자가 "ls"명령어를 사용한다.
    • 이 때 PATH에 "."을 추가했으면 ls명령어가 사용되는 것이 아닌 악성 파일 "ls"가 실행 될 가능성이 존재한다.

Directory Access

  • 프로그램 상에서 디렉토리를 참조하는 함수는 3가지가 있다.
  • opendir
  • closedir
  • readdir

Opendir

  • Opendir을 통해서 디렉토리를 열 수 있다.
#include <sys/types.h>
#include <dirent.h>
DIR *opendir (const char *dirname);
  • const char *dirname : 열고자 하는 타겟 디렉토리의 이름을 의미한다
  • return value
    • 성공 : DIR pointer를 반환한다. , name of directory that we want to open
    • 실패 : null pointer를 반환한다.
  • DIR은 directory stream을 의미한다.
  • directory stream은 타겟 디렉토리 안에 나열된 정보들의 sequence를 말한다.

Readdir

  • Readdir을 통해서 디렉토리를 읽을 수 있다.
#include <sys/types.h>
#include <dirent.h>
struct dirent *readdir (DIR *dirptr);
  • DIR *dirptr : 읽고자 하는 디렉토리의 directory stream을 의미한다.
  • Readdir은 디렉토리 안의 entry값을 참조하고 있다. 참조값은 읽을 때마다 이동하며, file offset과 비슷한 역할을 한다고 볼 수 있다.
  • return value
    • 성공 : 첫번째 디렉토리 entry값이 구조체 dirent * 형태로 반환된다. 이후 다음으로 옮겨간다.
    • 실패 : null pointer를 반환한다.
  • 구조체 dirent는 여러개의 요소가 있지만 중요한 건 아래 두가지다.
    • ino_t : inode 번호
    • char : 파일이름

Closedir

  • Closedir을 통해서 디렉토리를 닫을 수 있다.
#include <dirent.h>
int closedir (DIR *dirptr);
  • DIR *dirptr : 닫고자 하는 디렉토리의 directory stream을 의미한다.
  • return value
    • 성공 : 0을 반환한다.
    • 실패 : -1을 반환한다.

Rewinddir

  • Readdir를 사용하면 디렉토리에서 참조하는 entry값이 자동으로 변한다.
  • Rewinddir은 참조하는 entry값을 최초로 되돌린다.
#include <dirent.h>
int rewinddir (DIR *dirptr);
  • DIR *dirptr : 처음으로 되돌리고자 하는 디렉토리의 directory stream을 의미한다.

File Status Information

  • 파일을 만들게 되면 그 파일에 대한 여러가지 정보가 존재한다.
  • 파일의 정보를 얻을 수 있는 함수는 2가지가 있다.
    • lstat : 링크 파일에 대한 정보를 반환한다.
    • stat : 링크가 가리키고 있는 원본 파일의 정보를 반환한다.
  • 이 두가지 정보는 파일 경로가 symbolic link일 때 사용한다.
#include <sys/stat.h>
int lstat(const char *restrict path, struct stat *restrict buf); 
int stat(const char *restrict path, struct stat *restrict buf);

구조체 stat structure

Defined in sys/stat.h

  • dev_t st_dev;
    • 파일을 포함하고 있는 Device의 Device ID
  • ino_t st_ino;
    • 파일의 시리얼 넘버
  • mode_t st_mode;
    • 파일의 모드
  • nlink_t st_nlink;
    • 하드 링크의 개수
  • uid_t st_uid;
    • 파일의 User ID
  • gid_t st_gid;
    • 파일의 Group ID
  • off_t st_size;
    • 이 파일의 크기(symbolic link)
  • time_t st_atime;
    • 마지막에 접근한 시간
  • time_t st_mtime;
    • 마지막으로 수정된 시간
  • time_t st_ctime;
    • 마지막으로 상태가 변경된 시간

UNIX File Implementation

  • POSIX는 파일의 타입을 크게 구분하지 않는다. 대부분 사용자의 편의를 위해 파일을 구분한다.
  • 그 대신 유닉스는 tree structure를 통해 파일을 정리한다.
  • 디렉토리는 파일 이름과 파일을 찾아갈 수 있는 inode를 가진다.

inode

  • 파일이 생성되었을 때, 파일에 대한 정보를 저장하는 데이터 구조를 inode라고 한다.
  • 모든 파일은 inode를 표시하는 inode number를 부여받는다.
  • inode는 다음과 같은 파일의 정보를 저장한다.
    • user나 group의 ownership
    • 엑세스 모드
    • 파일의 타입
    • stat structure의 요소들
    • ...
  • 시스템에서 만들 수 있는 inode의 개수는 정해져 있다.
  • inode의 크기는 고정되어 있다.

  • file information : 파일에 대한 정보를 저장한다.
  • direct pointers : 파일 블럭을 직접 가리킨다. 다이렉트 포인터를 따라가면 직접 파일에 접근할 수 있다.
  • indirect pointer : 간접적으로 파일을 가리키는 포인터다.
    • single indirect pointer : 한 단계 거쳐서 파일을 가리킨다. 인다이렉트 포인터는 포인터 블럭을 가리키게 된다. 이 포인터 블럭 안에는 다이렉트 포인터가 쌓여 있기 때문에, 포인터 블럭의 요소들을 참조해서 파일에 직접 접근할 수 있다.
    • double indirect pointer : 두 단계 거쳐서 파일을 가리킨다. 더블 인다이렉트 포인터는 싱글 인다이렉트 포인터를 가리킨다. 그러므로 싱글 인다이렉트 포인터보다 큰 파일을 참조할 수 있다.
    • triple indirect pointer : 세 단계 거쳐서 파일을 가리킨다. 더블 인다이렉트 포인터와 하는 일이 비슷하며, 엄청나게 큰 파일에 접근할 때 사용한다.

Directory implementation

  • 디렉토리 또한 하나의 파일이다. 디렉토리 타입의 파일인 것이다.
  • 디렉토리는 파일 이름과 파일 경로를 가지고 있다.
  • 디렉토리 = inode number + filename
  • inode에 접근하는 방법은 다음과 같다.
    • 프로그램은 경로 이름을 통해서 파일에 접근한다.
    • OS는 root directory에서부터 하나씩 접근하며 파일 이름과 inode number를 찾는다.
    • OS가 inode number를 찾으면 이를 통해 파일에 직접적으로 접근 가능하다.
  • inode + filename 구조를 통해 얻는 이점은 다음과 같다.
    • 파일 이름을 바꿀 때 directory entry만 바꾸면 되는 것이므로 간편하다.
    • 파일의 경로를 바꿀 때 directory entry만 바꾸면 되는 것이므로 간편한다.
    • 디스크 상에 물리적인 실체는 하나만 존재해도 그 파일을 나타내는 이름은 여러개가 존재할 수 있다. 예를 들어 바로가기 같은 경우는 파일을 복사할 필요가 없이 같은 directory entry를 가지는 파일을 추가하면 된다.
    • directory entry는 파일 크기가 작기 때문에 용량이나 성능면으로도 큰 이점이 생긴다.

inode pointer의 예시

어떤 inode 객체가 128 bytes이고, 포인터는 4 bytes이며, status information은 68 bytes를 차지한다고 가정해보자.

  • 블럭의 사이즈는 8K bytes이고 block pointers는 4 bytes라고 가정한다.
  • 이 inode안에 direct pointers는 얼마나 있는가?
    – single, double, triple indirect pointers는 4 bytes씩 차지한다.(12 bytes)
    – 128-68-12 = 48 bytes -> 48/4 = 12 direct pointers
  • direct pointer를 가지고 얼마나 큰 파일을 가리킬 수 있는가?
    • 8K = 8 * 2^10 bytes
    • 8192 * 12 = 98,304 bytes
  • singel indirect pointers는 얼마나 큰 파일을 가리킬 수 있는가?
    • single indirect pointer는 8K 블럭을 가리킨다. 8K 블럭은 8192/4 = 2048 pointers 개의 direct pointer를 가질 수 있다.
    • 2048 * 8192 = 16,777,216 bytes
    • (2 2^10) (8 2^10) = (2 8) * (2^20) = 16 MB
  • Link는 filename과 inode간의 관계를 표현한 것이다.
  • UNIX는 두 가지의 Link를 가진다.
    • Hard Link
    • Symbolic Link(Soft Link)
  • Directory entiy는 hard link로 불린다. 왜나하면 filename과 inode를 직접 연결시키기 때문이다.
  • Symbolic Link는 간접적인 연결을 한다. Symbolic Link는 하나의 파일이다. 이 파일은 원본 파일로 가는 경로를 가지고 있다. 이후 경로를 다시 탐색해서 원본 파일으로 접근한다.
  • 어떤 inode를 가리키는 hard link는 여러개가 있을 수 있다. 그렇기 때문에 inode는 자신을 가리키고 있는 hard link의 개수를 가지고 있다.
  • Hard link는 하드디스크상의 inode를 찾아갈 수 있는 직접적인 pointer 정보를 가지고 있다.
  • 같은 파일을 가리키는 Hard link가 여러개일 수 있다.
  • 어떠한 파일을 Hard link를 통해서 접근한다. 이 때 다른 이름을 통해서 접근하면, 모든 변경은 원본 파일에 영향을 준다.
  • Hard link는 같은 파일 시스템 안에서만 통한다.
  • 기존 파일을 가리키는 새로운 Hard link를 생성할 수 있으며 directory entry만 추가된다.
  • ln 명령어를 사용하면 Hard link를 생성할 수 있다.
  • 새로운 Hard link를 생성하면 inode의 link count가 증가하며, Hard link를 삭제하면 참조하던 inode의 link count가 감소한다.
#include <unistd.h>
int link (const char *path1, const char *path2);
int unlink (const char *path);
  • link
    • path1 : 원본 파일의 경로명
    • path2 : 새롭게 만들어진 hardlink의 경로명
  • unlink
    • path : 삭제할 hardlink의 경로명
  • 대부분의 파일 시스템은 위에서 말한 Hard Link를 구현한다.
  • inode가 언제 필요없는지를 확인하기 위해 Link Counter가 필요하다.
  • Link Counter는 integer값이다.
  • Link Counter는 자신을 참조하는 Hard Link값을 나타낸다.
  • 새로운 Hard link가 생기면 Link Counter값은 증가한다.
  • 기존에 참조하던 Hard Link가 없어지면 Link Counter값은 감소한다.
  • Hard Link값이 0이 되면 시스템은 대부분 inode 자체를 삭제한다(파일을 삭제한다).
  • Symbolic Link는 filname과 inode간에 간접적인 영향을 가지는 Link이다.
  • Symbolic Link를 만들면 Symbolic Link라는 파일을 만들게 된다.
    • 이 파일은 원본 파일의 경로를 가지고 있다.
    • OS는 Symbolic Link의 파일을 열면 경로를 발견하게 된다.
    • 이후 탐색을 중단하는 것이 아니라 다시 경로를 탐색한다.
  • Symbolic Link를 만들려면 ln 명령어에 -s라는 옵션을 준다.
  • Symbolic Link는 inode의 link count에 영향을 주지 않는다.
#include <unistd.h>
int symlink (const char *path1, const char *path2);
  • symlink
    • path1 : 원본 파일의 경로명
    • path2 : 새롭게 만들어진 hardlink의 경로명
profile
다크 모드의 노예

0개의 댓글