2021년 05월 29일 15:00 ~ 22:00 스터디 진행 내용을 정리한 글입니다.
53 명
서기: 김현우님, 김성원님
공유: 이민욱님
매주 스터디 내용은 iamroot
사이트에 정리되어 올라갈 예정
리눅스 커널 내부구조 (백승재, 최종무 저) 54pg.
Operating System
?운영체제(Operating System
) 란... 쉽게 말해 자원 관리자(Resource Manager
).
그럼 운영체제에게 있어 자원(Resources
)은 무엇일까? 크게 아래의 두 가지로 나뉜다:
Physical
) 자원CPU
, Memory
, Disk
등이 있다.Abstraction
) 자원CPU
를 추상화 시킨 Task
, Memory
를 추상화 시킨 Segment
와 Page
, Disk
를 추상화 시킨 File
등이 있다. 운영체제가 CPU
, Memory
, Disk
라는 세 개의 물리적 자원을 가지고 있다 해보자.
우리가 edit
이라는 프로그램을 이용해 간단한 C
코드 (test.c
) 를 작성했다. 우리는 이 코드를 영속적으로 저장하고 싶다. 그래서 운영체제에게 해당 코드를 영속적으로 저장하라는 명령을 내렸다.
운영체제는 이 test.c
라는 코드를 File
이라는 추상적 자원으로 취급하여, Disk
라는 물리적 자원에 기록한다. Disk
는 비휘발성 메모리이기 때문에 데이터가 영속적으로 저장 되어진다. File
이라는 추상적 자원을 통해 사용자는 코드를 더 쉽게 관리 가능하다.
디스크는 최소 단위인 섹터(일반적으로 512 Bytes
) 로 그 내용을 기록하며, 운영체제는 다시 섹터를 묶어 다시 Block
이라는 단위(일반적으로 4KiB
) 로 데이터를 다룬다. 근데 왜 512 Bytes
이며 4 KiB
일까? 이는 성능적인 요인이 주를 이루지만 역사적인 이유(오랜 시간 사용되어 굳어진, Sementic
한) 도 존재한다.
test.c
파일을 컴파일하면 a.out
이라는 바이너리 데이터가 생성되고, 이 역시 파일로 취급되며 디스크에 쓰여진다.
운영체제에게 a.out
이라는 이름의 바이너리 파일을 실행하라는 명령을 내리면 그 결과 task
라는 새로운 추상적 자원(객체 혹은 object
)가 생성된다. 이 객체(task
)는 수행 중인 프로그램으로 정의되며 각 태스크들은 서로 경쟁하며 CPU
를 사용하기 위해 노력한다.
어떤 task
를 실행해야 할지 결정하는 결정하는 스케쥴링 정책은 다양하지만 초기 커널에서는 라운드 로빈 기법(RR
정책)을 사용했다. 비율 단조 정책(RM 정책
) 도 존재하는데 이는 수행 주기가 가장 짧은 프로세스에게 가장 높은 우선순위를 부여하는 방식이다. 다만 각 task
에 대한 priority
를 직접 설정해서 동작시켜야 하기 때문에 일반적인 OS
에서는 사용하지 않고, 적시성이 중요한 RTOS
에서 사용하는 정책이다.
또한 바이너리 파일(a.out
)이 task
로 수행되기 위해서는 파일을 구성하고 있는 데이터가 Memory
로 적재되어야 한다. 이를 위해 운영체제는 메모리 역시 관리해야 한다. 앞서 말한 것처럼 메모리는 Segment
와 Page
라는 추상적 자원으로 그 내용을 관리한다.
Signal
과 inode
Signal
) 리눅스는 다양한 시그널을 지원하며 시그널 목록은 kill -l
명령어를 입력하여 확인 가능하다. 시그널은 다양하고 시그널에 대한 기본적인 행동은 운영체제가 정한다. 일반적인 시그널에 대한 반응은 종료이다. 이러한 행동 대신 본인이 원하는 행동을 수행하고 싶다면 시그널 핸들러를 설치해야 한다. 다만 일부 시그널에 대해서는 핸들러를 설치할 수 없다.
super block
과 inode
inode
란 전통적인 유닉스 계통 파일 시스템에서 사용하는 자료 구조로 정규 파일, 디렉터리 등의 관한 파일 시스템 정보를 가진다. 파일들은 각자 1개의 아이노드를 가지며, 아이노드는 소유자 그룹, 접급 모드, 파일 형태, 아이노드 숫자 등 해당 파일에 관한 정보를 가진다.
superblock
은 각 블록의 크기, 전체 블록 수, 블록 그룹 당 블록 개수, 첫 블록 그룹보다 전에 예약된 블록의 수와 같은 기본정보가 담겨 있다. 또한 inode
전체 개수와 블록 그룹당 inode
개수를 포함한다.
위 사진은 elixir
사이트에서 확인한 5.12.8 버전의 커널 파일 디렉터리이다. 굉장히 다양한 디렉터리가 있는데 그 내용을 모두 다 설명할 순 없고 맥락적으로 큰 범주로 묶어 설명하겠다.
kernel
디렉터리 태스크의 생성과 소멸, 프로그램의 실행, 스케줄링, 시그널 처리 등의 기능이 이 디렉터리에 구현되어 있다.
한편 문맥 교환(Context Switch
) 와 같이 하드웨어의 종속적인 부분은 arch/$(ARCH)/kernel
디렉터리에 구현되어 있다. 여기에서 $(ARCH)
는 각 아키텍처의 플레이스홀더(placeholder
)이다.
arch
디렉터리 리눅스 커널 기능 중 하드웨어의 종속적인 부분들이 구현된 디렉터리이다. 아키텍처에 따라 다시 하위 디렉터리로 구성된다. arch/$(ARCH)/boot
는 각 아키텍처 부팅 시 사용되는 부트스트랩 코드가 구현되어 있다. arch/$(ARCH)/kernel
에는 태스크 관리자 중에서 문맥 교환이나 쓰레드 관리 같은(아키텍처의 종속적인) 기능, arch/$(ARCH)/mm
에는 메모리 관리자 중에서 페이지 부재 결함 처리 같은 하드웨어 종속적인 기능이 구현되어 있다. arch/$(ARCH)/lib
에는 커널이 사용하는 라이브러리 함수가, arch/$(ARCH)/math-emu
에는 FPU (Floating Point Unit)
에 대한 에뮬레이터가 구현되어 있다.
fs
디렉터리 리눅스에서 지원하는 다양한 파일 시스템과 관련 시스템 호출의 구현이 들어있는 디렉터리이다. 대표적으로 ext2
, ext3
, ext4
, nfs
, fat
, proc
, sysfs
, devfs
, isofs
, reiserfs
, f2fs
, xfs
등이 있으며 이런 다양한 파일 시스텡믈 사용자가 일관된 인터페이스로 접근할 수 있도록 도와주는 가상 파일 시스템(virtual file system
) 의 관련된 내용도 여기에 들어있다.
driver
디렉터리리눅스에서 지원하는 디바이스 드라이버가 구현된 디렉터리이다. 디바이스 드라이버는 디스크, 터미널, 네트워크 카드 등 주변 장치를 추상화하여 관리하는 커널 구성 요소이다.
net
디렉터리이 디렉터리는 리눅스가 지원하는 통신 프로토콜이 구현된 디렉터리이다. 소켓(socket
) 역시 이 디렉터리에 구현되어 있다.
ipc
디렉터리리눅스 커널이 지원하는 프로세스간 통신 기능이 구현된 디렉터리이다. 다만 파이프는 fs
, 시그널은 kernel
, 소켓은 net
디렉터리에 구현되어 있다.
init
디렉터리커널 초기화 부분, 즉 커널의 메인 시작 함수가 구현된 디렉터리이다. 하드웨어 종속적인 초기화가 이뤄진 후에 이 디렉터리에 구현되어 있는 start_kernel()
이라는 C 함수로 제어가 넘어온다.
include
디렉터리 리눅스 커널이 사용하는 헤더 파일들이 구현된 디렉터리이다. 헤더 파일 중에서 하드웨어에 독립적인 부분은 include/linux
하위 디렉터리에 정의되어 있으며 하드웨어에 종속적인 부분은 include/asm-$(ARCH)
하위 디렉터리에 정의되어 있다.
리눅스 커널 및 명령어들에 대한 자세한 문서 파일들이 존재하는 Documentation
디렉터리, 커널 라이브러리 함수들이 구현된 lib
디렉터리, 커널 구성 및 컴파일 시 이용되는 스크립트 들이 존재하는 scripts
디렉터리 등이 존재한다.
[책] 리눅스 커널 내부구조 (백승재, 최종무 저)
[사이트] https://ko.wikipedia.org/wiki/아이노드
[사이트] https://elixir.bootlin.com
[사이트] http://www.iamroot.org/xe/index.php?mid=Note&document_srl=215988