Linux Programmer

파일(file)에 대한 토막글 : 파일이란 무엇인가? 본문

컴퓨터 관련/리눅스(유닉스) 일반

파일(file)에 대한 토막글 : 파일이란 무엇인가?

sunyzero 2019. 3. 8. 13:13

파일(file)에 대한 토막글 : 파일이란 무엇인가?

 

토막글인데 쓰다보니 길어졌다. 보기 귀찮은 분들은 TL;DR을 참고하자. (페북에 썼다가 짬이 생긴 시간에 정리해서 블로그에 올려둡니다.)

 

이 글은 운영체제(OS)의 핵심 파트인 파일 시스템(FS)을 이해하는데 필요한 중요 개념인 경로(path)에 대해 굉장히 길게 설명하는 글이다.  따라서 조금 지루할 수도 있음을 밝힌다.😣 참고로 경로를 구성하는 것은 directory와 filename이며 /bin/bash 라는 파일이 있다면 "/bin"이 디렉토리, "bash"가 파일네임(혹은 basename이라고도 함)이 된다. 

보통 file이라고 불리는 덩어리를 읽거나 쓰려면 메모리에 mapping(다른 말로 하면 사본을 만든다고 이해하면 됨)되어야 하는데, 이 과정에서 외부 접점(=경로)을 가지는 메모리를 named memory라고 하며, 반대로 mapping된 file의 실체(원본) 경로가 존재하지 않는 경우를 anonymous memory라고 부른다. 여기서 named라는 용어와 annoymous라는 용어가 나오는데, 이 용어의 의미를 알아두면 앞으로 코딩하거나 시스템을 설계할 때 큰 도움이 된다. 왜냐하면 가상메모리(VM, Virtual Memory)라고 불리는 것은 파일 시스템을 실제로 사용가능하게 만들기 위해 추상화하는 공간을 맵핑하는 것이 목적이므로 파일 시스템에서 쓰이는 용어와 개념은 VM에도 똑같이 쓰이기 때문이다.(프로그래밍에서 변수나 데이터를 다루는 것도 사실은 이 개념의 확장선상이다)

그래서 파일 시스템과 메모리의 연관성을 잘 이해하게되면 locality, memory hierarchy의 개념을 배울 때도 둘(named & anonymous)을 연관지어 이해할 수 있게 되며, 더 나아가 dirty memory에 대해서도 직관을 기를 수 있게 된다. 또한 세부적으로 file cache(page cache)가 왜 존재하는지도 이해할 수 있게 되어 더 빠른 시스템을 코딩하거나 설계 할 수 있는 능력을 갖출 수 있게 된다. (예를 들어 zfs같이 현대적인 FS가 cache를 구성하여 빠른 속도를 가져오는 것도 같은 이론적 배경에서 출발한다.)

 

실제로 기업체 특강을 가보면 의외로 file의 개념에 대해 일부만 알고 있는 programmer나 system engineer들이 꽤 있었다. 대부분 파일이라고 하면 그냥 TXT나 PDF 같이 데이터가 저장된 파일만 생각하는 경우가 많은데, 그런 경우는 추상적인 개념을 눈에 보이는 부분만 이해한 경우다. 그래서 file에 대해 간단한 상식 글을 가볍게 써볼까 했는데..... 엄청난 시간이 소요 됐다. 이런... ㅠㅠ

 

참고로 여기서 다루는 용어(terminology)들은 대부분 한글과 영어를 병기한다. 그리고 대부분은 영어로 쓴다. 그 이유는 나중에 검색을 할 때도 어차피 영어 단어를 쓰는 경우가 많기 때문에 영어 단어로 주로 표기하는 것이다.

 

1. 이 글을 쓰게된 이유

기업체 특강에서는 다수를 상대로 강의하다보니 모르는 용어나 개념이 나와도 모두들 질문하지 않는 암묵적인 불문율이 있다.(shy programmer들이 어디든 많다) 그래서 강의 도중에 아는지 모르는지 아리송할 때가 많다. 그러나 다 아는 줄알고 시험문제 냈다가... 파국을 맞기도 한다.😰

 

