SungJin Kang

SungJin Kang

hour30000@gmail.com

© 2024

Dark Mode

스레드 컨텍스트 스위치 내부 동작

두 가지 메커니즘을 이해해야 한다. 첫번 째는 커널에 들어가고, 나오는 메커니즘이다. 그러니깐 유저 모드 코드에서 동작 중인 어떤 스레드를 커널쪽 코드에서 동작하도록 전환하는 것이다. 두 번째는 커널 모드에서 동작 중인 한 스레드를 다른 스레드로 전환하는 컨텍스트 스위치 메커니즘을 이해해야 한다.
스레드 A가 sched_yield() 호출하고 스레드 B에 의해 대체되었을 때 아래와 같은 동작들이 발생한다.
스레드 A가 커널에 진입한다. ( 유저 모드에서 커널 모드로 전환 )
커널에서 동작 중인 스레드 A가 커널에 있는 스레드 B로 전환한다. ( 스레드 컨텍스트 스위치는 반드시 커널 모드에서만 이루어진다 )
스레드 B가 커널을 빠져나간다. ( 커널 모드에서 유저 모드로 전환 ) ( 스레드 A는 커널에 머무른다 )
각각의 스레드는 유저 모드 스택과 커널 모드 스택을 가지고 있다. 한 스레드가 커널에 진입할 때 유저 모드 스택(SS:ESP)의 현재 값과 명령어 포인터(CS:EIP)를 스레드의 커널 모드 스택에 저장한다, 그리고 CPU는 커널 모드 스택으로 전환한다. ( “int $80” 시스템 콜 메커니즘을 통해 CPU에서 자체적으로 이루어 진다. “int”는 인터럽트를 의미하고, “0x80”은 인터럽트 넘버이다. 인터럽트는 프로그램 흐름을 해당 인터럽트를 다루는 곳으로 전환한다. 리눅스에서는 0x80 인터럽트 핸들러는 커널이다. 시스템 콜을 다루기 위해 사용된다. “int $80” 명령어를 수행하면 eax 레지스터에 저장되어 있는 숫자에 맞는 시스템 콜을 호출한다. ) 다른 레지스터 값, Flag들도 커널 스택에 저장된다.

스레드가 다시 커널에서 유저 모드로 돌아 올 때 커널 모드 스택에 저장해두었던 레지스터 값, 플래그들을 다시 가져와 유저 모드 스택과 명령어 포인터 값들을 복구한다. ( 커널로 들어갈 때 유저 모드에서의 정보를 백업해두고 커널에서 유저 모드로 복귀할 때 저장해둔 정보들을 복구함. 또한 커널에서 유저 모드로 복귀할 때 커널 모드에서 사용한 커널 스택을 정리(clean)함. )

스레드 컨텍스트 스위치(커널 모드에서 스레드간의 컨텍스트 스위치)시 스레드는 스케줄러를 호출한다.(스케줄러는 별개 스레드에서 동작하는 것이 아니라 현재 스레드에서 동작한다. 현재 스레드의 스택을 사용한다. 스레드의 스택은 유저 모드에서 사용되는 스택, 스케줄러와 같은 커널 모드에서 사용하는 스택으로 나뉜다.) 스케줄러 코드는 다음으로 동작할 프로세스를 선택하고 “switch_to()”함수를 호출한다. 이 함수는 다른 스레드의 커널 스택로 전환하는 동작을 수행한다(현재 스레드의 TCB로 스택 포인터의 값을 저장한고, 다음으로 실행할 스레드의 TCB에서 이전에 저장해두었던 스택 포인터를 로드한다. 스레드 컨텍스트 스위치의 핵심은 커널 모드에서 스레드간의 커널 스택을 전환하는 것이다 ). 이 지점에서 커널에서 잘 사용되지 않는 다른 스레드 상태(floating point, SSE 레지스터와 같은 것들) 값들에 대해서도 저장, 복구하는 동작을 수행한다. 만약 전환된 스레드들이 가상 메모리 공간을 공유하지 않는다면(해당 스레드들이 서로 다른 스레드들에 존재한다면), 페이지 테이블 또한 전환된다.

그래서 스레드 컨텍스트 스위치시 한 스레드의 유저 모드 상태 값들이 저장되지도 복구되지도 않는 것을 볼 수 있는데, 이는 너가 커널에 진입하고, 나갈 때 스레드의 커널 스택에 저장되고 복구되기 때문이다.(스레드 컨텍스트 스위치는 스레드가 커널모드로 진입한 후 커널에서 동작 중인 다른 스레드로 전환한다는 개념이다. 그러니 스레드 컨텍스트 스위치를 위해 이미 커널에 진입한 시점에서는 커널 진입시 이미 유저 모드 상태 값들을 백업(저장)해두었다는 의미. 커널 진입한 후 벌어지는 스레드 컨텍스트 스위치에서는 유저 모드의 상태 값에 대해 걱정할 필요가 없다는 의미) 컨텍스트 스위치 코드는 유저 모드 레지스터 값들을 덮어쓰지 않을까 걱정할 필요가 없다. 그 시점에서는 이미 커널 스택에 그것들이 안전하게 저장(백업)되어 있기 때문이다.


윈도우나 리눅스 같은 시스템에서는 “시스템 스레드”(주로 “커널 스레드”라고 불림.)라는 개념이 존재한다. 시스템 스레드는 특별한 스레드인데, 이 스레드는 오직 커널 모드에서만 동작하기 때문이다. ( 유저 모드에서의 스택이 없다는 것이다. )

references : https://stackoverflow.com/a/12702322, https://stackoverflow.com/a/40239396