회고록 블로그

[운영체제 공부] 파일디스크립터(file descriptor) 공부하기 본문

3. Computer Science 공부/운영체제

[운영체제 공부] 파일디스크립터(file descriptor) 공부하기

김간장 2022. 4. 3. 05:16

이 글을 쓰기까지 꽤나 오랜 시간이 걸렸다.

 

3월 24일에 처음 파일 디스크립터를 공부하기 위해, 자료를 수집하며 공부를 하고 글을 썼는데

네 시간 동안 출처와 함께 정리한 글이 모두 날아가고(...)

마음의 상처를 받아 절대 다시는 정리글을 쓰지 않겠다고 눈물을 흘리다가

결국 오늘 다시 돌아와서 파일 디스크립터를 정리하는 글을 쓰고 있다.

 

다시는 날아가지 않도록 가능하면 초안은 노션(자동저장)에 써놓고 티스토리에는 나중에 옮겨 적어야겠다.. 🥹

 

 

※ 필자는 초초초초보자입니다. 틀린 내용은 언제든지 피드백 부탁드립니다.

 

개요


그렇다..

42 과제를 하다보니 파일 디스크립터를 이해해야 하는 순간이 왔다.

 

사실 문제를 풀기 위해서는 파일 디스크립터에 대해서 얕게 알고 넘어가도 될 것 같지만

동작 방식을 이해 하지 못하면 응용을 못하는 바보똥멍청이라서  

또한, 계속 파고 파면서 공부할 때 희열을 느끼는 변태같은(?) 성향이 있기 때문에

이왕 과제를 푸는 김에 파일 디스크립터를 깊게 공부 해보기로 했다.

 

공부할 때 참고한 출처는 각 문단의 하단에 남길 예정이다.

 

 

✅ 파일 디스크립터(file descriptor)?


유닉스/리눅스는 모든 것이 [파일]이다.

디렉터리, 텍스트 파일 등 뿐만 아니라

소켓, 파이프, 블록 디바이스(HDD, CD/DVD 등 장치를 말한다), 캐릭터 디바이스(키보드, 마우스, 모니터 등 장치가 있다)도 모두 [파일]이라고 보고 관리한다.

예를 들어서 리눅스를 설치할 때 파티션을 나누다보면 나중에 /dev 디렉터리 밑에서 sda1, sad2 등 파티션을 볼 수 있는데,

이 또한 이 곳에서는 [파일]이다. (= 블록 디바이스)

파티션(저장장치)인데도 말이다. 

 

시스템(커널)이 이러한 [파일]들에 접근하려면 파일 디스크립터(file descriptor)라는 것이 필요하다.

 

많은 글들을 찾아보고 읽어봤지만, 딱 잘라 파일 디스크립터가 무엇이다라고 말해주는 글을 잘 못본 것 같다.

그래서 정보처리기사 필기 정리본에서 찾아왔다.

 

파일 디스크립터는 한 줄로 정리하면

"파일을 관리하기 위해 운영체제가 필요로 하는 파일에 대한 정보를 갖고 있는 제어블록(FCB)"이라고 한다.

 

참고 자료 : https://dev-ahn.tistory.com/96

 

 

✅ 파일 디스크립터(file descriptor)의 역할


정의를 아는 것도 좋지만 실제로 무슨 역할을 하고, 왜 존재하는지 이해하는 것도 중요하다고 생각한다.

 

파일 디스크립터는 파일을 식별/접근하기 위해서 사용한다.

한마디로 파일을 관리하기 위해서 사용하는 것이다.

(파일을 식별하기 위한 "고유 식별자"인 것이다)

 

파일 디스크립터는 0 이상의 정수 값으로 되어 있다.

다만, 0은 표준입력 / 1은 표준출력 / 2는 표준에러로 이미 정해져 있기 때문에 0부터 2까지는 사용할 수 없다.

만약 파일을 열게 되면(open) 3부터 시작해서 순서대로 파일 디스크립터가 부여된다.

 


 

처음에 파일 디스크립터를 공부할 때, 왜 이게 필요한지 전혀 이해를 하지 못했다.

파일명으로 파일을 열고(open) 관리하면 되지 않나?

하지만, 리눅스(커널)가 파일을 오픈(open)하는 방법이 생각보다 복잡했다.

 

그래서 파일 디스크립터에 대한 공부를 넘어, 파일을 오픈(open)하는 과정에 대해서 공부를 시작했다.

이제 그걸 이해 해보자.

 

✅ 시스템에게 중요한건 파일의 내용이 아니라 [메타 데이터]이다


