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)라고 불리는 것은 파일 시스템의 추상화된 공간을 맵핑하는 것이기 때문에 파일 시스템에서 쓰이는 용어와 개념이 그대로 쓰이기 때문이다.

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

 

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

 

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

 

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 로 하면 이 명령행 문서를 편집할 때 vi를 사용하는 것이고, set -o emacs (기본값)으로 변경하면 emacs를 사용하게 되는 것이다. 이를 직관적으로 보여주는 것이 명령행을 에디터로 사용하는 것이다. 

 

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

 

이렇듯 우리가 사용하는 POSIX 시스템은 대부분이 파일의 추상적 개념이 어우러져 있다. 그것의 통일성을 눈여겨보게 되면 새로운 개념을 배울 때 좀 더 일관성 있게 정리할 수 있다.

 

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'가 표시되는 것을 볼 수 있다. 

이것보다 더 직관적으로 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 상태를 유지할 수 있기 때문이다.)

 

또한 가상 메모리(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]

 

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 파일 시스템의 구조와 컨셉에 대해 좀 더 깊이 알 수 있다.

 

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같은 정보가 저장되는 프로세스의 메타 정보가 어딘가에 존재한다는 뜻이다. 이 부분이 PCB(Process Control Block)에 존재한다. 참고로 여기서 말하는 PCB는 녹색 기판의 PCB(Printed Circuit Board)를 말하는 것이 아니다.

 

 

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

 

덧붙여서 깨진 파일을 복구하면 왜 파일명이 제대로 안나오고 무슨 일련번호 붙여서 나오는지도 이해할 수 있을 것이다. 그러면 돌발 퀴즈를 하나 해보자. 10테라 바이트의 정보를 저장할 때 파일 1개로 저장하는 것이 효율적일까? 아니면 1TB짜리 10개로 저장하는게 효율적일까? 그것도 아니면 10MB짜리 1백만개로 저장하는 것이 효율적일까?

 

5. 잡담

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

 

뛰어난 프로그래머나 엔지니어가 되려면 직관은 필수다. 그리고 직관을 키우는데는 알고리즘으로 논리력을 키우는 것도 중요하지만, 그 알고리즘과 데이터구조가 왜 나왔는지 인과관계를 이해해야 한다. 그래서 역사가 중요한 법이다.


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


만일 1천대의 서버를 운영하는 회사에서 운영체제, 네트워크를 제대로 이해해서 5%만이라도 최적화된 서비스를 만들면 회사는 대략 50대를 감축할 수 있다. 반대로 논리력은 있지만 직관이 없어서 겨우 20% 더 느린 시스템을 만들면 회사는 수백대의 서버를 더 확보해야 한다. 이것은 cost 상승을 가져오고, 경쟁사에 의해 망할 수도 있는 결과를 가져온다.

 

* 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칸 정도는 올려줄 것으로 예상된다.

 

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에서 접근 가능한 주소가 생기는 것이다. [본문으로]