원래는 file과 memory 개념을 말할 때 용어나 개념은 생략하고 강의를 진행 해왔다.(요새는 비용절감 때문인지 강의 시간을 정말 타이트하게 줘서 어쩔 수 없다) 그러다가 몇 년전부터 출강하러가면 강의 전후로 시험문제를 출제해달라는 요청이 있어서, 시험 문제에 file과 memory에 대한 용어 및 개념에 대한 문제도 출제 했었다. 그런데 담당의 피드백에 의하면 file과 memory의 관계에 대해 틀리는 분들이 의외로 많았다고 했다. 원래 교과서에 나오는 개념을 물어본 것인데, 틀리는 경우를 추적해보니 교과서가 아닌 인터넷 검색으로 틀린 정보를 배웠다는 케이스가 많았다.(시험은 항상 오픈북, 인터넷 사용가능이었다. 그래서 틀린 내용을 구글링하는 경우가 많았다.)

 

결국 한 번은 파일에 대해 제대로 된 글을 써둘까 다짐 했고 마음 먹은지 한 3년 만에 쓰게 되었다. 절대 게을러서 그런게 아니라 그냥 바빠서다. 게임도 해야되고, 소설도 읽어야 하고, 밀린 드라마도 봐야되고, 정말 바빴다. 🤷‍♂️⁉️ (이렇게 정리해두면 나중에 강의할 때 읽어보라고 하면 편할 것 같긴 하다.)

 

1.1. POSIX 

여기서 자주 나오는 용어인 POSIX를 이야기 하고 넘어가겠다. POSIX[각주:1]파직스(pahz-icks)라고 읽는다. 절대로 포식스라고 읽는거 아니다. 번역서에 죄다 포식스라고 적는데 틀린 발음이다. POSIX 발음의 기원에 대한 것은 각주에 적어두었다. (포식스라고 하면 positive 46으로 읽히기 때문에 조심해야 한다.)

 

POSIX 이야기를 하는 것은 여기서 다루는 개념들은 POSIX 시스템을 기반으로 하는 것들이기 때문이다. 물론 특수한 목적의 OS(Operating system)를 빼고, 범용 OS 대부분이 POSIX 기반 운영체제이므로 대학의 운영체제론에서 배우는 개념의 구현(implementation)도 POSIX 운영체제라고 간주해도 무리가 없다. 그래서 OS를 제대로 이해하려면 modern OS의 근간이 된 POSIX system, 그 중에서도 UNIX와 Linux에 대한 이해가 필수적이다.

만일 특수한 운영체제라고 하더라고 POSIX의 기본 개념을 차용하고 슬림화시켜서 만드는 경우가 많으므로 POSIX 개념을 알아두는 것은 운영체제 전반을 이해하는데 있어서 매우 중요하다.

 

2. 파일(file)이란?

우리가 탐색기나 파일관리자에서 볼 수 있는 oooo.txt, oooo.pdf 같은 이름을 가진 파일, 즉 데이터를 저장하는 파일들은 정식 명칭으로는 regular file이라고 부른다. 그러면 regular file이 아닌 것도 있다고 생각할 수 있을 것이다.

가장 쉽게 볼 수 있는 non-regular file로는 디렉터리(directory)가 있다. 디렉터리는 파일 목록을 담고있는 파일이다. 더 쉽게 추상적으로 설명하면 파일목록을 담고 있는 HTML 파일이라고 간주하면 이해가 쉽다.(당연히 디렉터리는 HTML 파일로 구현된 것은 아니다. 하지만 개념을 이해할 때는 그냥 파일 목록을 담고 있는 hyper text라서 클릭하면 다른 파일로 점프 시켜주는 거라고 생각하면 이해가 빠르다.)

 

이 외에 특수하게 pipe라든지 socket, 혹은 device들도 전부 file의 종류다. 그러면 왜 file이라는 형태로 만들었을까? 그건 interface의 통일성 때문이다. OS론과 그 외 프로그래밍언어를 공부해보면 interface와 abstraction에 대한 직관을 얻을 수 있을 것이다.

 