사람에게는 파일에 적힌 [내용]이 중요하지만 시스템 입장에서는 파일의 내용보다 [메타 데이터]를 더 중요하게 여긴다.

 

#메타데이터(meta data)?

데이터에 관한 구조화된 데이터이다. 다른 데이터를 설명해주는 데이터라고도 한다.

메타 데이터는 자산의 이름, 유형, 소유자, 제목, 설명 등을 담고 있는데 예를 들어서 여기 하나의 파일이 있다고 가정하자.

이 파일의 메타 데이터는 이런 것들이 될 수 있다.

파일의 소유자 : 김간장

파일 사이즈 : 4KB

생성시간 : 2022-03-24 14:47:32

데이터 저장 위치, 식별자, 타입 등등

 

특히, 이번 글에서는 메타데이터 중에서도 location과 protection을 유심하게 볼 것이다.

 

⚠️ protection

파일에 대해 부적절한 접근을 막는 것을 의미한다.

파일에 대해서 read, write, execute, append 등 권한을 부여한다.

인가되지 않은 사용자가 파일을 읽거나, 수정하는 것을 방지하기 위해서는 이 메타 데이터가 있어야 한다.

 

⚠️ location

운영체제가 파일에 접근하는 방법을 먼저 이해해야 한다.

쉽게 생각하기에는 만약 어떤 파일을 읽어야 한다고 하면,

운영체제가 디스크에서 해당 파일명을 검색해서 찾아내고 내용을 읽으면 될 것 같지만, 실제로는 이렇게 작동하지 않는다.

(이렇게 동작하지 않는 이유는 디스크와 CPU의 속도 차이 등 때문이 아닐까..)

 

운영체제가 파일에 접근할 때는 "찾고자 하는 파일의 메타 데이터를 메모리로 가져온 후 location 정보를 읽어서 파일의 위치를 찾는다"고 한다.

C언어를 기준으로 예시를 들어보자.

index.txt 라는 파일을 아래와 같이 열려고(open) 한다.

file = open("index.txt", O_RDWR);

이때 커널은 아래와 같이 동작한다고 한다.

1. open 함수를 통해서 디렉터리 탐색(directory search)을 한다.

2. 파일을 찾고, 해당 파일의 메타 데이터(=아마 inode로 추정된다)를 메모리에 로딩해둔다.

3. read나 write 함수 등을 통해서 파일을 읽거나 쓰게 되면, 메타 데이터의 location 정보를 참고해서 파일의 위치를 찾는다.

(출처 : https://operatingsystems.tistory.com/entry/OS-File-System)

 

이걸 시스템 콜이라고 하는데 일단 그건 나중에 정리하기로 하고

여기에서 정리해야할 사실은 세 가지이다.

1) 시스템 입장에서 파일을 볼 때 중요한건 메타 데이터이고,

2) 그 중에서도 protection과 location 정보가 중요하다.

3) 시스템(커널)이 파일에 접근할 때, 디스크에서 바로 파일의 위치로 접근하는게 아니다.

 

참고자료1 : https://operatingsystems.tistory.com/entry/OS-File-System-Protection

참고자료2 : https://operatingsystems.tistory.com/entry/OS-File-System

 

 

✅ 그렇다면 시스템은 파일의 내용을 어떻게 읽어올까


C언어의 open 함수를 통해 시스템이 파일을 어떻게 열어오는지 한번 공부해보자.

아까 위에서 봤던 파일 오픈(open) 코드를 다시 보자.

 

file = open("index.txt", O_RDWR);

이 open 함수로 특정 파일(index.txt)을 열려고 하면 커널은 정확하게 아래와 같은 일을 순차적으로 한다고 한다.

 

1. 디스크에서 파일(index.txt)을 찾는다.

2. 그 파일의 inode 정보를 가져온다.

3. 그리고 inode table에 빈 엔트리를 생성한다.

    혹은 이미 inode table에 해당 파일의 inode 정보가 존재한다면 빈 엔트리는 만들지 않는다.

    (이때, inode의 접근 권한 정보를 보고 권한을 확인한다)

4. 3에서 확인한 접근 권한(rwx)을 보고 열기를 허용하는지 확인한다.

5. inode가 있는 주소의 위치를 가리키는 포인터를 파일 테이블(file table) 엔트리에 저장한다.

    (이때, 접근 권한을 여기서도 한번 더 검사한다고 한다. 읽기전용, 쓰기/읽기 등)

