Thread Sleeping, Timer

Seungyun Lee·3일 전

RTOS

목록 보기
13/14

Thread Sleeping

CPU가 쉴 틈 없이 while(1)을 도는 스핀락(Spinlock)의 낭비를 막기 위해, 스레드 스스로 "나 1초 동안 기절할 테니까, 내 차례 건너뛰고 다른 애들 먼저 실행시켜 줘!"라고 운영체제에 양보하는 우아한 기술입니다.

void Task(void){
	InitializationStuff();
	while(1){
		PeriodicStuff();
		OS_Sleep(ONE_SECOND); // go to sleep for 1 second
		}
}

void Scheduler(void){
	RunPt = RunPt->next; // skip at least one
	while((RunPt->Sleep)||(RunPt-> blocked)){
		RunPt = RunPt->next; // find one not sleeping and not blocked
	}
}

교수님이 슬라이드 하단에 적어둔 글은 "이 코드가 실전에서 왜 터질 수밖에 없는가?"를 경고하는 내용입니다. 서술형 출제 1순위입니다.

① The Fatal Crash (전멸 시나리오)

Exam Keyword: "Cannot let all threads sleep; requires an Idle Thread."
만약 시스템에 있는 모든 스레드가 동시에 OS_Sleep을 호출해서 전부 다 자버리면 어떻게 될까요?
스케줄러의 while 루프는 조건이 영원히 참(True)이 되어 무한 루프(Infinite loop)에 빠지고, 시스템이 완전히 다운(Crash)됩니다.
해결책: 절대 잠들지 않고, 절대 Block되지 않는 잉여 스레드인 Idle Thread를 무조건 하나 만들어 두어야 이 참사를 막을 수 있습니다.

② The Decrementer (누가 잠을 깨우는가?)

Exam Keyword: "A periodic background task (e.g., SysTick ISR) is needed to decrement the Sleep counters."
스레드는 스스로 자면서 자기 카운터를 줄일 수 없습니다. 따라서 백그라운드에서 주기적으로(예: 1ms마다) 울리는 하드웨어 타이머(SysTick)가 모든 TCB를 순회하며 Sleep 카운터를 1씩 깎아줘야(Decrement) 합니다. 카운터가 0이 되는 순간, 그 스레드는 잠에서 깨어납니다.

③ Jitter and Execution Delay (수면의 배신)

Exam Keyword: "Sleep parameter reaching 0 makes the thread Ready, not Running. This introduces scheduling delay (Jitter)."1초를 자겠다고 OS_Sleep을 호출했지만, 1초 뒤에 정확히 바로 실행되는 것이 아닙니다!카운터가 0이 되면 스레드는 '기절(Sleep)' 상태에서 '준비 완료(Ready)' 상태로 바뀔 뿐입니다. 내 차례가 올 때까지 현재 돌고 있는 다른 스레드들의 작업이 끝나기를 쳇바퀴(Round-robin) 줄에서 기다려야 합니다.만약 내 앞에 대기 중인 스레드가 nn개 있고, 한 스레드당 쓰는 시간이 Δt\Delta t라면, 내가 진짜로 깨어나서 코드를 실행하기까지 n×Δtn \times \Delta t 만큼의 오차 시간(Delay/Jitter)이 추가로 발생하게 됩니다. (이것이 RTOS에서 예측 가능성을 해치는 가장 큰 원인 중 하나입니다.)


Timer

1. 5 Hardware Components