16 Comments
  • 프로필사진 좋은 글 2019.03.13 11:06 좋은 글 감사합니다. 회사에서 강의 들은 이후 관련 글 전부 정독하고 있습니다. 볼 때마다 강사님의 정통파 무공에 놀라게 됩니다.
  • 프로필사진 sunyzero 2019.03.13 18:07 신고 감사합니다. 제 강의를 들으셨던 분이군요. 앞으로도 좋은 글 올리도록 노력하겠습니다. ^^
  • 프로필사진 Beyond the infinity 2019.10.19 19:53 신고 좋은 글 잘 보고 갑니다.
    2번정도 읽었는데 잘 이해가 안 되는 부분이 있습니다.
    일반적으로 read 가 write 보다 latency 를 더 유발하다고 하셨는데, 어떤 원리로 그런건지 알고 싶습니다.
  • 프로필사진 sunyzero 2019.10.21 16:09 신고 write작업은 dirty상태를 유지하는 OS기능으로 인해 일정 횟수는 생략될 수 있기 때문입니다.
    만일 write작업을 A->B->C->D->E의 순서대로 했다고 가정합니다. 그리고 E상태에 올 때 grace period가 끝났다고 가정한다면, A,B,C,D의 변경은 RAM에만 dirty상태로 존재했고, 실제로는 디스크 기록이 생략될 수 있습니다. 따라서 디스크 접근 횟수가 줄어들어서 latency의 상당부분이 생략되는 효과가 있습니다.

    하지만 read작업은 뭘 하든간에 처음에 읽어들일때는 디스크로부터 읽어야 하므로 이 부분은 피할 수 없는 latency입니다. 물론 read도 다시 반복해서 읽는 경우에는 page cache에서 읽기 때문에 latency가 적지만 처음에 읽는 것이라면 이것을 줄이는 방법은 별로 없습니다.(초기 읽기라면 readahead를 사용해도 latency를 숨길뿐 생략할 수는 없습니다.)
  • 프로필사진 mhkim 2020.12.13 18:29 좋은 글 감사합니다. anonymous memory가 뭔지 잘 몰랐었는데, 이 글을 읽고 도움이 많이 되었습니다. 그런데 shared memory가 anonymous인지 named인지 퀴즈 답이 궁금합니다. 제 생각에는 사용자가 결정할 수 있을 것 같은데 맞나요??
  • 프로필사진 sunyzero 2020.12.29 01:06 신고 shm은 anonymous입니다. file system에 path가 존재하는 경우만 file-backed입니다.
  • 프로필사진 lynn312 2020.12.23 13:48 좋은 글 감사합니다. 그런데 궁금한점이 있는데 non regular file 이 항상 anonoymous file 인건가요 아닌건가요? 제가 시스템 프로그래밍 책을 읽고 있는데 가상 메모리는 regular file , 아니면 anonymous file 에 대응이 된다고 나와있어서요. 그리고 또 궁금한것이 그러면 heap memory 에 할당이 되는 애들은 실제 디스크에 저장된것이 아니라고 하셨는데 그러면, 접근하는 속도가 더 느린 건가요? 또한 heap memory 에 할당이 되는 anonymous file 들은 항상 접근할 때마다 계속 heap 에 할당을 해줘야 되는건가요? ( 즉 일반 regular file 들은 처음에만 느리고 다시 읽을때는 빠른데 anonymous file 은 그렇지 않은건지가 궁금합니다. 그 이유도 궁금합니다.(
  • 프로필사진 sunyzero 2020.12.29 01:19 신고 regular file과 anonymous file은 서로 비교 가능한 성질이 아닙니다.

    heap은 anonymous memory영역입니다. 당연히 disk보다 빠르죠.

    heap에 접근할 때마다 할당을 해줘야 한다는 것은 무슨 이야기인지 모르겠습니다. 아마도 혼동을 겪는 이유는 commit단계와 page fault를 완벽히 이해하지 못해서 그런듯 싶습니다. commit과 page fault에 대한 부분을 보시고, major page fault, minor page fault에 대해서도 찾아보시기 바랍니다.

    메모리에 로딩된 이후에 anonymous,named의 영역은 둘 다 어차피 물리적 메모리이므로 이론적 속도는 같습니다만, 실제로 운영체제가 이들을 관리하는 기법에 따라서 달라집니다. 다만 몇몇 운영체제는 anonymous 영역에 좀 더 우선순위를 두는 경우가 많습니다.
  • 프로필사진 dd 2021.01.05 14:51 예를 들어 읽기 전용으로 사용되는 named memory는 page out은 되지만 swap out은 안되는 것이 바로 이런 개념 때문이다. 이 부분에 대해 질문이 있는데요, swap out 안 되는 이유가 혹시 뭔지 설명해 주 실 수 있으신가요? swap out 이 메모리에서 swap 메모리 영역으로 이동하는 건 알고 있고 swap 이 disk 에 저장된 메모리들 중에서 오랫동안 쓰지 않는 데이터들을 모아둔다는 것은 알고 있습니다. 이해가 될만한 키워드나 자료를 추천해주시면 감사하겠습니다.
  • 프로필사진 익명 2021.01.05 17:06 비밀댓글입니다
  • 프로필사진 익명 2021.01.05 17:08 비밀댓글입니다
  • 프로필사진 sunyzero 2021.01.09 22:37 신고 named memory는 file-backed memory이므로 말 자체가 가진 의미 그대로의 성질을 가집니다. 즉 named memory는 이미 원본이 되는 파일이 디스크에 있는데, 굳이 swap out시켜서 동일한 사본을 1개 더 만들 이유가 없지요.
  • 프로필사진 핑크빛엔젤 2021.02.08 21:16 신고 안녕하세요. 글 정말 잘 읽었습니다. 다름이 아니라 글을 읽다보니, 내용 중에 디렉토리와 파일 모두 공통인터페이스를 갖고, 디렉토리는 디렉토리 혹은 파일이라는 자식을 가질 수 있는 구조가 디자인 패턴 중에 composite 패턴이 적용된 예시 같은데 맞나요? 감사합니다.
  • 프로필사진 sunyzero 2021.02.09 07:15 신고 아닙니다.
  • 프로필사진 핑크빛엔젤 2021.02.11 23:39 신고 앗 그렇군요.. 혹시 왜 아닌지 설명해주실수 있으신가요? 아니면 관련 키워드라도..감사합니다.
  • 프로필사진 sunyzero 2021.02.22 00:42 신고 대장금 : "홍시맛이 나서 홍시라 생각한 것이온데..."

    그럼 홍시맛이 나는 이유에 대해서 조금 더 설명해드리겠습니다.

    1. 디렉터리에 대한 잘못된 이해
    디렉터리는 목록을 가지고 있는 것이지, 그 자체가 자식을 가지는 방식이 아닙니다. 이에 대해서는 파일처리론과 운영체제론, 컴퓨터 시스템 과목에서 다루게 됩니다.

    디렉터리에 대해서 많은 사람들이 혼동하는 것은 directory entry의 구조를 잘 모르기 때문인 경우가 많습니다. 그래서 디렉터리가 뭔가를 포함한다고 착각할 수 있는 것이죠. 디렉터리는 포함하는게 아닙니다. 위치 정보를 주는 것이죠.
    (참고로 디렉터리 서비스라고 불리는 네트워크 서비스에 대해서 한번 보시는게 좋다고 봅니다. 디렉터리라는 용어를 경직된 관념으로 이해하여 디렉터리는 파일이라고 생각하면 사고의 넓이를 제한받게 됩니다)

    2. 모든 사물을 객체지향으로만 바라보는 경우의 문제
    보통 객체지향과 객체지향설계 및 패턴에 대해 배우기 시작하면 모든 것들을 객체의 형태로 바라보는 경우가 생깁니다. 즉 객체지향이라는 필터가 사고에 장착되어 모든 지식들이 객체지향이라는 필터에 맞게 해석되는 것이죠.

    하지만 의외로 컴퓨팅 시스템에서는 그렇지 않은 경우도 많기 때문에 경직된 필터로만 사고하면 제대로 이해하지 못하는 경우도 생깁니다.

    즉 특정 도메인 지식에 의해 경직된 사고를 하는 것보다는 좀 더 열린 사고를 하는 것이 좋습니다. 이론적 개념이나 구현기술은 꼭 1가지가 아닐 수도 있거든요.
댓글쓰기 폼