사용자 모드로 구현하는 기능1

김민세·2022년 11월 23일
0

리눅스 구조

목록 보기
3/6
post-thumbnail

OS는 커널 이외에도 사용자 모드에서 동작하는 다양한 프로그램으로 구성되어 있다. 이러한 프로그램들은 library 형태인 것도 있고 단독 프로그램으로 동작하는 것도 있다.

프로세스와 OS의 관계

위 그림은 기본적으로 사용자 모드의 프로세스 처리부터 시스템 콜을 통한 커널 처리를 호출하는 방식이다. 이러한 호출을 하는 것은 프로그램 고유의 코드인 경우도 있고, 그 코드가 OS에서 제공하는 library와 그렇지 않은 library로 나누어질 수 있다.

시스템 전체를 들여다보면 시스템에는 애플리케이션이나 미들웨어뿐만 아니라 OS가 제공하는 프로그램도 여러 가지 있다.


시스템 콜

프로세스는 프로세스의 생성이나 하드웨어의 조작 등 커널의 도움이 필요한 경우 시스템 콜을 통해 커널 처리를 용처한다.

시스템 콜의 종류

  • 프로세스 생성, 삭제
  • 메모리 확보, 해제
  • 프로세스 간 통간(IPC)
  • 네트워크
  • 파일시스템 다루기(디바이스 접근)

CPU의 모드 변경

시스템 콜은 CPU의 특수한 명령을 실행해야만 호출된다. 프로세스는 보통 사용자 모드로 실행되고 있지만 커널에 처리를 요청하고자 시스템 콜을 호출하면 CPU에서는 인터럽트(interruot) 이벤트가 발생한다.

인터럽트 이벤트가 발생하면 CPU는 사용자 모드에서 커널 모드로 변경되며 요청한 내용을 처라하기 위해 커널은 동작하기 시작한다. 요청한 내용 처리가 끝나면 커널 내의 시스템 콜 처리가 종료된다. 그리고 다시 사용자 모드로 돌아가 프로세스의 동작을 계속 진행한다.

CPU의 모드 변경

커널은 프로세스가 요청한 내용을 처리하기 전에 프로세스의 요구가 유효한지 확인한다. 요구 사항이 맞지 않는다면 커널은 시스템 콜을 실패했다고 처리한다.


시스템 콜 호출의 동작 순서

프로세스가 어떠한 시스템 콜을 호출했는가는 strace 명령어를 통해 확인할 수 있다. 단순한 메세지를 출력하는 hello.cstrace를 적용해보겠습니다.

#include <stdio.h>

int main(void)
{
	puts("hello world");
    return 0;
}

먼저 컴파일한 다음 strace 없이 샐행한다.

gcc -o hello hello.c
./hello

hello world가 출력된다면 strace로 이 프로그램이 어떤 시스템 콜을 호출하는지 살펴본다.

strace -o hello.log ./hello

프로그램은 이전과 마찬가지로 화면 hello world를 출력한다. 그렇다면 strace의 결과가 저장된 hello.logcat을 통해 내용을 살펴본다.

execve("./hello", ["./hello"], 0x7ffece29a2d0 /* 64 vars */) = 0
brk(NULL)                               = 0x562efdee8000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffe6ecf6930) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa45daea000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=57679, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 57679, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa45dadb000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0i8\235HZ\227\223\333\350s\360\352,\223\340."..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2216304, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2260560, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa45d8b3000
mmap(0x7fa45d8db000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7fa45d8db000
mmap(0x7fa45da70000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7fa45da70000
mmap(0x7fa45dac8000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x214000) = 0x7fa45dac8000
mmap(0x7fa45dace000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa45dace000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa45d8b0000
arch_prctl(ARCH_SET_FS, 0x7fa45d8b0740) = 0
set_tid_address(0x7fa45d8b0a10)         = 12347
set_robust_list(0x7fa45d8b0a20, 24)     = 0
rseq(0x7fa45d8b10e0, 0x20, 0, 0x53053053) = 0
mprotect(0x7fa45dac8000, 16384, PROT_READ) = 0
mprotect(0x562efd6c4000, 4096, PROT_READ) = 0
mprotect(0x7fa45db24000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7fa45dadb000, 57679)           = 0
newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}, AT_EMPTY_PATH) = 0
getrandom("\xa4\x4e\x3c\x1a\x5c\xa6\x2f\xcd", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x562efdee8000
brk(0x562efdf09000)                     = 0x562efdf09000
write(1, "hello world\n", 12)           = 12
exit_group(0)                           = ?
+++ exited with 0 +++

와 같이 나온다. 여기서 제일 중요한 것은 write(1, "hello world\n", 12) = 12이다.
write(1, "hello world\n", 12) = 12write() 시스템 콜이 hello world\n문자열을 화면에 출력하고 있다. hello.c는 C언어로 작성되었지만 작성한 언어와 상관없이 프로그램이 커널에 처리를 요청할 때에는 결국 시스템 콜을 호출한다.

profile
김민세

0개의 댓글