타이머를 '알람시계'라고 생각하시면 완벽하게 이해됩니다.

  1. N-bit counter: 설정된 시간부터 0까지 거꾸로 숫자를 세는(Count down) 핵심 부품입니다. 0이 되면 Trigger flag를 띄우고 새로운 값으로 재장전(Reload)됩니다.

  2. Reload value (R): 알람을 맞추는 '시간 설정 값'입니다. 카운터가 0이 되면 이 R 값이 다시 카운터로 장전됩니다. (소프트웨어 초기화 시 딱 한 번 상수로 설정됨)

  3. Trigger flag: 카운터가 0에 도달했을 때 "알람 울렸다!"라고 CPU에게 알리는 깃발입니다. 이 깃발이 인터럽트(Interrupt)를 요청하며, ISR 내부의 소프트웨어가 반드시 이 깃발을 지워줘야(Clear) 다음 알람이 정상 작동합니다.

  4. Base clock: 타이머가 돌아가는 기본 속도입니다. 주파수(fbasef_{base}, 예: 80MHz)와 주기(tbaset_{base}, 예: 12.5ns)를 가집니다.

  5. Prescaler (M): Base clock이 너무 빨라서 타이머가 순식간에 끝나버리는 것을 막기 위해, 톱니바퀴 기어처럼 클럭을 MM배 느리게 쪼개주는(Modulo-M) 감속기입니다.

2. 시험에 무조건 나오는 타이머 계산 공식

교수님이 "원하는 인터럽트 주기(P)를 얻기 위해 Reload value(R)를 얼마로 설정해야 하는가?"를 묻는 계산 문제를 낼 확률이 99%입니다. 이 공식들은 치트 시트 구석에 꼭 적어가세요.

인터럽트 주기 (Interrupt Period, P):
P=tbase×M×(R+1)P = t_{base} \times M \times (R + 1)
(카운터가 R부터 0까지 세기 때문에 총 R+1 번의 카운트가 발생합니다.)

리로드 값 구하기 (Reload Value, R):
위 공식을 R에 대해 정리한 것입니다. 코드에 직접 넣어야 하는 값입니다.
R=(Ptbase×M)1R = \left( \frac{P}{t_{base} \times M} \right) - 1
(단, R은 타이머 비트 수에 따라 2N2^N보다 작은 정수여야 합니다.)

최대 CPU 점유율 (Maximum Utilization):
ISR이 실행되는 데 걸리는 최대 시간(Δt\Delta t)이 전체 주기(PP)에서 차지하는 비율입니다. 하나의 ISR이 CPU를 독점(Monopolizing)하지 못하게 방지할 때 계산합니다.
Max utilization=100×ΔtPMax\ utilization = 100 \times \frac{\Delta t}{P}

3. Priority & Preemption

이 부분은 RTOS 스케줄링 이론과 섞여서 서술형이나 True/False 문제로 자주 나옵니다.

  • Lower numbers signify higher priority: 숫자가 작을수록 VVIP입니다. (Priority 0이 제일 높음)
  • SysTick Priority 7: RTOS에서 메인 스레드를 교체(Context Switch)하는 SysTick 타이머는 우선순위가 7(가장 낮음)로 고정되어 있습니다. 그래서 사용자가 만드는 타이머 인터럽트는 0에서 6 사이로만 설정해야 합니다.
  • Preemption (선점): 실행 중인 ISR(예: Level 3)보다 더 높은 우선순위(예: Level 1)의 인터럽트가 터지면, CPU는 현재 ISR을 즉시 멈추고(Suspend) 높은 우선순위로 달려갑니다.
  • Equal priority (동일 우선순위): 같은 우선순위의 인터럽트가 터지면 새치기하지 못하고 First come first served (FIFO, 선입선출) 방식으로 순서대로 처리됩니다.

4. 타이머 초기화 9단계 (Initialization 9 Steps)

이 부분은 빈칸 채우기 코딩 문제로 나올 수 있습니다. 순서의 논리를 기억하세요.

  1. Activate clock (클럭 켜기)
  2. Set mode to continuous down counting (거꾸로 세기 모드)
  3. Set prescale (M) (감속기 설정)
  4. Set reload value (R) (알람 값 장전)
  5. Arm trigger flag (타이머 깃발 장전)
  6. Arm timer in NVIC (인터럽트 컨트롤러 장전)
  7. Set priority in NVIC (우선순위 부여)
  8. Clear trigger flag (혹시 모를 쓰레기 깃발 치우기)
  9. Enable interrupts (전체 인터럽트 허용, I=0)
profile
RTL, FPGA Engineer

0개의 댓글