심지어 우리가 터미널에서 명령을 내리는 명령행도 추상화되어 하나의 문서로 편집된다. 이 문서를 history라고 부른다. (실제로 파일도 있다) 따라서 bash에서 set -o vi 로 하면 이 명령행 문서(=history)를 편집할 때 vi를 사용하는 것이고, set -o emacs (기본값)으로 변경하면 emacs를 사용하게 되는 것이다. 이를 직관적으로 보여주는 것이 명령행을 에디터로 사용하는 것이다. 

 

예를 들어 bash 셸에서 EDITOR=vim 을 실행해서 기본 에디터를 vim으로 맞춰놓아보자. 그리고 CTRL-X CTRL-E 를 누르면 즉시 vim이 실행된다. 여기에서 ls -al 을 타이핑하고 :x 를 눌러서 저장하고 나오면 즉시 ls -al이 실행되는 것을 볼 수 있다. 즉 명령행 타이핑을 직접 에디터로 문서 편집하듯이 할 수 있는 것이다.

 

이렇듯 우리가 사용하는 POSIX 시스템은 파일의 추상적 개념이 어우러져 있다. 이 추상적 개념은 통일성(특히 named, anonymouse, data vs meta data)이 있는데 이를 눈여겨보게 되면 새로운 개념을 배울 때 좀 더 일관성 있게 정리할 수 있다.

 

3. 경로(path)

위에서 파일의 종류 중에 디렉터리를 이야기 했는데, 파일들이 유저의 눈에 보일려면 탐색기나 파일관리자에서 디렉터리로 이동해야만 한다. 즉 파일의 정확한 위치 정보는 directory + file name이 되겠다. 이것을 경로(path)라고 부른다.

Path를 좀더 고상하게 표현하면 file을 외부에서 액세스할 수 있는 접점(interface)이라고 할 수 있다. 이렇게 path가 존재하는 경우를 named file, path가 존재하지 않는 경우를 anonymous file이라고 부른다.

 

그럼 path가 존재하지 않는 anonymous file이 있을까? 외부에서 보이지 않는다면 anonymous file로 생각하면 된다. 가장 쉽게 찾으려면 last | sort 같은 명령을 치면 중간에 보이는 "|" 문자의 구현체가 바로 익명 파이프(anonymous pipe)라고 불리는 파일이다. last 명령의 실행 결과의 표준출력(stdout)이 sort의 표준입력(stdin)으로 전달될 때 anonymous pipe 파일을 통해 전달되는데 그 과정에서 anonymous pipe의 path는 존재하지 않는다.

 

익명 파이프

 

 

그럼 anonymous pipe말고 path가 존재하는 named pipe[각주:2]도 존재할까? 당연히 있다. named pipe로 last | sort와 같은 결과를 만들려면 mkfifo /tmp/myfifo; last > /tmp/myfifo &; sort /tmp/myfifo 으로 명령하면 된다. 이렇게하면 named pipe로 /tmp/myfifo를 만들어서 통신할 수 있다. 명령어 실행 후 ls -l /tmp/myfifo 해보면 file attribute에 pipe를 뜻하는 'p'가 표시되는 것을 볼 수 있다. Named pipe는 재사용도 가능하다.

 

이것보다 더 직관적으로 last > last.txt; sort last.txt 로 실행해서 last.txt라는 regular file을 생성해서 넘겨줘도 된다. 하지만 뭔가 수준 낮아보이기 때문에 이렇게 하는 사람은 거의 없다.

 

여기서 직관이 있는 사람들은 anonymous 접두어가 붙으면 임시적인 lifetime을 가지는 객체구나~하면서 무릎을 칠 수 있을 것이다. 반대로 named 접두어가 붙으면 persistency를 가지겠구나~하면서 무릎을 연타할 수 있을 것이다.

 

3.1. Anonymous vs Named

