여러분들은 리눅스 시스템이 탑재된 휴대폰이나 라즈베리파이를 쓰면 동시에 여러 프로그램을 실행할 수 있습니다. 휴대폰을 보면 다양한 프로그램이 동시에 실행하는 것을 확인할 수 있습니다. 예를 들면 브라우저를 실행하면서 음악을 듣거나 메신저를 하면서 어플리케이션을 다운로드 할 수 있습니다.

그래서 사람들은 여러 프로세스들이 동시에 CPU에서 실행한다고 느낄 수 있습니다. 하지만 CPU는 여러 개의 프로세스를 절대로 동시에 실행할 수는 없습니다. 리눅스 커널을 포함한 다양한 운영체제에서 스케줄링과 멀티 태스킹 기법이 생겨난 이유는 다음과 같습니다.
CPU는 한 순간에 한 개의 프로세스의 코드만을 실행할 수 있습니다.
 
여러 개의 프로세스들이 효율적으로 번갈아 CPU에서 실행할 수 있게 규칙을 부여하고 프로세스들을 관리하는 소프트웨어 모듈을 스케줄러라고 말합니다.

하나의 프로세스는 CPU에서 실행을 시작하면 계속 CPU에서 실행하는 것이 아니라 실행을 하다가 잠깐 멈추고 다시 실행하는 방식으로 동작합니다. 즉, 프로세스는 CPU를 점유하면서 실행 중인 상태와 실행 대기하는 상태로 계속 변경하는 것입니다.

메모리에 존재하는 여러 프로세스 중에서 실제 CPU에서 실행될 프로세스를 선택하는 일을 스케줄링이라고 말합니다. 이 때 어떤 프로세스를 어떤 방식으로 선택할지를 결정해야 합니다. 

스케줄링 동작은 다음 그림으로 표현할 수 있습니다.
 

CPU에서 실행하려고 대기 중인 Process A ~ Process D 프로세스 중 하나를 선택해서 CPU에서 실행시키는 동작입니다.

스케줄링 동작을 다른 각도에서 살펴보겠습니다. 다음 그림은 프로세스 상태 변화 다이어그램입니다.
 

커널은 프로세스에게 프로세스 상태를 부여합니다. 프로세스가 생성 및 실행된 후 종료할 때까지 위와 같은 상태 변화로 동작합니다.

프로세스가 CPU에서 실행하기 위해서는 실행 대기(TASK_RUNNING) 상태로 변경한 다음 커널 스케줄링에 의해 CPU 실행(TASK_RUNNING) 상태로 변경되어야 합니다.

대부분 보통 프로세스 실행 상태 변화 흐름을 프로세스 1인칭으로 바라볼 때가 많습니다.
이번에는 시스템 전체 관점으로 프로세스 상태 변화 다이어그램을 살펴봅시다.

 

커널에서 실행 중인 전체 프로세스가 각각 어떤 상태로 실행 중인지를 보여주는 그림입니다. 원형으로 표시된 A~N는 각각 프로세스를 의미합니다.

A~D 프로세스들은 실행 대기(TASK_RUNNING) 상태에 있습니다. CPU에서 실행하기 위해 대기 중인 프로세스입니다.

CPU 실행(TASK_RUNNING) 상태를 보면 E 프로세스가 CPU에서 실행 중입니다.

다른 관점으로 스케줄링 동작을 다음과 같이 설명할 수 있습니다.
실행 대기(TASK_RUNNING) 상태에 있는 프로세스 중 하나를 선택해서 CPU 실행(TASK_RUNNING) 상태로 바꿔주는 동작

커널 스케줄링은 프로세스 상태 기준으로 실행 대기 중에 있는 프로세스를 어떤 방식으로 실행할지를 결정합니다. 따라서 프로세스 상태 정보는 매우 중요합니다.

대부분 드라이버 코드를 작성할 때 프로세스 상태를 변경하는 코드를 작성할 필요는 없습니다. 하지만, 커널 스레드 핸들 함수를 구현할 때 프로세스 상태를 변경하는 코드를 입력할 때가 있습니다. 이 때 반드시 set_current_state() 함수를 써서 프로세스 상태를 변경해야 합니다.

그 이유는 다음과 같이 set_current_state() 함수 코드를 보면 알 수 있습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/sched.h]
1 #define set_current_state(state_value)
2 do {
3 WARN_ON_ONCE(is_special_task_state(state_value));
4 current->task_state_change = _THIS_IP_;
5 smp_store_mb(current->state, (state_value));
6 } while (0)

5 번째 줄을 보면 smp_store_mb() 함수를 호출해서 메모리 배리어를 실행합니다.
메모리 배리어 코드를 추가하면 GCC 컴파일러가 코드 최적화를 위해 코드 위치를 변경하는 것을 방지합니다.


스케줄링(scheduling)/스케줄러(schedule)란 무엇일까? 


