Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

230224
1178
109352


[리눅스 커널] 프로세스: 유저 모드와 커널 모드란 4. Process Management

유저 레벨 프로세스에 대해 알아보기 전에 먼저 유저 모드가 무엇인지 살펴봅시다.

우리가 라즈베리파이에서 바탕 화면에 있는 아이콘을 클릭해서 어떤 프로그램이 실행 중이라고 가정합시다. 이 때 프로그램은 유저 모드나 커널 모드 중 하나로 실행합니다. ftrace 로그를 보면 시스템 콜로 유저 모드와 커널 모드를 자주 스위칭하는 동작을 확인할 수 있습니다.

   "유저 모드와 커널 모드로 나누는 기준은 무엇일까?" 

메모리 접근과 실행 권한 기준으로 두 모드로 분류합니다. 이해를 돕기 위해 한 가지 예를 들겠습니다. 어떤 시스템 메모리 공간을 유저모드나 커널모드 구분없이 0~4G 사이 가상 메모리에서 연속으로 쓰고 있다고 가정해볼까요? 커널 코드와 전역 변수가 0~4G 메모리 구간에 메모리 주소로 매핑돼 있는 조건입니다.

만약 어떤 운영체제에서 라즈베리파이 Geany와 같은 어플리케이션까지 리눅스 시스템 개발자가 구현하면 문제가 발생하지 않을 것입니다. 리눅스 시스템 개발자는 커널 코드 실행 흐름이나 메모리 제어하는 동작을 잘 알고 있을 가능성이 높기 때문입니다. 따라서 유저와 커널 모드로 실행 흐름을 두 개로 나눠서 설계할 필요가 없을 것입니다.

그런데 리눅스 시스템 개발자가 아닌 리눅스 커널 세부 동작을 잘 모르는 응용 프로그램 개발자가 어플리케이션을 유저 모드와 커널 모드가 없는 운영체제에서 어플리케이션을 개발한다고 생각해봅시다. 응용 프로그램에서 메모리를 제대로 관리하면 괜찮겠지만 실수로 코드를 잘못 작성해서 커널 자료구조나 함수가 위치한 메모리 공간을 오염시키면 어떻게 될까요? 커널 패닉으로 리눅스 시스템이 리부팅하거나 다양한 시스템 오동작을 유발할 것입니다.

이와 비슷한 이유로 메모리에 직접 접근하지 못하는 프로그램 언어를 설계했는데 이를 Managed 언어라고 합니다. 뎨표적인 예로 프로그램 언어인 Java(자바)를 들 수 있습니다. 메모리에 직접 접근 가능한 언어는 대표적으로 C와 C++입니다. 
 
응용 어플리케이션을 개발하고 설치할 수 있는 라즈베리파이 같은 범용 운영체제에서는 0~3G 가상 메모리 공간까지는 유저 모드만 접근할 수 있고 커널 모드에서는 0~4G 메모리까지 접근할 수 있도록 제한을 걸어 둡니다. 

우리가 실행하는 모든 어플리케이션은 유저 모드와 커널 모드 중 하나 모드에서 동작한다고 했습니다. 그런데 유저 어플리케이션 입장에서 커널에 어떤 서비스를 요청해야 할 때가 있습니다. 예를 들면, 파일을 읽고 쓰거나 현재 실행 중인 프로세스 정보를 얻고 싶을 때입니다. 

   “ 이럴 때 유저 모드 어플리케이션에서 커널에 어떤 방식으로 이 정보를 요청할까?”
 
시스템 콜을 실행하면 유저 모드에서 커널 모드로 전환합니다. 유저 모드에서 커널 코드를 직접 실행하지 못하기 때문입니다. 그래서 시스템 콜을 통해서 특정 서비스를 커널에게 요청하는 것입니다.

   "커널 입장에서는 시스템 콜을 통해 유저 모드에서 요청한 서비스를 어떻게 처리할까?" 

시스템 콜 종류가 다양하나 커널에서는 다음과 같이 동작합니다.
1. 시스템 콜 매개인자 오류 점검
먼저 유저 공간에서 시스템 콜을 실행하면서 전달한 인자에 오류가 있는 지 점검합니다. 유저 모드에서 어플리케이션 개발자가 시스템 콜로 인자를 잘못 전달할 수 있기 때문입니다. 커널에서 이 인자를 그대로 읽어서 처리하면 커널이 오동작할 가능성이 높습니다.