그리고 직관이 뛰어난 사람은 anonymous와 named의 접두어를 memory의 개념까지 확장 할 수 있게 된다.

원래 학교에서 배운 주기억 장치(main memory)는 RAM을 의미한다. 그리고 disk에 존재하는 file을 읽거나 쓰려면 memory에 사본이 로딩되어야 한다는 것을 배웠을 것이다. (관련되는 용어중에 locality, memory hierarchy, memory mapped I/O 등등 배울 때 다뤄졌을 것이다.)

 

그러면 process가 실행된다는 것은 디스크에 저장된 file의 path를 확인하고, 해당 file을 읽어 들이면 memory에 사본이 만들어지는 과정이 숨어있다는 것이다. 이 때 디스크에 사본이 존재하는 메모리를 named memory라고 부른다. 반대로 C언어의 malloc같은 기능, 혹은 C++의 new로 메모리를 할당하면 path가 존재하지 않는 heap memory에 할당되는데, 이것은 anonymous memory가 된다. 왜냐하면 디스크에 힙 메모리의 사본 파일이 존재하지 않기 때문이다.

 

실제로 Linux의 /proc/meminfo를 anonymous 영역이 있는데, 아래의 예시에서는 10348760 kB로 표시되고 있다.

 $ cat /proc/meminfo 
 MemTotal:       65881540 kB 
 MemFree:        23760816 kB 
 MemAvailable:   51603980 kB 
 Buffers:         1946552 kB 
 Cached:         25071184 kB 
 SwapCached:            0 kB 
 Active:         23043564 kB 
 Inactive:       14178812 kB 
 Active(anon):   10226884 kB 
 Inactive(anon):   121876 kB 
 Active(file):   12816680 kB 
 Inactive(file): 14056936 kB 
 Unevictable:          96 kB 
 Mlocked:              96 kB 
 SwapTotal:      33554428 kB 
 SwapFree:       33554428 kB 
 Dirty:               736 kB 
 Writeback:             0 kB 
 AnonPages:      10204852 kB 
 Mapped:          3262644 kB 
 Shmem:            144132 kB 
 Slab:            2089756 kB 
 SReclaimable:    1685968 kB 
 SUnreclaim:       403788 kB 
 KernelStack:       26448 kB 
 PageTables:       178036 kB 
 NFS_Unstable:          0 kB 
 Bounce:                0 kB

 

즉 시스템에서 사용하는 모든 데이터를 외부에서 액세스가 가능한지, 여기서 액세스가 가능한지는 바꿔말하면 해당 데이터(==file)의 경로의 유무로 구분지으면 anonymous, named로 분류될 수 있는 것이다. 그래서 /proc/meminfo를 봤을 때 anonymous 영역이 늘어나는 경우는 heap memory나 stack같은 것들이 증가했다는 것을 알 수 있게 해주는 것이다.

 

한 번 실행했던 프로그램이나 한 번 읽어들인 파일을 다시 읽을 때는 속도가 빨라지는 이유가 바로 file을 읽는 대신에 이미 메모리에 존재하는 사본인 named memory영역에서 가져오기 때문이다. 가끔 이걸 cache effect라고 부르기도 하는데, 사실상 cache effect는 여기서만 사용되는 기능은 아니므로 정확한 표현은 아니다.

 

그러면 간단 퀴즈를 풀어보자. Shared memory는 anonymous일까? 아니면 named일까? (찍어도 확률은 1/2이다)

심심풀이 땅콩으로 문제를 하나 더 풀어보자. 이거 은근히 틀리는 사람들이 많았던 문제다.
디스크의 파일을 읽는(reading) 작업과 쓰기(writing) 작업 중에 어떤 작업이 더 느린가? (다른 말로 둘 중 어떤 것이 latency를 더 유발하는 작업인가?) 이거 대부분 writing이 더 느리다고 하는 분들이 많은데, 일반적으로 reading쪽이 더 느리다. 물론 OS가 embedded 환경이라든지, 반복 작업, 혹은 작업의 개수, 용량이 얼마나 큰지에 따라 조금씩 답이 달라질 수 있지만 일반적으로는 reading이 더 느리다.