여러분들은 리눅스 시스템이 탑재된 휴대폰이나 라즈베리파이를 쓰면 동시에 여러 프로그램을 실행할 수 있습니다. 휴대폰을 보면 다양한 프로그램이 동시에 실행하는 것을 확인할 수 있습니다. 예를 들면 브라우저를 실행하면서 음악을 듣거나 메신저를 하면서 어플리케이션을 다운로드 할 수 있습니다.

그래서 사람들은 여러 프로세스들이 동시에 CPU에서 실행한다고 느낄 수 있습니다. 하지만 CPU는 여러 개의 프로세스를 절대로 동시에 실행할 수는 없습니다. 리눅스 커널을 포함한 다양한 운영체제에서 스케줄링과 멀티 태스킹 기법이 생겨난 이유는 다음과 같습니다.
CPU는 한 순간에 한 개의 프로세스의 코드만을 실행할 수 있습니다.

여러 개의 프로세스들이 효율적으로 번갈아 CPU에서 실행할 수 있게 규칙을 부여하고 프로세스들을 관리하는 소프트웨어 모듈을 스케줄러라고 말합니다.

하나의 프로세스는 CPU에서 실행을 시작하면 계속 CPU에서 실행하는 것이 아니라 실행을 하다가 잠깐 멈추고 다시 실행하는 방식으로 동작합니다. 즉, 프로세스는 CPU를 점유하면서 실행 중인 상태와 실행 대기하는 상태로 계속 변경하는 것입니다.

메모리에 존재하는 여러 프로세스 중에서 실제 CPU에서 실행될 프로세스를 선택하는 일을 스케줄링이라고 말합니다. 이 때 어떤 프로세스를 어떤 방식으로 선택할지를 결정해야 합니다. 

스케줄링 동작은 다음 그림으로 표현할 수 있습니다.
 

CPU에서 실행하려고 대기 중인 Process A ~ Process D 프로세스 중 하나를 선택해서 CPU에서 실행시키는 동작입니다.

스케줄링 동작을 다른 각도에서 살펴보겠습니다. 다음 그림은 프로세스 상태 변화 다이어그램입니다.
 

커널은 프로세스에게 프로세스 상태를 부여합니다. 프로세스가 생성 및 실행된 후 종료할 때까지 위와 같은 상태 변화로 동작합니다.

프로세스가 CPU에서 실행하기 위해서는 실행 대기(TASK_RUNNING) 상태로 변경한 다음 커널 스케줄링에 의해 CPU 실행(TASK_RUNNING) 상태로 변경되어야 합니다.

대부분 보통 프로세스 실행 상태 변화 흐름을 프로세스 1인칭으로 바라볼 때가 많습니다.
이번에는 시스템 전체 관점으로 프로세스 상태 변화 다이어그램을 살펴봅시다.

 

커널에서 실행 중인 전체 프로세스가 각각 어떤 상태로 실행 중인지를 보여주는 그림입니다. 원형으로 표시된 A~N는 각각 프로세스를 의미합니다.

A~D 프로세스들은 실행 대기(TASK_RUNNING) 상태에 있습니다. CPU에서 실행하기 위해 대기 중인 프로세스입니다.

CPU 실행(TASK_RUNNING) 상태를 보면 E 프로세스가 CPU에서 실행 중입니다.

다른 관점으로 스케줄링 동작을 다음과 같이 설명할 수 있습니다.
실행 대기(TASK_RUNNING) 상태에 있는 프로세스 중 하나를 선택해서 CPU 실행(TASK_RUNNING) 상태로 바꿔주는 동작

커널 스케줄링은 프로세스 상태 기준으로 실행 대기 중에 있는 프로세스를 어떤 방식으로 실행할지를 결정합니다. 따라서 프로세스 상태 정보는 매우 중요합니다.

대부분 드라이버 코드를 작성할 때 프로세스 상태를 변경하는 코드를 작성할 필요는 없습니다. 하지만, 커널 스레드 핸들 함수를 구현할 때 프로세스 상태를 변경하는 코드를 입력할 때가 있습니다. 이 때 반드시 set_current_state() 함수를 써서 프로세스 상태를 변경해야 합니다.

그 이유는 다음과 같이 set_current_state() 함수 코드를 보면 알 수 있습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/sched.h]
1 #define set_current_state(state_value) \
2 do { \
3 WARN_ON_ONCE(is_special_task_state(state_value));\
4 current->task_state_change = _THIS_IP_; \
5 smp_store_mb(current->state, (state_value)); \
6 } while (0)

5 번째 줄을 보면 smp_store_mb() 함수를 호출해서 메모리 배리어를 실행합니다.
메모리 배리어 코드를 추가하면 GCC 컴파일러가 코드 최적화를 위해 코드 위치를 변경하는 것을 방지합니다.


+ Recent posts