리눅스에서 프로세스의 상태를 한번 알아보겠습니다.
프로세스에 대해서는 앞에서도 간단히 설명 드렸습니다. 실행중인 프로그램이라고 단순히 설명 할 수 있습니다. 이러한 프로세스들은 여러가지의 상태를 가지게 되는데, 이 상태들에 대해 오늘 한번 알아보겠습니다.
구글에 검색해 보면 많은 사람들이 자료를 블로깅해 놓았을텐데, 보면 다들 분류하는 상태의 갯수가 다른 것 처럼 보입니다. 하지만 크게 보시면 시스템의 프로세스는 5가지의 상태를 가지게 됩니다.
1179 struct task_struct {
1180 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
1181 void *stack;
1182 atomic_t usage;
1183 unsigned int flags; /* per process flags, defined below */
1184 unsigned int ptrace;
1185
.
.
.
1613 };
지난번 한번 보셨던 task_struct, 프로세스 서술자 입니다 !
코드 중 빨간색으로 음영처리 된 녀석이 프로세스의 상태 값을 저장하고 있는 녀석입니다.
그럼 저 state 변수에 들어가는 상태들을 한번 확인해 보도록 하겠습니다.
아래 내용은 task_struct와 동일한 sched.h에 포함된 매크로 입니다.
주석부분에 잘 설명이 되어 있고, 마스크형식으로 총 5가지의 상태를 알 수 있습니다.
Running, Interruptible, Uninterruptible, Stopped, Trasced 이렇게 5가지요 ^^.
하지만 주석부분을 잘 읽어보시면 runnabillty와 exiting 두가지 상태로 나뉠수 있는 것을 알 수 있죠?
이 부분에 대해서는 아래 계속해서 설명 드리겠습니다.
/* 198 * Task state bitmask. NOTE! These bits are also 199 * encoded in fs/proc/array.c: get_task_state(). 200 * 201 * We have two separate sets of flags: task->state 202 * is about runnability, while task->exit_state are 203 * about the task exiting. Confusing, but this way 204 * modifying one set can't modify the other one by 205 * mistake. 206 */ 207 #define TASK_RUNNING 0 208 #define TASK_INTERRUPTIBLE 1 209 #define TASK_UNINTERRUPTIBLE 2 210 #define __TASK_STOPPED 4 211 #define __TASK_TRACED 8 212 /* in tsk->exit_state */ 213 #define EXIT_DEAD 16 214 #define EXIT_ZOMBIE 32 215 #define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD) 216 /* in tsk->state again */ 217 #define TASK_DEAD 64 218 #define TASK_WAKEKILL 128 219 #define TASK_WAKING 256 220 #define TASK_PARKED 512 221 #define TASK_STATE_MAX 1024 222
그럼 위의 이놈들의 상태에 대해서 한번 알아보도록 하겠습니다.
아래 그림을 보시죠 !
기존의 TASK가 fork()를 호출하여 새로운 프로세스를 생성하게 됩니다. 그렇게 생성된 프로세스는 TASK_RUNNING 상태 중의 READY상태가 되죠. 아 READY 상태는 프로세스 생성은 되었으나 CPU에 자원을 할당받지 못한 프로세스의 상태입니다. 또는 실행 중이던 프로세스가 할당된 시간을 모두 사용한 후 다음 할당까지 기다리는 거죠.
이렇게 준비상태의 프로세스가 CPU와 여러 자원을 할당 받아 명령어를 처리하는 실행상태가 TASK_RUNNING에 running 상태입니다.
이 때 두가지의 레벨로 나뉠 수 있는데요, User Level과 Kernel Level 입니다.
먼저 User Level은 말 그대로 사용자 수준 권한으로 동작하고 0~4GB의 주소공간 중 아래 3GB를 사용합니다. 기본적으로 사용자 영역에서 사용되는 응용프로그램들을 CPU가 수행하고 있는 상태입니다.
Kernel Level은 CPU에서 커널 프로그램의 일부분을 수행하고 있는 상태인데요, 프로그램 중 커널 수준의 권한이 필요한 부분에서 커널에 진입하여 프로세스가 실행되는 상태입니다.
다음은 TASk_INTERRUPTIBLE에 대해서 알아보죠. 이름만 들어도 어느정도 감이 잡히실 겁니다.
현재 실행중인 TASK_RUNNING상태의 프로세스가 TASK_INTERRUPTIBLE로 상태변경되는데요, 어떠한 특정 사건, 조건이 발생하길 기다리면서 휴면상태로 대기하고 있는 것입니다. 그러다 기다리고 있던 조건 즉, 이벤트가 발생하게 되면 커널은 그 조건을 수행하기 위해 다시 TASK_RUNNING상태로 변경되죠.
위 그림을 다시 보면 TASK_UNINTERRUPTIBLE이라는 대기상태가 하나 더 있습니다.
거의 모든 것이 TASk_INTERRUPTIBLE과 동일한데요, 여기서 한가지 차이점은 어떤 사건, 조건이 발생해도가능 상태로 변경되지 않는 다는 것이죠.
이렇게 설명하니 좀 헷갈리실 것 같은데. 이 상태는 프로세스가 자신의 원하는 사건, 조건을 제외하고 다른 이벤트에서 응답을 하지 않는 다는 것입니다. 그 특정조건이 빠른 시간내에 발생하는 경우에 사용되지요.
TASK_TRACED 상태는 이름그대로 생각하시면 됩니다. 추적당하고 있는 것이죠. 현재의 프로세스가 다른 프로세스 즉, 디버거나 다른 툴에 의해 프로세스가 추적당하고 있는 상태라고 보시면 됩니다.
TASk_STOPPED은 프로세스 실행이 정지된 상태입니다. 프로세스가 실행이 정지된 상태입니다. 하지만 INTERRUPTIBLE과 UNINTERRUPTIBLE과는 다르게 실행 가능한 상태가 아닙니다.
그럼 이러한 프로세스의 상태를 변화시키는 방법에 대해서 살펴봅시다.
함수를 이용하여 프로세스 상태를 조작하는 것인데요. 아래 코드를 보시죠
아래 코드는 sched.h를 참조하시면 됩니다.
250 #define __set_task_state(tsk, state_value) \ 251 do { (tsk)->state = (state_value); } while (0) 252 #define set_task_state(tsk, state_value) \ 253 set_mb((tsk)->state, (state_value)) 254 255 /* 256 * set_current_state() includes a barrier so that the write of current->state 257 * is correctly serialised wrt the caller's subsequent test of whether to 258 * actually sleep: 259 * 260 * set_current_state(TASK_UNINTERRUPTIBLE); 261 * if (do_i_need_to_sleep()) 262 * schedule(); 263 * 264 * If the caller does not need such serialisation then use __set_current_state() 265 */ 266 #define __set_current_state(state_value) \ 267 do { current->state = (state_value); } while (0) 268 #define set_current_state(state_value) \ 269 set_mb(current->state, (state_value))
set_task_state와 set_currnt_state 함수는 동일하다고 보시면 됩니다.
set_task_state(task, state);
이러한 방식으로 함수를 사용하여 특정 TASK를 원하는 프로세스 상태로 변경할 수 있답니다.
프로세스는 지속적으로 상태를 변경해가며 여러 프로세스들이 유기적으로 실행이 되고 있습니다. 이 외의 더 많은 정보를 알고 싶으시다면 검색 또는 서적을 이용하여서 공부를 하시면 많은 도움이 될 것 같네요.!
다음시간에도 프로세스에 대해 계속 이야기하도록 하겠습니다
'Linux > Kernel Analysis' 카테고리의 다른 글
void __init early_fixmap_init(void) (작성중...) (0) | 2018.08.21 |
---|---|
[Linux] Writing udev rules, udev 규칙 작성법에 관하여.. (1) | 2015.10.31 |
[Linux Kernel] Process - Task struct(프로세스 서술자) (0) | 2014.07.06 |
[Linux Kernel] 프로세스란? (1) | 2014.07.01 |
[Linux Kernel] Kobject에 대하여 (0) | 2014.06.27 |