왜 그런지는 anonymous, named를 제대로 이해했다면 알 수 있을 것이다. (힌트 : writing은 실제 기록이 일어나지는 않으면서 일정 시간동안 dirty 상태를 유지할 수 있기 때문이다. => 풀어서 설명하면 writing은 디스크에 직접 기록하지 않고, 우선적으로 메모리에 기록하기 때문이다.)

 

또한 가상 메모리(virtual memory)로 개념을 확장하면 page in/page out, swap in/swap out까지도 영향을 받는다. 예를 들어 읽기 전용으로 사용되는 named memory는 page out은 되지만 swap out은 안되는 것이 바로 이런 개념 때문이다.


덤으로 dirty page의 개념도 named와 연관시켜서 알아두면 큰 그림을 보는데 도움이 될 수 있다. DB로 따지면 update SQL구문이나 random walking이 왜 더 큰 비용을 발생시키는지도 이해할 수 있게 된다. 예를 들어 50바이트 크기의 랜덤 데이터 10개를 update했다고 가정하자. 하필이면 10개의 데이터가 모두 서로 다른 page(4096바이트)에 존재한다면 총 40960 바이트가 dirty 상태가 된다. 즉 name memory에 대한 개념을 이해하지 못한 프로그래머라면 50B*10개, 500바이트의 입출력이 발생했다고 착각하지만 실제로는 그것보다 82배의 오버헤드가 생기게 된다.

 

이 개념을 network으로 확장하면 소켓(socket) 중에 named socket은 뭐가 있을지도 생각해보면 이해가 빨라진다. 그리고 소켓에서 bind 한다는 단어를 사용한 이유도, 이름을 붙여주는 행위라는 것을 이해할 수 있게 되고, 이는 소켓 프로그래밍도 파일의 연장선이라는 것을 이해할 수 있게 된다. 실제로 테스트를 해보면 10년이상 네트워크 프로그램을 작성했다는 프로그래머도 소켓의 bind가 named socket을 만들어주는 행위, 즉 외부에서 접속 가능한 경로의 실체가 만들어지는 것임을 이해를 못한 경우가 많았다.[각주:3]

 

IPC 기법 중에서 동기화에 많이 쓰이는 semaphore도 anonymous semaphore는 프로세스가 종료하면 같이 사라진다. 그래서 주로 스레드끼리 동기화할 때 사용된다. 여기서도 알 수 있듯이 anonymous semaphore는 임시의 수명을 가지고 있고, 외부에서 접근이 불가능한 속성을 가지는 것이다. 그에 비해 named semaphore는 프로세스가 종료해도 그 자체가 남아있어 나중에 재사용이 가능하다. 

 

4. Data, Meta data

앞에서는 path의 유무(有無)에 따른 분류인 anonymous, named 개념에 대해 이야기 했다. 그 결과 anoymous file, named file 및 memory와의 관계에 대해서 설명했는데, 이번에는 조금 다른 기준인 data, meta data에 대해 이야기 해보고자 한다.

 

예를 들어 디지털 카메라를 이용해서 "우주소녀"가 노래부르는 것을 녹화했다고 가정하자. (우주소녀 팬이라서 예시를 그렇게 들었다고 생각한다면... 맞았다. 특히 루다 팬이다.) 녹화된 영상의 파일명을 "우주소녀.mp4"로 저장했는데, 이 파일의 이름을 "BTS.mp4"로 변경했다면 영상의 내용이 방탄소년단(BTS)의 영상으로 바뀔까? 만일 바뀐다고 대답했다면 심각한 인지오류가 존재하는 것이므로 가까운 병원부터 가봐야 한다.

 

파일명을 바꾼다고 파일의 내용은 바뀌지 않는다는 것은 삼척동자도 안다. 그렇다면 파일명(file name)은 어디에 저장되는 것일까라는 의문이 생길 것이다. 의문이 생기지 않는다면 유투브에 가서 BTS영상이나 보자.

 

