그림으로 배우는 Linux 구조 - 1 / 개요


Kernel

linux1_1

프로세스에서 저장 장치에 직접 접근할 경우 명령 실행 순서가 올바르게 제어되지 않으면 위험할 수 있습니다. 이런 문제 말고도 원래라면 접근이 불가능이어야 할 프로그램이 장치에 접근 가능한 문제도 생길 수 있습니다. 이런 문제를 해결하기 위해 커널은 하드웨어 도움을 받아 프로세스가 직접 접근할 수 없도록 합니다.

구체적으로는 CPU 내장된 모드 기능을 사용합니다. PC나 서버에서 사용하는 일반적인 CPU에는 커널 모드와 사용자 모드 두 종류의 모드가 있습니다.

프로세스가 사용자 모드로 실행되고 있으면 사용자 공간(userland)에서 프로세스를 실행한다라고 합니다.

CPU가 커널 모드라면 그 어떤 명령을 실행하는 데 아무런 제약이 없는 반면에, 사용자 모드로 실행 중이라면 특정한 명령을 실행하지 못하는 등의 제약이 걸립니다. 따라서 프로세스는 커널을 통해서 간접적으로 장치에 접근합니다.

linux1_2

커널은 커널 모드로 동작하면서 다른 프로세스에서는 불가능한 장치 제어, 시스템 자원 관리 및 배분 기능을 제공합니다. 이런 장치 제어뿐만 아니라 시스템 내부의 모든 프로세스가 공유하는 자원을 한 곳에서 관리하고 시스템에서 동작하는 프로세스에 배분할 목적으로 커널 모드로 동작하는 프로그램 입니다.


System Call

시스템 콜은 프로세스가 커널에 처리를 요청하는 방법입니다. 새로운 프로세스 생성이나 하드웨어 조작처럼 커널의 도움이 필요할 때 사용합니다.

  • 프로세스 생성, 삭제
  • 메모니 확보, 해제
  • 통신 처리
  • 파일 시스템 조작
  • 장치 조작

프로세스는 사용자 모드로 실행되지만 커널에 처리를 요청하기 위해 시스템 콜을 호출하면 CPU에서는 예외(Exception) 이벤트가 발생합니다. 이를 계기로 CPU 모드가 사용자 모드에서 커널 모드로 바뀌고 요청 내용에 따라 커널 처리가 동작합니다.

linux1_3

# 특정 프로세스를 특정 논리 코어에서 실행
# taskset -c <logical cpu num> <command>
$ sudo taskset -c 0 ./inf-loop.py &

# CPU 코어 0의 데이터를 1초마다 5번 수집하는 의미
$ sar -P 0 1 5
Linux 6.2.0-1012-aws (ip-10-20-15-218) 	12/05/2023 	_x86_64_	(4 CPU)
01:04:57 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
01:04:58 PM       0     91.09      0.00      0.00      0.00      8.91      0.00
01:04:59 PM       0     96.97      0.00      0.00      0.00      3.03      0.00
01:05:00 PM       0     88.12      0.00      0.00      0.00     11.88      0.00
01:05:01 PM       0     87.00      0.00      0.00      0.00     13.00      0.00
01:05:02 PM       0     82.00      0.00      0.00      0.00     18.00      0.00
Average:          0     89.02      0.00      0.00      0.00     10.98      0.00


$ taskset -c 0 ./systemcall-inf-loop.py  &

# CPU 코어 0의 데이터를 3초마다 5번 수집하는 의미
$ sar -P 0 3 5
Linux 6.2.0-1012-aws (ip-10-20-15-218) 	12/15/2023 	_x86_64_	(4 CPU)

05:54:29 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
05:54:32 AM       0     60.33      0.00     21.33      0.00     18.33      0.00
05:54:35 AM       0     71.10      0.00     24.92      0.00      3.99      0.00
05:54:38 AM       0     65.89      0.00     23.75      0.00     10.37      0.00
05:54:41 AM       0     64.45      0.00     20.60      0.00     14.95      0.00
05:54:44 AM       0     69.33      0.00     20.00      0.00     10.67      0.00
Average:          0     66.22      0.00     22.12      0.00     11.66      0.00
ubuntu@ip-10-20-15-218:~/workspace/ch01$

%user + %nice = 사용자 모드에서 프로세스를 실행하는 시간 %system = 커널이 시스템 콜을 처리한 시간 비율 %idle = 아무것도 하지 않는 아이들 상태 비율


Library

여러 프로그래밍 언어에는 다수의 프로그램에서 공통으로 사용하는 처리를 라이브러리로 합쳐서 제공하는 기능이 있습니다. 라이브러리 중에는 OS가 미리 공통된 기능을 가진 라이브러리를 준비해서 제공하는 경우도 있습니다.

linux1_6

C 언어는 ISO(International Organization for Standardization)에서 정한 표준 라이브러리가 존재합니다. 리눅스에도 이런 표준 C라이브러리가 제공됩니다. 일반적으로 GNU 프로젝트에서 제공하는 glibc(libc) 를 표준 C라이브러로 사용합니다. C 언어로 작성된 대부분의 프로그램은 libc 를 링크합니다.

프로그램이 어떤 라이브러리를 링크하는지 알아보려면 ldd 명령어를 사용해서 확인합니다.

$ ldd /bin/echo
	linux-vdso.so.1 (0x00007ffdcc39b000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fae38000000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fae383d6000)


$ ldd /usr/bin/python3
	linux-vdso.so.1 (0x00007fffb9ffa000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2af4886000)
	libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f2af4855000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f2af4839000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2af3e00000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f2af4975000)

libc.so.6는 표준 C라이브러리를 뜻합니다. 그리고 ld-linux-x86-64.so.2는 공유 라이브러리를 로드하는 특별한 라이브러리 입니다. C 언어를 직접 사용하는 일이 드물어졌다고 하지만 OS 수준에서는 안 보이는 곳에서 중요한 언어로 사용됩니다.

libc는 표준 C 라이브러리뿐만 아니라 System call wrapper 함수도 제공합니다. 시스템 콜은 일반 함수 호출과 다르게 C 언어 같은 고급 언어에서 직접 호출할 수 없습니다. 아키텍처에 의존하는 어셈블리 코드를 사용해서 호출해야 합니다.

# x86_64
mov  $0x6e, %eas
syscall


# arm64
mov  x8, <Num of system call>
svc #0

linux1_8
이런 문제점을 해결하기 위해 libc는 내부적으로 시스템 콜을 호출할 뿐인 시스템 콜 래퍼 함수를 제공하며 래퍼 함수는 아키텍처별로 존재합니다.

라이브러리는 Static library와 Shared library 두 종류로 분류할 수 있습니다. 모두 같은 기능을 제공하지만 프로그램과 결합하는 방식이 다릅니다. 정적 라이브러리는 링크할 때 라이브러리에 있는 함수를 프로그램에 집어 넣습니다. 반면에 공유 라이브러리는 링크 할 때 ‘이 라이브러리의 이런 함수를 호출한다’라는 정보만 실행 파일에 포함합니다. linux1_9





Reference

  • 그림으로 배우는 리눅스 구조 (다케우치 사토루) https://books.google.co.kr/books?id=7djUEAAAQBAJ&pg=PA35&hl=ko&source=gbs_selected_pages&cad=1#v=onepage&q&f=false —

Tag: [ book  linux  system  ]