SCHED_FIFO
와 SCHED_RR
은 리눅스 커널의 실시간 스케줄링 정책 중 두 가지로, 실시간 시스템에서 높은 우선순위를 가진 작업을 효과적으로 처리하기 위해 사용된다. SCHED_FIFO
와 SCHED_RR
는 각각의 특성과 활용 방법이 다르며, 이를 정확히 이해하고 적용하는 것은 Preempt RT 환경에서 실시간 애플리케이션의 성능을 최적화하는 데 매우 중요하다.
SCHED_FIFO
SCHED_FIFO
(First-In, First-Out) 스케줄링 정책은 선입선출 방식으로 동작하는 비선점(Non-Preemptive) 실시간 스케줄러이다. 이 정책에서는 우선순위가 높은 스레드가 실행 중일 때, 그 스레드가 명시적으로 sched_yield()
를 호출하거나, 더 높은 우선순위를 가진 다른 스레드가 준비 상태에 놓이지 않는 한, 절대로 실행이 중단되지 않는다. 이는 실시간 시스템에서 매우 결정론적(Deterministic)인 응답 시간을 보장할 수 있다는 장점이 있다.
SCHED_FIFO의 동작 원리
SCHED_FIFO
는 다음과 같은 원칙에 따라 동작한다:
-
우선순위 기반 실행:
SCHED_FIFO
정책을 따르는 스레드는 정해진 우선순위에 따라 스케줄링된다. 우선순위가 높은 스레드가 실행되며, 동일한 우선순위를 가진 스레드들 사이에서는 먼저 대기열에 들어온 스레드가 실행된다. -
선점되지 않음: 동일한 우선순위를 가지는 다른 스레드가 실행 대기 상태에 들어오지 않는 한, 현재 실행 중인 스레드는 CPU를 점유하고 계속 실행된다.
-
명시적 양보:
SCHED_FIFO
스레드는 자발적으로 CPU 사용을 양보하지 않는 한 다른 스레드로 전환되지 않는다. 스레드는sched_yield()
함수를 호출하여 다른 동일 우선순위의 스레드에게 CPU를 양보할 수 있다.
SCHED_FIFO 스케줄링의 예
다음은 SCHED_FIFO
를 사용하는 간단한 스레드 생성 예제이다:
#include <sched.h>
#include <pthread.h>
#include <stdio.h>
void* thread_function(void* arg) {
printf("Thread with SCHED_FIFO policy is running\n");
while(1) {
// 무한 루프 안에서 작업 수행
}
}
int main() {
pthread_t thread;
struct sched_param param;
pthread_create(&thread, NULL, thread_function, NULL);
param.sched_priority = 10; // 우선순위 설정
pthread_setschedparam(thread, SCHED_FIFO, ¶m);
pthread_join(thread, NULL);
return 0;
}
이 예제에서 SCHED_FIFO
스레드는 무한 루프 내에서 작업을 수행하며, 명시적으로 양보하거나 종료되지 않는 한 CPU를 계속 점유하게 된다.
SCHED_RR
SCHED_RR
(Round-Robin) 스케줄링 정책은 SCHED_FIFO
와 유사하지만, 시간 할당량(Time Slice)을 사용하여 동일한 우선순위를 가진 스레드들 간에 공정하게 CPU를 분배한다. SCHED_RR
는 SCHED_FIFO
와 달리 선점적(Preemptive) 스케줄링을 허용하며, 실시간 응답성과 함께 시스템의 유연성을 보장한다.
SCHED_RR의 동작 원리
SCHED_RR
는 다음과 같은 원칙에 따라 동작한다:
-
시간 할당량:
SCHED_RR
는 각 스레드에 대해 정해진 시간 할당량을 제공한다. 이 시간 동안 스레드는 CPU를 점유할 수 있으며, 할당량이 끝나면 자동으로 스케줄러에 의해 동일한 우선순위를 가진 다른 스레드로 전환된다. -
선점적 스케줄링:
SCHED_RR
는 시간 할당량이 만료되면 선점되어, 다른 동일한 우선순위의 스레드에게 CPU를 넘깁니다. 이로 인해 동일 우선순위의 스레드들이 공정하게 실행 시간을 나눌 수 있다. -
우선순위 기반:
SCHED_RR
또한 우선순위 기반으로 작동하며, 우선순위가 높은 스레드가 먼저 실행된다. 그러나 동일한 우선순위를 가진 스레드들 사이에서는 라운드 로빈 방식으로 CPU 사용 시간을 나눈다.
SCHED_RR 스케줄링의 예
다음은 SCHED_RR
를 사용하는 스레드 생성 예제이다:
#include <sched.h>
#include <pthread.h>
#include <stdio.h>
void* thread_function(void* arg) {
printf("Thread with SCHED_RR policy is running\n");
while(1) {
// 무한 루프 안에서 작업 수행
}
}
int main() {
pthread_t thread;
struct sched_param param;
pthread_create(&thread, NULL, thread_function, NULL);
param.sched_priority = 10; // 우선순위 설정
pthread_setschedparam(thread, SCHED_RR, ¶m);
pthread_join(thread, NULL);
return 0;
}
이 예제에서는 SCHED_RR
스레드가 무한 루프 내에서 작업을 수행하며, 동일한 우선순위의 다른 스레드들과 공정하게 CPU 시간을 나누어 사용하게 된다.
SCHED_FIFO와 SCHED_RR의 비교
SCHED_FIFO
와 SCHED_RR
는 각각 장단점이 있으며, 특정 상황에 따라 적절하게 선택하여 사용해야 한다.
SCHED_FIFO
vs SCHED_RR
-
선점 여부:
SCHED_FIFO
는 선점되지 않는 반면,SCHED_RR
는 선점적이다.SCHED_FIFO
는 우선순위가 높은 스레드가 자발적으로 CPU를 양보하지 않는 한, 계속 실행된다. 반면,SCHED_RR
는 정해진 시간 할당량이 만료되면 다른 동일 우선순위의 스레드로 전환된다. -
우선순위 기반: 두 정책 모두 우선순위 기반으로 동작하지만,
SCHED_RR
는 동일 우선순위의 스레드들 간에 공정한 CPU 사용을 보장한다.SCHED_FIFO
는 우선순위가 높은 스레드가 우선적으로 실행되며, 동일 우선순위의 스레드들 사이에서 선입선출 방식으로 CPU를 분배한다. -
응답 시간:
SCHED_FIFO
는 매우 결정론적인 응답 시간을 제공할 수 있지만, 실시간 애플리케이션이 CPU를 장시간 점유할 수 있는 반면,SCHED_RR
는 시간 할당량을 기반으로 보다 균등한 CPU 사용 시간을 보장한다. 따라서SCHED_RR
는 스레드 간의 응답 시간을 균등하게 나누는 데 유리한다.
SCHED_FIFO
와 SCHED_RR
의 적절한 활용
-
SCHED_FIFO
사용:SCHED_FIFO
는 실시간 시스템에서 응답 시간의 예측 가능성을 보장하고, 특정 작업이 다른 작업보다 우선적으로 수행되어야 할 때 유용하다. 예를 들어, 시스템의 핵심 제어 루프나 실시간 데이터 수집 작업 등에서는SCHED_FIFO
를 사용하여 우선 순위가 높은 작업이 적시에 실행되도록 할 수 있다. -
SCHED_RR
사용:SCHED_RR
는 실시간 시스템 내에서 여러 스레드가 공정하게 CPU를 공유해야 할 때 적합한다. 시간 할당량을 통해 각 스레드가 CPU를 일정 시간 동안 사용할 수 있으며, 이는 시스템의 응답성을 높이고, 여러 작업이 동시에 진행될 때 공정한 자원 배분을 가능하게 한다.
SCHED_FIFO
와 SCHED_RR
의 설정 및 조정
스레드의 스케줄링 정책을 설정하기 위해 pthread_setschedparam()
함수를 사용한다. 이 함수는 스레드의 스케줄링 정책과 우선순위를 설정하는 데 사용된다. 예를 들어, 다음과 같이 스레드의 스케줄링 정책을 설정할 수 있다:
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
void* thread_function(void* arg) {
printf("Thread with SCHED_FIFO policy is running\n");
while(1) {
// 무한 루프 안에서 작업 수행
}
}
int main() {
pthread_t thread;
struct sched_param param;
pthread_create(&thread, NULL, thread_function, NULL);
param.sched_priority = 10; // 우선순위 설정
// SCHED_FIFO 또는 SCHED_RR로 스케줄링 정책 설정
pthread_setschedparam(thread, SCHED_FIFO, ¶m);
// pthread_setschedparam(thread, SCHED_RR, ¶m); // SCHED_RR로 설정할 경우
pthread_join(thread, NULL);
return 0;
}
위 예제에서 pthread_setschedparam()
함수를 사용하여 스레드의 스케줄링 정책을 SCHED_FIFO
로 설정하고 있다. SCHED_RR
로 변경하려면 주석을 해제하면 된다.
스케줄링 정책과 성능 최적화
실시간 애플리케이션의 성능을 최적화하기 위해서는 스케줄링 정책 외에도 여러 요소를 고려해야 한다. SCHED_FIFO
와 SCHED_RR
의 활용뿐만 아니라, 실시간 시스템의 전체적인 성능을 높이기 위해 다음과 같은 최적화 기법을 고려할 수 있다:
-
우선순위 조정: 작업의 중요도에 따라 적절한 우선순위를 설정하여, 중요한 작업이 충분히 자원을 확보할 수 있도록 한다.
-
시간 할당량 조정:
SCHED_RR
의 경우, 시간 할당량을 조정하여 각 스레드가 공정하게 CPU를 사용할 수 있도록 한다. -
실시간 우선순위 조정: 실시간 우선순위 조정은 시스템의 응답성을 개선하고, 중요한 실시간 작업이 신속하게 처리될 수 있도록 한다.
SCHED_FIFO
와 SCHED_RR
의 디버깅과 모니터링
실시간 스케줄링 정책을 적용한 시스템에서는 디버깅과 모니터링이 중요하다. 각 스케줄링 정책의 동작을 확인하고, 문제가 발생할 경우 신속하게 대응하기 위해 몇 가지 도구와 기법을 사용할 수 있다.
디버깅 기법
-
htop
및top
: 이들 도구를 사용하여 실시간 시스템의 프로세스와 스레드 상태를 모니터링할 수 있다. 특히htop
은SCHED_FIFO
와SCHED_RR
스케줄링 정책이 적용된 스레드를 시각적으로 확인할 수 있는 기능을 제공한다. -
chrt
명령어:chrt
명령어를 사용하여 실행 중인 프로세스의 스케줄링 정책과 우선순위를 조정할 수 있다. 예를 들어, 다음 명령어를 사용하여 프로세스의 스케줄링 정책을SCHED_FIFO
로 설정할 수 있다:
bash
chrt -f 10 <pid>
이 명령어는 프로세스 <pid>
의 스케줄링 정책을 SCHED_FIFO
로 설정하며, 우선순위를 10으로 설정한다.
pcontrol
:pcontrol
유틸리티를 사용하여 실시간 애플리케이션의 상태를 모니터링할 수 있다. 이 도구는 프로세스의 스케줄링 상태를 분석하고, 스케줄링 정책과 우선순위를 확인하는 데 유용하다.
성능 모니터링
-
perf
: 리눅스의 성능 분석 도구인perf
를 사용하여 실시간 애플리케이션의 성능을 모니터링하고 분석할 수 있다.perf
는 CPU 사용량, 함수 호출 횟수, 대기 시간 등을 측정하여 성능 병목 지점을 찾는 데 유용하다. -
latencytop
: 실시간 애플리케이션의 지연(latency)을 모니터링하는 데 유용한 도구이다.latencytop
은 시스템의 지연을 분석하고, 지연을 발생시키는 원인을 파악하는 데 도움을 준다. -
ftrace
:ftrace
는 리눅스 커널의 트레이싱 프레임워크로, 실시간 애플리케이션의 성능을 모니터링하는 데 사용된다.ftrace
를 사용하여 시스템 콜, 스케줄링 이벤트 등을 추적하고 분석할 수 있다.
실시간 애플리케이션에서의 고려 사항
실시간 애플리케이션을 개발하고 운영할 때는 다음과 같은 추가적인 고려 사항이 있다:
-
우선순위 역전(Blocking): 실시간 시스템에서는 우선순위 역전 문제를 피해야 한다. 낮은 우선순위의 스레드가 높은 우선순위의 스레드를 블로킹하지 않도록 설계해야 한다. 이를 해결하기 위해 우선순위 상속(priority inheritance) 메커니즘을 사용할 수 있다.
-
자원 경쟁: 공유 자원에 대한 경쟁이 발생하지 않도록 조정해야 한다. 자원에 대한 적절한 동기화와 관리를 통해 경쟁 문제를 최소화할 수 있다.
-
실시간 요구사항: 실시간 애플리케이션의 요구사항을 명확히 정의하고, 스케줄링 정책과 우선순위를 조정하여 실시간 요구를 충족할 수 있도록 해야 한다.
-
테스트 및 검증: 실시간 애플리케이션의 성능과 신뢰성을 테스트하고 검증하는 것이 중요하다. 다양한 테스트 환경과 조건을 통해 애플리케이션의 동작을 확인하고, 문제를 조기에 발견하여 수정할 수 있다.