티스토리 뷰

창병모, 리눅스 프로그래밍 원리와 실제(생능출판, 2022)

6.1 파일 시스템

파일 시스템 구조

부트 블록(Boot block)

- 파일 시스템 시작부 위치, 첫 번째 섹터 차지

- 유닉스/리눅스가 처음 시작될 때 사용되는 부트스트랩 코드 저장되는 블록

 

슈퍼 블록(Super block)

- 전체 파일 시스템 대한 정보 저장

- 파일 시스템 내의 총 블록 수, 사용가능한 i-노드 개수, 사용 가능한 블록들을 나타내는 비트 맵, 블록의 크기, 사용 중인 블록 수, 사용 가능한 블록 수

 

i-리스트(i-list)

- 각각의 파일을 나타내는 모든 i-노드들의 리스트

- 한 블록은 약 40개 정도의 i-노드 포함

 

데이터 블록(Data block)

- 파일의 내용(데이터)을 저장하기 위한 블록

 

i-노드와 블록 포인터

- 리눅스에서 각 파일은 i-node라고 불리는 구조에 의해 표현

- 파일에 대한 모든 정보

- 파일의 내용을 저장하기 위해 직접 블록 포인터, 간접 블록  포인터, 이중 간접 블록 포인터, 삼중 간접 블록 포인터 존재

 

직접 블록 포인터(direct block pointer) : 파일 데이터 저장될 데이터 블록 주소, 12개 존재

 

간접 블록 포인터(indirect block pointer) : 직접 블록 포인터 저장

- 하나의 블록 포인터(주소) 크기가 4바이트고 한 블록의 크기가 4096바이트라면 간접 블록 포인터가 가리키는 하나의 데이터 블록 내에는 1024개의 직접 블록 포인터 저장 가능

 

이중 간접 블록 포인터(double indirect block pointer) : 간접 블록 포인터 개념 확장

- 1024개의 간접 블록 포인터 저장 -> 여기에 1024개의 직접 블록 포인터 저장

 

삼중 간접 블록 포인터(triple indirect block pointer) : 이중 간접 블록 포인터 한단계 더 확장

 

6.2 파일 입출력 구현

파일 열기 및 파일 입출력 구현

파일 디스크립터 배열(file descriptor array)

- 프로세스마다 파일 디스크립터 배열 하나씩 가짐

- 열린 파일의 파일 디스크립터 저장하기 위한 자료구조

 

파일 테이블(file table)

- 커널 자료구조로 열려진 모든 파일 목록을 저장하기 위한 자료구조

- 파일 테이블 엔트리로 구성, 파일을 열 때마다 파일 테이블 엔트리 만들어짐 

 

동적 i-노드 테이블(active i-node table)

- 커널 내의 자료구조로 열린 파일의 i-노드를 저장하기 위한 테이블

- 파일을 열면 파일 시스템 내에서 그 파일의 i-노드 내용을 가져와 이 테이블에 엔트리를 만듬

 

fd = open("file", O_RDONLY);

(1) 가장 먼저 파일 시스템 내에서 그 파일의 i-노드 찾음, 디렉터리에 저장되어 있고 이 정보 이용해야 찾을 수 있음

(2) i-노드 찾으면 i-노드 내의 모든 정보를 커널 내의 자료구조인 동적 i-노드 테이블로 가져와 테이블 내에 하나의 엔트리 만듬

(3) 커널 내의 자료구조인 파일 테이블에도 하나의 엔트리 만들고, 이 엔트리 내에 현재 파일 위치나 파일 상태 플래그 저장

(4) 프로세스 내의 자료구조인 파일 디스크립터 배열에 하나의 엔트리를 만들고 그 인덱스 반환

➡ 인덱스가 open() 시스템 호출이 반환하는 파일 디스크립터

 

파일 입출력 구현을 위한 자료구조

- 사용자가 열린 파일에 대해 읽기나 쓰기 입출력을 요청하면 어떻게 해당 데이터 블록을 찾을까?

- 커널 코드는 파일 테이블 엔트리 내의 현재 파일 위치 정보와 동적 i-노드 내의 블록 포인터 정보를 이용해 해당 데이터 블록의 위치 계산

- 커널 내의 코드는 이렇게 블록의 위치를 결정한 다음 해당 블록에서 데이터를 읽거나 해당 블록에 데이터 씀

 

한 파일을 두 번 열기 구현

 

- 이미 열었던 파일의 i-노드 내용이 동적 i-노드 테이블에 존재할 것

- 따라서 동적 i-노드 테이블 내에 새로운 엔트리 만들 필요 X

- 열린 파일 테이블 내에는 새로운 엔트리 만들어야 함

- 새로 파일을 열게 되면 거기에 따라 현재 파일 위치, 파일 플래그 등을 새로 설정해야 하기 때문

- fd 테이블에도 새로운 엔트리 하나 만들어 새로운 파일 디스크립터 반환

 

dup() 시스템 호출 구현

 

- dup() 혹은 dup2() 시스템 호출을 하는 경우

- 기존에 열려 있는 파일을 공유하는 새로운 파일 디스크립터 반환

