Post

컴퓨터는 쉴때 뭐할까?

컴퓨터는 쉬는시간에 뭐할까

예전에 리눅스에 간단한 앱을 올리는데, 큐에 요청이 없다면 while문이 돌면서 대기하게 했다. 그런데 cpu 사용률이 너무 올라가서 sleep 코드를 삽입했고, 그제서야 점유율이 줄어들었다.

그러다가 유휴 프로세스에 대해 알게 되었고, while문 공회전과 어떤 차이점이 있는지 궁금해서 한번 알아봤다.

유휴 프로세스란

스크린샷 2024-05-08 200810

시스템 유휴 프로세스(System Idle Process, 시스템 아이들 프로세스)는 다른 실행 가능한 스레드들이 CPU에 스케줄되지 않았을 때 실행되는 하나 이상의 커널 스레드로 이루어진다. 멀티프로세서 시스템에서는 각 CPU 코어에 관련된 하나의 유휴 스레드가 존재한다. 하이퍼스레딩이 활성화된 시스템에서는 각 논리 프로세서를 위한 유휴 스레드가 존재한다.

라고 검색하면 나오는데 쉽게 말해서 cpu가 할일없을때 실행되는 프로세스다. 위의 사진과 같이 cpu 점유율이 낮아지면서 놀고 있으면 유휴 프로세스가 작동중인 시점이다.

스크린샷 2024-05-08 200723

CPU 시간으로 정렬해서 보면 압도적으로 가장 많은 시간을 점유중이다. 아이러니하게도 컴퓨터는 대부분의 시간을 놀고 있는 셈이다.

코드를 보자

우리 모두의 오픈소스인 리눅스 코드를 살펴보기로 했다.

그런데 어디서부터 봐야될지 감이 안와서 이곳저곳 참고했는데, 커널 스케줄링 -> 프로세스 idle 쪽으로 가면 되는것 같다.

1
2
3
4
5
6
7
8
9
10
11
static void do_idle(void)
{

    while (!need_resched()) {
		// ...
        cpuidle_idle_call();
        // ...
    }
}

  • https://github.com/torvalds/linux/blob/dccb07f2914cdab2ac3a5b6c98406f765acab803/kernel/sched/idle.c

코드가 상당히 복잡했는데, 많이 간추려봤다.

먼저 idle 전환이 필요하다고 생각되면 cpu idle 스케줄링을 시작하는 듯 하다.

1
2
3
4
5
6
7
void __cpuidle default_idle(void)
{
	raw_safe_halt();
	raw_local_irq_disable();
}

  • https://github.com/torvalds/linux/blob/master/arch/x86/kernel/process.c#L742

여기서부터 좀 헷갈렸는데, 유휴 프로세스도 어쨌든 프로세스니까 프로세스쪽 idle 코드를 살펴보면 되지 않을까 싶어서 코드를 살펴봤다.

raw_safe_halt() 메서드를 호출하는걸 볼수 있다. 쫒아가보자.

1
2
3
4
5

// ...
#define raw_safe_halt()			arch_safe_halt()
// ...
  • https://github.com/torvalds/linux/blob/dccb07f2914cdab2ac3a5b6c98406f765acab803/include/linux/irqflags.h#L186

raw_safe_halt()는 arch_safe_halt()란다. 아키텍쳐마다 살짝씩 다르게 되는건가? 다시 추적해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
static __always_inline void arch_safe_halt(void)
{
	native_safe_halt();
}

// ...

static __always_inline void native_safe_halt(void)
{
	mds_idle_clear_cpu_buffers();
	asm volatile("sti; hlt": : :"memory");
}

  • https://github.com/torvalds/linux/blob/dccb07f2914cdab2ac3a5b6c98406f765acab803/arch/x86/include/asm/irqflags.h#L45

우리의 친구 x86쪽으로 와봤다. arm 코드도 있었던 것 같은데, 아마 구동원리는 비슷하지 않을까 싶어서 그냥 한놈만 가져왔다.

asm volatile(“sti; hlt”: : :”memory”);

이 놈을 한번 살펴보자.

해석해보자

sti 명령어는 “Set Interrupt Flag”의 약자로, 인터럽트를 허용하는 x86 아키텍처의 어셈블리 명령어라고 한다. 예전에 임베디드 공부할때 비슷한 놈을 종종 본것 같다. 아마 인터럽트 플래그를 설정해서 활성화하고, ISR이 실행되서 인터럽트 발생 시 감지할 수 있게 하는 명령어같다.

hlt 명령어는 x86 아키텍처에서 사용되고, CPU가 아무런 작업도 수행하지 않는 대기 상태로 전환된다. hlt를 실행하면 CPU의 실행 파이프라인이 비워지고, 다음 명령어가 실행되기 전까지 대기 상태가 된다고 한다. 인터럽트가 발생한다면 대기 상태는 종료된다.

:::memory는 컴파일러에게 해당 어셈블리 코드가 메모리에 영향을 줄 수 있음을 알려줍니다. 이는 메모리를 수정하지 않더라도, 인터럽트 활성화와 정지로 인해 메모리의 내용이 변경될 수 있음을 나타냅니다.

이건 못찾겠어서 챗gpt에게 물어봤다. 위와 같이 대답해줬다.

정리해보자

처음 볼때는 while문에 탈출 조건이 없는게 특이했는데, 인터럽트로 while문을 부수고 나오는 방식인 듯 하다. 흐름을 정리해보면,

유휴 프로세스 스케줄링 -> 유휴 프로세스 작동 -> cpu 대기상태 -> 인터럽트 발생 시 종료

이런 느낌인 것 같다.

소프트웨어 상에서 while문 공회전과는 아예 다른 어셈블리 명령어가 사용된 것을 확인할 수 있었다. 임베디드 공부할 때 스위치 딸깍딸깍 하면서 인터럽트 관련 코드 짜던 기억이 있는데, 간만에 다시 마주쳐서 반가웠다.

참고

  • https://yohda.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%BB%A4%EB%84%90-Cpuidle-function
  • https://github.com/torvalds/linux
  • https://modoocode.com/en/inst/sti
This post is licensed under CC BY 4.0 by the author.

© . Some rights reserved.

Using the Jekyll theme Chirpy