여기서 직관이 있는 사람들은 이마를 탁 치면서 파일의 내용(data)외에 파일명(file name)을 저장하는 공간이 어딘가 따로 존재하겠구나~라고 생각할 것이다. (그래서 인터넷에서 파일을 다운받으면 파일의 내용뿐만 아니라 파일명도 같이 전송된다. 그렇지 않고 내용만 전송하는 구조라면 수신측에서 적절한 파일명을 임시로 만들어서 저장하게 된다. 대표적인 것이 채팅 프로그램에서 파일을 전송받는 경우이다.)

 

실험을 위해 "touch 우주소녀.txt" 라고 명령을 내려 empty file을 생성해보자. "stat 우주소녀.txt"로 확인 해보면 access time (atime), modify file(mtime), change time(ctime)이 보이는데 처음에는 모두 동일한 시간으로 되어있다. 그러나 우주소녀.txt를 방탄소년단.txt로 이름을 변경하면 ctime만 바뀐다. 

만일 echo "I love BTS" > 방탄소년단.txt 명령을 실행하여 파일에 I love BTS란 내용을 추가한 뒤에 보면 mtime, ctime이 모두 변경된다. 즉 mtime은 data의 변경 시간을 보여주는 것이고, ctime은 meta data가 변경되었다는 것을 보여주는 것이다. 그런데 내용을 변경하면 왜 mtime, ctime이 다 변경될까? 제대로 이해를 했다면 즉시 답이 나올 것이다.

$ touch 우주소녀.txt 

$ stat 우주소녀.txt   
File: 우주소녀.txt   Size: 0               Blocks: 0          IO Block: 4096   
regular empty file Device: fd02h/64770d    Inode: 25956399    
Links: 1 Access: (0664/-rw-rw-r--)  
Uid: ( 1001/sunyzero)   Gid: ( 1001/sunyzero) 
Context: unconfined_u:object_r:user_home_t:s0 
Access: 2019-03-08 12:50:48.986567472 +0900 
Modify: 2019-03-08 12:50:48.986567472 +0900 
Change: 2019-03-08 12:50:48.986567472 +0900  
Birth: -  

$ mv 우주소녀.txt 방탄소년단.txt 

$ stat 방탄소년단.txt   
File: 방탄소년단.txt   Size: 0               Blocks: 0          IO Block: 4096   
regular empty file Device: fd02h/64770d    Inode: 25956399    
Links: 1 Access: (0664/-rw-rw-r--)  
Uid: ( 1001/sunyzero)   Gid: ( 1001/sunyzero) 
Context: unconfined_u:object_r:user_home_t:s0 
Access: 2019-03-08 12:50:48.986567472 +0900 
Modify: 2019-03-08 12:50:48.986567472 +0900 
Change: 2019-03-08 12:51:19.762699257 +0900  

$ echo "I love BTS" > 방탄소년단.txt 

$ stat 방탄소년단.txt   
File: 방탄소년단.txt   Size: 11              Blocks: 8          IO Block: 4096   
regular file Device: fd02h/64770d    Inode: 25956399    
Links: 1 Access: (0664/-rw-rw-r--)  
Uid: ( 1001/sunyzero)   Gid: ( 1001/sunyzero) 
Context: unconfined_u:object_r:user_home_t:s0 
Access: 2019-03-08 12:50:48.986567472 +0900 
Modify: 2019-03-08 12:54:48.641593693 +0900 
Change: 2019-03-08 12:54:48.641593693 +0900

 

참고로 위 부분이 좀 더 궁금하다면 i-node와 hard link, file hole등에 대해서 공부하길 권장한다. 이들에 대해 깊숙히 배우면 POSIX 파일 시스템의 구조와 컨셉에 대한 직관력이 생길 것이다. 특히 file hole을 만들기 위해 파일 생성 후 1바이트 기록 후, 커서를 1GB 뒤로 이동시킨 후 1바이트를 기록하면, 실제로는 겨우 2바이트가 기록되었지만(실제로는 2블럭 사용), meta data는 연속된 1GB의 공간을 점유하는 것으로 착각될 수 있다. 그래서 stat에서는 size와 blocks를 따로 보여주는 것이다. 이는 논리적 공간의 크기와 실제 공간의 크기가 다를 수도 있다는 것을 보여준다. 우리가 사용하는 메모리도 논리적 공간의 크기와 실제 크기는 다른 것처럼 말이다.

 