6. 파일 테이블(file table)의 엔트리 위치를 가리키는 포인터를 파일 디스크립터 테이블(file descriptor table)의 엔트리에 저장한다.

7. 마지막으로 파일 디스크립터를 리턴한다.

(출처는 참고자료1, 참고자료2, 참고자료3을 확인해주세요)

 

※ 파일 디스크립터 테이블의 각 항목(entry)들은 파일 디스크립터와 파일 테이블을 가리키는 포인터.

이렇게 두개의 컬럼(?)으로 구성되어 있다.

※ 파일 테이블의 각 항목(entry)들은 파일 상태 플래그(읽기 전용, 읽기/쓰기, sync 등등)와 파일의 현재 위치(오프셋), inode 테이블에 대한 포인터로 구성되어 있다.

각 테이블의 항목이 어떤 컬럼(?)으로 구성되어 있는지 알면

왜 아래 그림에서 파일 디스크립터 테이블은 파일 테이블을 가리키고, 파일 테이블은 inode 테이블을 가리키는지 알 수 있다.

 

이후에는 앞서 위에서 얘기했던대로 read, write 등 함수를 이용해서

(파일의 내용을 읽고 쓰기 위해) 메타데이터(inode)의 위치 정보를 읽고 해당 위치로 가서 파일에 접근한다.

더 상세하고 구체적으로 알고자 한다면 아래의 참고자료1 URL을 꼭 보는 것이 좋을 것 같다.

 

 

너무 어려운 용어들이 막 나오기 시작해서 전혀 이해를 못하더라도 괜찮다.

하나씩 천천히 용어들을 공부하고 다시 위의 과정을 찬찬히 읽어보면 된다.

 

일단 파일 디스크립터 테이블과 파일 테이블, inode 테이블이 연결되어 있는 관계를 그림으로 확인하면 아래와 같다.

출처 : https://www.computerhope.com/jargon/f/file-descriptor.htm

 

참고자료1 : https://frontjang.info/entry/%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%ED%8C%8C%EC%9D%BC%EA%B0%84%EC%9D%98-%EC%97%B0%EA%B2%B0%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-1

참고자료2 : https://operatingsystems.tistory.com/entry/OS-File-System-Protection

참고자료3 : https://operatingsystems.tistory.com/entry/OS-File-System

 

 

✅ 파일을 여는(open) 과정을 이해하기 위해서 필요한 용어를 공부해보자


1. 디스크에서 파일(index.txt)을 찾는다.
2. 그 파일의 inode 정보를 가져온다.
3. 그리고 inode table에 빈 엔트리를 할당한다.
    혹은 이미 inode table에 해당 파일의 inode 정보가 존재한다면 빈 엔트리는 만들지 않는다.
    (이때, inode의 접근 권한 정보를 보고 권한을 확인한다)
4. 3에서 확인한 접근 권한(rwx)을 보고 열기를 허용하는지 확인한다.
5. inode가 있는 주소의 위치를 가리키는 포인터를 파일 테이블(file table)에 저장한다.
    (이때, 접근 권한을 여기서도 한번 더 검사한다고 한다)
6. 파일 테이블(file table)의 위치를 파일 디스크립터 테이블(file descriptor table)에 저장한다.
7. 마지막으로 파일 디스크립터 테이블(file descriptor table)의 주소를 리턴한다.

 

2번의 과정에서부터 갑자기 뜬금없이 inode라는 것이 나왔다. 이것부터 하나씩 차근차근 이해해보자.

 

⚠️ inode(아이노드)

확실하게 공부하려면 파일 시스템부터 알아봐야하지만, 시간이 부족하기 때문에 아주 간단하게 inode를 공부하기로 했다.

아이노드(inode)는 "유닉스 계통 파일 시스템에서 사용하는 자료구조"이다.

말이 너무 어렵다.

아주 쉽고 간단하게 말해서 파일에 대한 중요한 정보를 갖고 있는 일종의 데이터라고 한다.

앞서 위에서 시스템에게 중요한 것은 [메타 데이터]라고 했었다.

inode는 이 파일의 소유자가 누구인지, 파일의 사이즈는 얼마고, 파일의 위치는 어디이며, 마지막으로 수정한 날짜와 시간은 어떻게 되는지, 이 파일의 접근권한(rwx)은 어떻게 되는지 등등 파일에 대한 정보(메타 데이터)를 가지고 있다.

그 외 더 복잡한 것들이 있지만(15개의 블록 포인터 등) 일단 이 정도로만 공부하자.

1) 파일이나 디렉토리는 고유한 inode를 가지고 있다.