- i-노드 테이블이나 열린 파일 테이블 내에 새로운 엔트리 만들 필요 X

- fd 테이블 내에만 새로운 엔트리(파일 디스크립터)를 하나 만들어 열린 파일 테이블 내의 기조에 열려 있는 파일의 엔트리 가리키도록 하고 새로운 파일 디스크립터 반환

- 해당 엔트리 refcnt는 2가 됨

- 기존 파일을 공유해 사용

 

6.3 파일 상태 정보

파일 상태와 stat()

 

- 파일 하나당 하나의 i-노드가 있으며 i-노드 내에 파일에 대한 모든 상태 정보 저장

 

- 파일에 대한 상태 정보를 원하면 i-노드에 저장되어 있는 상태 정보 가져와야 함

➡ stat() 시스템 호출

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

- lstat()는 stat()와 같은데 대상 파일이 심볼릭 링크일 때 링크 자체에 대한 상태 정보를 가져옴 (링크 가리키는 파일 X)

 

- stat() 시스템 호출은 파일의 상태 정보를 가져와서 stat 구조체에 저장

 

파일 타입

 

- 파일 타입을 검사하기 위해서는 stat() 시스템 호출의 결과로 받은 stat 구조체의 st_mode 필드 내부를 조사해야 하는데 복잡하기 때문에 매크로 함수를 제공 (아래)

 

- 위 매크로 함수는 해당 파일이면 1반환, 아니면 0 반환

 

접근권한 변경 chmod()

int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

- 지정된 파일의 접근권한을 mode로 변경

 

fchmod 664 you.txt

- you.txt 파일의 접근권한을 664로 변경

 

접근 및 수정 시간 변경 utime

int utime(const char *filename, const struct utimebuf *times);

- 지정된 파일의 접근 및 수정 시간을 변경

- 성공하면 0, 실패하면 -1 반환

 

파일 소유자 변경 chown()

int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);

- path가 나타내는 파일의 사용자 ID와 그룹 ID를 변경

- 성공하면 0, 실패하면 -1 반환

 

6.4 디렉터리

디렉터리 내용

 

- 일련의 디렉터리 엔트리 저장, 각 디렉터리 엔트리는 디렉터리 내에 있는 하나의 파일 나타냄

- 디렉터리 엔트리는 위와 같은 구조체로 정의

- 하나의 디렉터리 엔트리는 파일 이름과 그 파일의 i-노드 번호로 구성

 

DIR *opendir(const char *path);

- path가 나타내는 디렉터리 오픈

- 성공하면 DIR 구조체 포인터 반환, 실패하면 NULL

 

struct dirent *readdir(DIR *dp);

- dp가 가리키는 디렉터리에서 한 번에 하나씩 디렉터리 엔트리 읽어서 반환

 

printStat() 함수

 

type() 함수

 

st_mode 필드

- lstat() 시스템 호출을 해 상태정보를 가져오면 파일 타입과 접근권한 정보는 st->st_mode 필드에 저장

- type() 함수는 st_mode 필드 내에 있는 파일 타입 정보를 조사하여 출력할 수 있는 하나의 문자로 반환

 

perm() 함수

- st_mode 필드 내에 있는 파일의 접근권한 정보를 조사해 출력할 수 있는 문자열 형태로 반환

- ex) rwxr-xr-x

 

디렉터리 조작

- 새로운 디렉터리를 만들기 위해서는 mkdir() 시스템 호출을 사용

- 디렉터리 지우기 위해서는 rmdir() 시스템 호출

 

디렉터리 구현

 

- 디렉터리도 다른 파일처럼 i-노드로 표현

- 내용은 다른 파일과 달리 그 디렉터리 안에 있는 파일의 이름과 그 파일의 i-노드 번호로 구성된 디렉터리 엔트리

 

- 루트, bin, usr 디렉터리는 각각 2번, 3번, 4번 i-노드로 표현

 

6.5 링크

하드 링크

 

int link(char *existing, char *new);

- 기존 파일 이름 existing에 새로운 링크 new를 만듬

- 성공하면 0, 실패하면 -1 반환

 

int unlink(char *path);

- 링크 path 제거

- 성공 0, 실패 -1

 

 

심볼릭 링크

- 심볼릭 링크는 실제 파일의 경로명을 따라서 데이터 블록에 저장하고 있는 링크로서 파일에 대한 간접적인 포인터 역할

- 따라서 다른 파일 시스템에 있는 파일도 경로명을 이용해 링크

 

int symlink(const char *actualpath, const char *sympath);

- 실제 파일의 경로면 actualpath를 저장하고 있는 심볼릭 링크 sympath를 만듬

- 성공 0, 실패 -1

 

심볼릭 링크 내용 확인

- 심볼릭 링크의 실제 내용을 읽어서 지정한 버퍼에 저장

- 끝에 널문자 추가해주지 않음

int readlink(const char *path, char *buf, size_t bufsize);​

- path 심볼릭 링크의 실제 내용을 읽어서 buf에 저장

- 성공하면 buf에 저장한 바이트 수를 반환, 실패 -1

 

 

Comments