4.1. Process에서 data, meta data

그럼 이번에는 프로세스로 무대를 넓혀보자. 예를 들어 chrome web browser 아이콘을 클릭하면 크롬 브라우저가 실행될 것이다. chrome 실행했는데 firefox가 실행되는 일은 일어나지 않을 것이다.


즉 chrome 실행 파일이 가리키는 path의 파일을 실행시키면 항상 동일한 binary file과 부가적인 파일들을 로딩하게 된다. 물론 재실행일 때는 disk로부터 읽기는 생략되고 메모리에 있는 사본인 named memory로부터 가져오기 때문에 재실행일 때는 좀 더 빠르게 실행될 수 있다.


하지만 PID(Process ID)라든지 User ID, 실행 권한 같은 정보들은 실행할 때마다 매번 변경된다. 왜일까? 다시말해 프로세스의 data 부분은 동일하지만 meta data는 매번 변경된다는 뜻이다. 그렇다면 PID같은 프로세스의 메타 정보도 어딘가에는 존재한다는 뜻이다. 이 PID 같은 메타 정보가 저장되는 곳을 PCB(Process Control Block)라고 한다. 참고로 여기서 말하는 PCB는 녹색 기판의 PCB(Printed Circuit Board)를 말하는 것이 아니다.

 

 

결론적으로 모든 data는 그 데이터를 관리하기 위한 meta data가 어딘가에 존재해야만 한다. 그리고 이 둘은 별개로 취급된다. 이 관계를 잘 이해하면 zombie process가 왜 defunct process(현존하지 않는 프로세스)라고 불리는지도 이해할 수 있다. == 즉 zombie process란 meta 부분만 살아있는 상태인 것이다.

메타 정보는 오버헤드처럼 보이지만 관리를 위해 꼭 필요한 정보이기도 하다. 현실에서도 일꾼을 10명분의 일이 있다면 딱 10명이 필요한게 아니라 보통은 10명의 일꾼, 1명의 관리자가 필요한 것도 어찌보면 meta data를 관리해야 하는 오버헤드를 보여주는 것이다.

 

가끔 파일이 깨져서 복구를 하게되면 왜 파일명이 제대로 안나오고 무슨 일련번호 붙여서 나오는지도 이해할 수 있을 것이다.(파일이 깨졌다는 것은 data와 meta data가 일치하지 않는 것을 의미한다.)

그러면 돌발 퀴즈를 하나 풀어보자. 10테라 바이트의 정보를 저장할 때 파일 1개로 저장하는 것이 효율적일까? 아니면 1TB짜리 10개로 저장하는게 효율적일까? 그것도 아니면 10MB짜리 1백만개로 저장하는 것이 효율적일까?

 

5. 잡담

POSIX 시스템을 제대로 이해하려면 file이라는 추상적 개념을 이해하는게 정말 중요하다. 반대로 file에 대해 이해가 깊지 못하면 짠밥에서 우러나오는 통밥스킬(i.e. perception)이 생기지 않는다. 그래서 추상적 개념을 제대로 이해하는 프로그래머는 하나를 보면 최소한 둘 이상을 직관으로 깨닫게 되므로, 새로운 인터페이스나 기술이 나온다고 하더라도 통밥스킬에 의해 연관된 개념을 빠르게 습득 할 수 있다. 이는 마치 고전을 많이 읽어서 직관이 생긴 사람들은 다른 인문 서적이나 과학 서적을 볼 때 책이 담고 있는 메시지를 빠르게 간파할 수 있는 것과 비슷하다. (반대로 직관이 너~무 없으면 같은 책을 읽어도 잘못 이해해서 주화입마에 빠지곤 한다.)

 

