Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

80258
1323
114582


4.1 프로세스 소개 4. Process Management

운영체체 도서에서 보통 가장 먼저 프로세스를 소개합니다. 리눅스 커널도 마찬가지입니다.
이번 절에서는 프로세스가 무엇인지 알아보고 태스크와 스레드에 대해서 살펴보겠습니다. 먼저 프로세스란 용어에 대해 같이 배워볼까요? 

프로세스란
프로세스는 추상적이고 다양한 의미를 담고 있어 프로세스가 무엇인지를 다양한 관점으로 바라볼 수 있습니다.

프로세스란 무엇일까요? 프로세스(Process)는 리눅스 시스템 메모리에서 실행 중인 프로그램을 말합니다. 스케줄링 대상인 태스크와 유사한 의미로 쓰입니다. 다수 프로세스를 실시간으로 사용하는 기법을 멀티프로세싱이라고 말하며 같은 시간에 멀티 프로그램을 실행하는 방식을 멀티태스킹이라고 합니다.

우리가 쓰고 있는 스마트폰 동작을 잠깐 생각해봅시다. 
전화를 하면서 메모를 남기고, 음악을 들으면서 브라우저를 볼 수 있습니다. 여러 어플리케이션이 동시에 실행하고 있습니다. 이것은 멀티태스킹을 수행해서 프로그램을 시분할 방식으로 처리하기 때문에 가능합니다.

이번에는 리눅스 개발자 입장에서 프로세스가 무엇인지 생각해 봅시다. 프로세스는 리눅스 시스템 메모리에 적재되어 실행을 대기하거나 실행하는 실행 흐름을 의미합니다. 이 부분을 읽으니 다양한 의문이 생깁니다.

   "프로세스가 실행을 대기한다면 실행할 때 어떤 과정을 거칠까?" 
   "프로세스는 어떤 구조체로 식별할까?" 

프로세스를 관리하는 자료구조이자 객체를 태스크 디스크립터라고 부르고 구조체는 struct task_struct 입니다. 이 구조체에 프로세스가 쓰는 메모리 리소스, 프로세스 이름, 실행 시각, 프로세스 아이디(PID), 프로세스 스택 최상단 주소와 같은 속성 정보가 저장돼 있습니다.

그렇다면 프로세스를 struct task_struct 이란 구조체로만 표현할 수 있을까요? 위에서 프로세스란 실행 흐름 그 자체라고 정의했습니다. 프로세스 실행 흐름은 어느 구조체에 저장할 수 있을까요? 이 질문에 다음과 같이 대답할 수 있습니다.

   "프로세스를 실행 흐름을 표현하는 또 하나 중요한 공간은 프로세스 스택 공간이며 
   이 프로세스 스택 최상단 주소에 struct thread_info 구조체가 있다."

그러면 다음 함수 실행 흐름을 보면서 프로세스 실행 흐름에 대해서 배워볼까요?
1 -000|__schedule()
2 -001|schedule_timeout()
3 -002|do_sigtimedwait()
4 -003|sys_rt_sigtimedwait()
5 -004|ret_fast_syscall(asm)

처음 위 함수 목록을 보면 낯설지만 차근 차근 보면 그리 어렵지 않을 것입니다.
위 함수 실행 흐름으로 다음 사실을 알 수 있습니다.
 - 함수 호출 방향은 5번째 줄에서 1번째 줄이다. 
 - 유저 공간 프로그램에서 sigtimedwait() 함수를 호출하면 이에 대응하는 시스템 콜 핸들러 함수인 sys_rt_sigtimedwait() 함수가 호출된다.  
- 1번째 줄 __schedule() 함수가 호출돼 스케줄링된다.

프로세스는 함수를 호출하면서 실행을 합니다. 

    "그렇다면 함수를 호출하고 실행할 때 어떤 리소스를 쓸까?" 

프로세스 스택 메모리 공간입니다. 모든 프로세스들은 커널 공간에서 실행할 때 각자 스택 공간을 할당 받으며 스택 공간 내에서 함수를 실행합니다. 

위에서 본 프로세스가 스케줄러에 의해 다시 실행한다고 가정합시다. 이후 어떻게 실행할까요?
1 -000|__schedule()
2 -001|schedule_timeout()
3 -002|do_sigtimedwait()
4 -003|sys_rt_sigtimedwait()
5 -004|ret_fast_syscall(asm)

1번 함수에서 5번 함수 방향으로 되돌아 올 것입니다. 이는 프로세스가 마지막에 실행했던 레지스터 세트와 실행 흐름이 프로세스 스택 공간에 저장돼 있었기 때문입니다. 여러분이 A() 함수에서 B() 함수를 호출하고 B() 함수에서 C() 함수를 호출하는 코드를 작성했다고 가정하겠습니다. C() 함수가 실행을 마무리하면 B() 함수에서 A() 함수로 되돌아 갑니다. 이와 같은 원리입니다.

정리하면 프로세스는 어느 정도 추상적인 개념을 내포합니다. 하지만 리눅스 커널에서 프로세스를 표현할 수 있는 다음과 같은 자료 구조가 있습니다.
  - struct task_struct: 태스크 디스크립터
  - struct thread_info: 프로세스 스레드 정보

프로세스에 대해 처음 공부를 시작하는 분들은 보통 다음과 같은 질문을 많이 합니다.

   " 프로세스를 잘 알려면 뭘 알아야 할까?"

프로세스가 무엇인지 잘 알려면 위에서 소개한 프로세스 속성 정보와 실행 흐름을 저장하는 구조체를 알아야 합니다. 프로세스를 제어하는 함수들은 위 구조체를 중심으로 실행되기 때문입니다.  