2) 그 inode는 파일/디렉토리에 대한 중요한 정보(메타 데이터)를 가지고 있다.

파일이나 디렉토리는 고유한 inode를 갖고 있다고 했으니 새 파일을 만들면 inode도 만들어질 것이다.

 

참고자료1 : https://i5i5.tistory.com/341

 

⚠️ inode(아이노드) table

신축 아파트를 지었을 때,

몇 호가 비워져있고 몇 호에 사람이 살고 있는지 파악하기 위해 표를 작성해서 관리하는 것처럼

수 많은 inode를 관리하기 위해서도 일종의 표 같은 것이 있다.

그게 바로 inode table이다.

새 파일이 생성되면 당연히 inode가 생성될 것이고 -> inode가 새로 만들어졌다면 inode를 관리하는 테이블에도 새로 만들어진 inode가 포함되어야 한다.

그렇기 때문에 새 파일을 만들면 당연히 inode table에 엔트리가 할당될 것이다.

 

⚠️ file table(파일 테이블)

많은 프로그램들은 소켓 통신도 하고, 특정 파일을 읽거나 쓰기도 해야하므로

커널에게 파일을 열어달라고(open) 요청을 하는 경우가 적지 않을 것이다.

파일을 열어달라고(open) 하는건 좋은데 어떤 파일을 열었고, 그 파일의 접근 권한은 어떻게 되고, 닫혔는지 등등을 알려면 이를 관리해줄 것이 필요하다.

그게 바로 파일 테이블(file table)이다.

 

즉, 파일 테이블은 프로세스에 의해 오픈된 파일의 읽기/쓰기 동작을 지원하기 위한 테이블이다.

파일이 열릴 때마다 하나씩 할당되기 때문에

1개의 프로세스가 "A" 라는 파일을 open 함수로 3번 열었다면 3개의 엔트리가 파일 테이블에 생성된다.

(출처 : https://m.blog.naver.com/songblue61/221391888403)

참고로 close() 함수를 사용해서 닫을 때 파일 테이블의 엔트리가 해제된다.

 

⚠️ file descriptor table(파일 디스크립터 테이블)

프로세스가 1개씩 가지고 있는 테이블이다.

프로세스가 현재 사용중인 파일을 관리하기 위해 존재하는 테이블이다.

파일 테이블(file table)과 분명하게 차이가 있다.

파일 디스크립터 테이블(file descriptor table)은 프로세스 A, 프로세스 B, 프로세스 C가 각각 가지고 있는 테이블이다.

각각의 프로세스가 사용중인 파일을 관리할 때 사용하는 것이다.

파일 디스크립터 테이블은 파일 테이블에 매핑된다.

 

작업을 하다보면 같은 파일을 오픈(open)하는 경우도 있을 것이다.

예를 들어 프로세스 A도 index.txt 파일을 열고, 프로세스 B도 indext.txt 파일을 열고 싶을 수 있다.

이때 프로세스 A의 파일 디스크립터와 프로세스 B의 파일 디스크립터는 같은 파일 테이블의 엔트리를 가리키게 된다.

 

참고자료: https://icarus8050.tistory.com/122

 

 

아래의 그림을 보면서 위에서 정리했던 파일 열기(open) 과정을 다시 상기하고, 각각의 테이블에 어떤 내용이 들어 있는지 고민해봐야겠다.

출처 : https://stackoverflow.com/questions/14189944/unix-system-file-tables 및 https://web.archive.org/web/20160619025634if_/http://www.cs.ucsb.edu/~rich/class/cs170/notes/FileSystem/filetable.rich.jpg

 

참고로, 파일 디스크립터는 lsof 명령어를 통해 확인할 수 있다.

 

✅ 별첨. 파일 디스크립터 0, 1, 2


앞서 파일 디스크립터 0, 1, 2는 이미 디폴트로 정해져있다고 했다.

정리하면 이렇다.

 

표준 입력(파일 디스크립터 0)

프로세스에 데이터를 입력하는 스트림이다. 특별한 경우가 아니라면 보통 키보드가 그 역할을 담당한다.

표준 출력(파일 디스크립터 1)

프로세스의 실행 결과나 실행 상태 보고를 출력하는 스트림이다. 기본적으로 디스플레이가 이 역할을 담당한다.

표준 에러(파일 디스크립터 2)

프로세스의 실행 상태나 에러 메시지를 출력하는 스트림이다. 기본값으로 디스플레이가 그 역할을 한다고 한다.

 

출처 : https://sangyeon96.gitbooks.io/linux-system/content/chapter3_section3.html

Comments