뛰어난 프로그래머나 엔지니어가 되려면 직관은 필수다. 그리고 직관을 키우는데는 알고리즘으로 논리력을 키우는 것도 중요하지만, 그 알고리즘과 데이터구조가 왜 나왔는지 인과관계를 이해해야 한다.(특히 어떤 결핍을 해결하기 위해 신기술, 알고리즘이나 개념이 나왔는지를 알아야 한다.)  단순하게 알고리즘을 잘 풀어서는 나중에 왜 그 기술을 써야 하는지, 혹은 신기술이 어느 부분을 커버하는지 알 수 없는 단순 코딩 기계가 된다. 그래서 모든 기술의 역사를 아는 것이 중요하다.


수십년동안 대가들은 컴퓨터가 좀 더 빠르게 작동되도록 노력해왔는데, 그 노력의 정점에는 운영체제가 있다. 그래서 운영체제를 어떻게 구현했는지 이해하면 높은 경지로 올라갈때 많은 도움을 받을 수 있다. 또한 운영체제 친화적인 프로그래밍 기술은 아무나 할 수 있는게 아니다. (웃픈 건 인터넷에 나와있는 잘못된 기법을 사용해서 순정 상태보다 더 느리게 만드는 경우도 있는데, 이게 다 운영체제의 이해부족이다.)


만일 1천대의 서버를 운영하는 회사에서 운영체제, 네트워크를 제대로 이해해서 5%만이라도 서비스를 최적화 하면 회사는 대략 50대를 감축할 수 있다. 반대로 논리력은 있지만 직관이 없어서 20% 더 느린 시스템을 만들면 회사는 수백대의 서버를 더 증설해야 한다. 이렇게 되면 cost 상승을 가져오고, 경쟁사에 의해 망할 수도 있는 결과를 가져온다. 마치 반도체 회사에서 웨이퍼당 수율차이라고 생겨서 A사는 90% 양품을 만드는데, B사는 45%만 양품이라면 B사는 항상 2배의 원가 오버헤드가 발생할 수 밖에 없다. 결국 경쟁에서 지게될 가능성이 높다.

 

* TL;DR

File은 추상적인 interface이다. path의 유무에 따라 anonymous, named로 나뉜다. memory도 path를 가진 backed file의 유무에 따라 anonymous memory, named(file-backed) memory로 나뉠 수 있다.
File은 data, meta data가 따로 관리된다. 프로세스들도 data, meta data가 따로 있다. 프로세스의 meta data 부분은 가지고 있는 부분은 PCB이다.

이걸 잘 이해하면 character 능력치 중에 perception skill이 +1 된다. 고로 이 문서는 구양진경처럼 베이스 내공을 1칸 정도는 올려줄 것으로 예상된다.

 

2024-11-20 비문, 오타 수정

2020-07-31 문장 오타 수정 및 보완

2019-03-18 문장 오타 수정 및 보완

2019-03-08 블로그로 옮겨옴 (문장을 다듬고 예제를 넣음)

2019-01-03 페북에 대충 씀

  1. https://www.opengroup.org/austin/papers/posix_faq.html [본문으로]
  2. UNIX 계열 시스템에서는 named pipe를 FIFO라고 부른다. 이는 선입선출의 방식으로 작동하는 것에 기인한다. 따라서 선입선출 작동방식인 FIFO와 스펠링은 같지만 다른 의미로 쓰인다. [본문으로]
  3. bind가 성공하면 AF_UNIX 소켓의 경우 특정 디렉터리 경로에 socket file이 생성된다. AF_INET 소켓의 경우 listen 상태의 IP:PORT를 가지게 된다. 즉 socket의 DOMAIN이 파일 시스템인 AF_UNIX이면 FS영역에 접근 가능한 인터페이스인 socket file을 만들고, DOMAIN이 INET영역이면 TCP/IP에서 접근 가능한 주소가 생기는 것이다. [본문으로]

반응형
Comments