다음 절에서는 프로세스와 함께 자주 쓰는 용어인 태스크에 대해서 살펴봅니다.

태스크란
필자가 처음 리눅스 커널 코드를 볼 때 가장 궁금했던 점입니다.

   " 리눅스 커널 함수 이름에 ‘task’ 를 왜 많이 쓸까?" 

사실 태스크는 리눅스 이외 다른 운영체제에서 예전부터 많이 쓰던 용어입니다.
운영체제 이론을 다루는 예전 이론서에서 대부분 태스크란 단어를 많이 볼 수 있습니다.

태스크는 운영체제에서 어떤 의미일까요? 말 그대로 실행(Execution)이라 말할 수 있습니다.
운영체제 책들을 보면 첫 장에서 태스크에 대한 설명을 볼 수 있습니다. 최근 운영 체제에서는 기본으로 멀티 태스킹 환경에서 프로그램을 실행합니다. 하지만 예전에는 특정 코드나 프로그램 실행을 일괄 처리했습니다. 이 실행 및 작업 단위를 태스크라고 불렀습니다.

화면이 없는 간단한 시나리오의 임베디드 시스템에서는 태스크 2개가 서로 시그널을 주고 받으며 시스템 전체를 제어할 수 있습니다. 

하지만 태스크에 대한 개념은 현재 프로세스와 겹치는 부분이 많습니다. 그런데 태스크에 대한 의미가 프로세스와 스레드 기법을 도입하면서 발전했습니다. 태스크를 실행하는 단위인 실행(Execution)을 결정하는 기준이 스케줄링으로 바뀐 것입니다.  

예전에 쓰던 용어를 현재 소프트웨어에 그대로 쓰는 경우가 많습니다. 이를 레거시(Legacy)라고 말하고 과거 유물이란 뜻도 있습니다.

그래서 예전에 썼던 태스크란 용어를 리눅스 커널 소스 코드에서 그대로 쓰고 있습니다. 대표적인 예로 프로세스 속성을 표시하는 구조체 이름을 struct process_struct 대신 struct task_struct으로 쓰고 있습니다. 

프로세스 마다 속성을 표현하는 struct task_struct 구조체는 태스크 디스크립터 혹은 프로세스 디스크립터라고도 말합니다.

그래서 리눅스 커널 함수 이름이나 변수 중에 task란 단어가 보이면 프로세스 관련 코드라 생각해도 좋습니다. 예를 들어 다음 함수는 모두 프로세스를 관리 및 제어하는 역할을 수행하며 함수 이름에 보이는 task는 process로 바꿔도 무방합니다.
  - dump_task_regs
  - get_task_mm
  - get_task_pid
 - idle_task
  - task_tick_stop

그러면 이번 소절에서 배운 내용을 간단히 정리해볼까요?

   " 첫째, 리눅스 커널에서 태스크는 프로세스와 같은 개념으로 쓰는 용어이다." 
   " 둘째, 소스 코드나 프로세스에 대한 설명을 읽을 때 태스크란 단어를 보면 프로세스와 
      같은 개념으로 이해하자."

스레드란
스레드란 무엇일까요? 스레드는 유저 레벨에서 생성된 가벼운 프로세스라 말할 수 있습니다. 
스레드는 일반 프로세스에 비해 컨택스트 스위칭을 실행할 때 시간이 적게 걸립니다. 그 이유는 스레드는 자신이 속한 프로세스 내의 다른 스레드와 파일 디스크립터, 파일 및 시그널 정보에 대한 주소 공간을 공유하기 때문입니다. 프로세스가 자신만의 주소 공간을 갖는 것과 달리 스레드는 스레드 그룹 안의 다른 스레드와 주소 공간을 공유합니다.

하지만 커널 입장에서는 스레드를 다른 프로세스와 동등하게 관리합니다. 대신 각 프로세스 식별자인 태스크 디스크립터(struct task_struct)에서 스레드 그룹 여부를 점검할 뿐입니다.

다음 절에서는 리눅스 터미널에서 ps 명령어를 입력해서 프로세스에 대해 조금 더 알아 봅시다.

프로세스 확인하기
리눅스 커널은 그 내용이 방대하고 깊이가 있어 단기간에 익히기 어렵습니다. 대부분 “프로세스란 무엇인가”란 질문으로 리눅스 커널을 공부를 시작합니다. 안타깝게도 프로세스에 대한 내용을 조금 읽다가 대부분 포기합니다. 그러면 프로세스를 설명하는 책이나 블로그가 이론으로 프로세스를 설명하기 때문입니다.

   " 그러면 어떻게 해야 프로세스를 빨리 익힐 수 있을까?"

프로세스에 익숙해지려면 먼저 리눅스 시스템에서 프로세스를 출력하는 명령어를 자주 입력하고 ftrace 로그에서 프로세스 관련 정보를 자주 봐야 합니다. 이번 장에서는 라즈베리파이에서 터미널을 열어서 명령어를 입력하고 ftrace 로그로 프로세스 동작을 확인합니다.

Reference(프로세스 관리)

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

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

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


핑백

덧글

  • 2019/04/02 21:44 # 답글 비공개

    비공개 덧글입니다.
  • 2019/04/03 12:40 # 비공개

    비공개 답글입니다.
  • 지나가는설인 2019/08/19 21:59 # 삭제 답글

    글 잘 읽었습니다. 상세한 설명 정말 감사드려요! 9월에 책 내시는 것 맞나요? 나오면 꼭 사고싶네요
  • AustinKim 2019/08/19 22:01 #

    응원해주셔서 감사합니다. 책은 10월 이후에 출간될 것 같습니다.
    자주 블로그에 오셔서 유용한 정보 얻어 가셨으면 좋겠습니다.
댓글 입력 영역