https://www.yes24.com/Product/Goods/122109062
그림으로 배우는 리눅스 구조 - 예스24
선배가 옆에서 하나하나 알려주듯 친절히 설명해주는실습과 그림으로 배우는 리눅스 지식의 모든 것 * Go 언어와 Python, Bash 스크립트 실습 코드 제공* 이 도서는 『실습과 그림으로 배우는 리눅
www.yes24.com
1. 프로그램과 프로세스
OS를 처음으로 접하는 학부생들에게는 어려운 질문이다. 프로그램과 프로세스의 차이는 무엇일까? 저자의 설명은 다음과 같다.
- 프로그램: 컴퓨터에서 동작하는 관련된 명령 및 데이터를 하나로 묶은 것.
- Go, C/C++, Rust 는 컴파일러(compiler) 언어 : 소스 코드를 빌드해서 만들어진 executable file을 프로그램이라고 부름.
- Javascript, Python, Matlab 은 Interpreter(Script) 언어 : 컴파일 하지 않고 소스 코드 자체가 프로그램이 됨
- 프로세스: 실행되어서 동작중인 프로그램
저자가 간단하게 표현했지만, 좀 더 깊게 들어가보자. 프로그램은 Set of Instructions(명령어의 집합)이라고도 불린다. 웹 브라우저, 텍스트 에디터, shell, 게임 등 다양한 프로그램은 결국 명령어의 집합에 불과하다.
그렇다면 이 단순한 명령어들을 "어떻게" 실행한다는 걸까?
그림을 보며 알 수 있듯, CPU가 프로그램을 Disk에서 메모리로 올리는, 즉 load하는 과정이 필요하고, 메모리에 load한 이후 명령어 한 줄씩 CPU가 실행하면서 프로세스가 발생한다. 즉 프로세스란, "메모리에 올려지고, CPU가 접근할 수 있는 상태인 프로그램" 이라고 생각할 수 있다. 방금 문장에는 많은 비약이 있지만, 이 정도만 이해하고 넘어가자.
2. 커널
앞서 본 프로세스에 대한 설명은 커널을 이해하는데 도움이 된다. 예를 들어, zsh shell에서 ps 명령을 입력하고 결과를 확인해보자.
ps 명령어는 프로세스 상태를 보여주는 명령어로, 현재 터미널 상에서 실행중인 프로세스들을 보여준다. 그런데 이 결과는 놀라운 점이, 단순히 ps를 입력했을 뿐인데 ps에 해당하는 프로그램이 실행되어 프로세스가 생성된 것이다. 어떻게 이러한 과정이 발생한 것일까? ps 프로그램의 위치를 whereis를 이용해 검색하면 다음과 같다.
따라서 이는, zsh에서 "누군가"에게 ps 프로그램의 경로를 보내서 실행해달라고 "요청" 한 것이라고 보여진다. 여기에서 "누군가"에 해당하는 것이 바로 커널(kernel)이다. 그럼 이 ps 프로세스의 실행 과정을 단계별로 알아보자.
- zsh는 사용자 명령어(ps)를 해석하고, 커널에 '/usr/bin/ps'에 있는 파일을 실행하도록 요청한다.
- 커널은 zsh의 요청을 받아 ps 프로세스를 생성한다. 이때 ps 프로세스에 필요한 메모리, CPU Time을 알아서 할당해준다.
이처럼 커널은 User와 Device를 연결해주어서 프로세스를 관리해주는 역할을 한다. 그렇다면 당연한 질문이 있는데, User가 직접 Device에 접근하는게 더 빠르지 않을까? 이다. 그러나 User가, 정확히 말하면 프로세스가 직접 장치에 접근한다면 다음과 같은 문제점이 발생한다.
- 동시에 여러 프로세스가 같은 메모리에 접근했을 때 명령 실행 순서를 올바르게 제어하지 못하는 문제
- 접근 불가능이어야 할 프로그램이 장치에 접근할 수 있음.
이 문제를 해결하기 위해 커널은 하드웨어 도움을 받아 프로세스가 장치에 직접 접근할 수 없도록 하고, 구체적으로는 Kernel Mode와 User Mode로 CPU가 다르게 동작한다. 예를 들어, 프로세스가 User Mode로 실행되고 있으면, 사용자 공간에서 프로세스를 실행한다고 한다. CPU가 커널 모드라면, User Mode에서 실행하지 못하는 모든 명령을 사용할 수 있다. 따라서, 모든 프로세스는 커널을 통해서 간접적으로 장치에 접근한다. 그림으로 표시하면 다음과 같다.
3. 시스템 콜
시스템 콜(System Call)은 프로세스가 커널에 처리를 요청하는 방법이다. 하지만 주의할 점은, 커널에서 처리를 하기 위해서는 CPU가 커널 모드로 전환되어야 한다. 그림으로 보면 다음과 같다.
프로세스는 User Mode에서 실행되는데, 커널에 요청을 보내기 위해서 system call을 사용하고, 이 때 CPU에서는 exception이라는 이벤트가 발생한다. exception, interrupt, trap에 대해서는 차후에 정리를 해보겠다. 일단은 exception이 발생한다고만 알아 두면 된다. 중요한 건, exception 이후 Kernel mode로 바뀌고, 여기에서 커널 처리가 동작 후 시스템 콜 처리가 끝나면, 아무 일 없었다는 듯 조용히 User mode로 돌아와서 프로세스 동작이 이어진다. 기억해야 할 점은, 시스템 콜을 통해서만 프로세스에서 직접 CPU 모드를 변경할 수 있다는 것이다. 이 과정을 실습해보자.
(1) 시스템 콜 호출 확인
package main
import (
"fmt"
)
func main() {
fmt.Println("hello world")
}
단순히 hello world를 출력하는 go 파일이다. go build hello.go 명령으로 컴파일 하고, 이를 shell에서 실행하면서, strace를 사용해서 이 프로그램이 호출하는 system call을 볼 수 있다. 한 줄에 system call이 하나씩 표기되므로, wc -l 명령을 이용해서 system call의 개수를 측정해 보자.
hello.log의 마지막 문장이 +++ exited with 0 +++ 이므로, 총 197개의 system call이 발생한 것을 확인할 수 있다. 실제로 cat hello.log을 실행하면,
위와 같이 write()라는 system call이 발생하고, 이를 kernel이 받아서 hello world\n 문자열을 stdout, 즉 terminal에 표시해준다.
(2) 시스템 콜 처리하는 시간 비율 실습
sar 명령을 이용해서 현재 CPU가 실행하고 있는 명령 비율을 알아보자. sar -P 0 1 1 명령어로 CPU 코어 0이 어떤 종류의 처리를 하고 있는지 알 수 있다. -P 0 은 CPU 0의 데이터를 수집한다는 뜻이고, 1 1에서 앞의 1은 1초마다 데이터를 수집한다는 의미이고, 뒤의 1은 한 번만 데이터를 수집한다는 의미이다.
아무 작업을 하지 않았으므로 idle에 대부분의 시간이 소요됨을 알 수 있다. 여기에서 user + nice 가 User Mode이고, system이 Kernel mode라고 이해하면 된다. user와 nice의 차이는 차후에 설명한다.
그러면 구체적으로 확인하기 위해 다음 무한 루프 파이썬 파일을 실행하면서 측정해보자.
while True:
pass
보이는 것과 같이 user+nice 인 User mode에 거의 모든 영역이 할당됨을 알 수 있다. 왜 nice로 할당이 되는지는 차후에 공부할 것이다. 무한 루프 프로세스는 다음 명령으로 kill해주면 된다.
그렇다면 이번에는 다음 파이썬 코드로 system call을 무한 요청해보자.
import os
while True:
os.getpid()
이번에는 system call 호출이 무한으로 일어나서 %system이 많아졌다. 하지만 user mode도 70%가까이 되는데, 이는 while문 을 무한으로 실행해야 하기 때문이다. 그림으로 표현하면 다음과 같다.
'Linux > 그림으로 배우는 리눅스 구조' 카테고리의 다른 글
[그림으로 배우는 리눅스 구조] 5. 프로세스 관리(응용편) (0) | 2024.08.29 |
---|---|
[그림으로 배우는 리눅스 구조] 4. 메모리 관리 시스템(2) (0) | 2024.08.29 |
[그림으로 배우는 리눅스 구조] 4. 메모리 관리 시스템(1) (0) | 2024.08.29 |
[그림으로 배우는 리눅스 구조] 2. 프로세스 관리(기초편) (0) | 2024.08.27 |
[그림으로 배우는 리눅스 구조] 0. 환경 설정 (0) | 2024.08.27 |