그래서 시스템 콜이 실행하고 호출되는 시스템 콜 핸들러 함수에서는 예외 처리 루틴이 많습니다.

2. 커널 내부 함수 호출
그 다음 유저 모드에서 요청한 서비스 종류에 따라 커널 내부 함수를 호출합니다.

3. 유저 어플리케이션에 요청한 정보를 알려줌
유저 어플리케이션 메모리 공간에 요청한 정보를 써주거나 직접 요청한 값을 반환합니다.

이번에 유저 모드에 대해 조금 더 생각해 봅시다. 공부를 할수록 계속 의문이 생깁니다. 

   “유저 모드에 해당하는 파일은 무엇일까?” 

라즈베리파이에서 위와 같은 경로에 리눅스 시스템 구동에 필요한 라이브러리가 있습니다.
root@raspberrypi:/home/pi# ls /usr/lib/arm-linux-gnueabihf/
alsa-lib libltdl.so.7
audit libltdl.so.7.3.1
avahi liblwres.so.141
bluetooth liblwres.so.141.0.3
caca liblz4.so.1
cifs-utils liblz4.so.1.7.1
coreutils libm.a
...

이해하기 쉽게 설명을 드리면 이 라이브러리 파일이 유저 모드 코드라 볼 수 있습니다. 리눅스에서 실행 중인 유저 어플리케이션은 이 라이브러리와 링킹되어 메모리에 적재되어 실행하기 때문입니다.

이번에는 유저 레벨 프로세스에 대해 생각해봅시다. 

   “유저 레벨 프로세스는 어떻게 생성될까?“

여러분이 라즈베리파이에서 바탕 화면에 있는 아이콘을 더블 클릭해 어떤 프로그램을 실행할 수 있습니다. 혹은 리눅스 시스템 프로그램을 작성한 후 컴파일한 프로그램을 실행할 수 있습니다. 두 가지 방식으로 프로그램을 실행할 때 같은 흐름으로 유저 프로세스를 생성합니다.

여기서 우리가 기억해야 할 중요한 사실이 있습니다.

   "유저 모드에서 스스로 프로세스를 생성하지 못한다. 

대신 리눅스에서 제공하는 라이브러리 도움으로 프로세스 생성 요청이 가능합니다. 이런 기능을 GNU libc가 제공하며 이를 glibc이라고도 합니다.

   "유저 어플리케이션과 유저 레벨 프로세스는 커널에게 있어 의미일까? 

유저 어플리케이션은 라즈베리파이에서 보는 프로그램을 의미하고 유저 레벨 프로세스는 이 어플리케이션을 실행하는 주체를 의미합니다. 하지만 커널은 유저 어플리케이션이 뭔지 모릅니다. 커널 입장에서 실행 대상은 프로세스 밖에 없는 것입니다. 커널 입장에서는 유저 어플리케이션이나 유저 레벨 프로세스를 모두 프로세스로 간주합니다.

   "유저 레벨 프로세스와 커널 레벨 프로세스의 가장 큰 차이점은 무엇일까? 

먼저 실행 출발점이 다릅니다.

유저 레벨 프로세스는 유저 모드에서 fork() 함수나 pthread_create() 함수를 호출합니다. 다음 경로에 있는 glibc 리눅스 라이브러리 파일 도움으로 커널에 서비스를 요청해 생성합니다. 
root@raspberrypi:/home/pi# ls /usr/lib/arm-linux-gnueabihf/libc.a
/usr/lib/arm-linux-gnueabihf/libc.a

이렇게 유저 레벨 프로세스가 커널에 어떤 서비스를 요청할 일이 생기면 시스템 콜을 실행해야 합니다. 하지만 커널 레벨 프로세스는 커널 모드에서 실행합니다. 커널의 kthread_create() 함수 호출로 실행되는 것입니다.


Reference(프로세스 관리)

"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)

#Reference(프로세스 관리)
프로세스 디버깅
   glibc fork 함수 gdb 디버깅




핑백

덧글

댓글 입력 영역