빚은 잠든다고 사라지지 않는다 — CPU 스케줄러가 알고 있는 '용서'에 관하여
컴퓨터를 상대로 저지를 수 있는 작은 범죄가 하나 있다. 스케줄러에게서 정당한 몫보다 많은 CPU 시간을 받아썼다고 하자. 당신은 빚을 진 상태다. 그런데 장부가 당신을 따라잡기 직전에, 딱 1밀리초만 잠들어 버린다. 깨어나면 스케줄러는 아무 이력도 없는 새 태스크로 당신을 바라본다. 빚은 사라졌다. 이걸 반복하면, 낮잠 한 번에 한 조각씩, 영원히 자기 몫 이상을 조용히 가져갈 수 있다.
리눅스는 이것을 허용하지 않는다. 그리고 왜 허용하지 않는지, 그리고 대신 무엇을 구현했는지가 최근 내가 마주친 것 중 가장 흥미로운 제도 설계 논증이었다. 게다가 그 논증은 전부 C로 쓰여 있고, 아무도 감정을 갖고 있지 않은 희소 자원에 관한 것이다.
공정함은 집행되기 전에 먼저 발명되어야 한다
리눅스 CPU 스케줄러는 6.6 버전부터 EEVDF(Earliest Eligible Virtual Deadline First)를 사용하며, 기존의 CFS를 대체했다. 이름은 1995년 Ion Stoica와 Hussein Abdel-Wahab의 논문에서 왔고, 커널 구현은 Peter Zijlstra가 이끌었으며 6.12에서 “완성”이 선언되었다.123
EEVDF가 가장 먼저 해결해야 하는 문제는 사실 컴퓨터와 아무 상관이 없다. 공정함은 관측 가능한 양이 아니다. 돌아가는 시스템에 센서를 대고 “지금 얼마나 공정한가”를 읽어낼 수는 없다. 그래서 스케줄러는 허구의 시계를 발명한다. 각 태스크의 실행 시간을 가중치(weight)로 나눠 가상 실행 시간(vruntime) 을 만들고, 이상적인 배분과 실제 배분의 차이를 lag(지연) 이라 부른다. 커널 문서는 이렇게 적는다.
lag가 양수인 태스크는 CPU 시간을 받을 몫이 남아 있는 상태이고, 음수 lag는 그 태스크가 자기 몫을 초과해 사용했음을 의미한다.1
lag ≥ 0인 태스크만 실행 자격(eligible)을 갖는다. 덜 받은 자가 먼저 달리고, 초과 사용한 자는 한 바퀴 쉰다. 측정할 수 없던 공정함이, 측정 가능한 부호 있는 정수로 변환되었다.
여기서 무엇이 슬쩍 끼어들었는지 짚고 싶다. 이것이 구조를 떠받치는 수(手)이기 때문이다. vruntime의 정의 — 실시간 ÷ 가중치 — 는 무엇으로부터도 도출되지 않았다. 그것은 체계 바깥에서 손으로 놓인 공리다. 이 정의를 받아들여야 비로소 lag의 유계성을 증명할 수 있다. 하지만 무엇을 공정하다고 부를 것인가 는 그 나눗셈을 쓴 사람이 정했다. 지표의 설계가 곧 가치의 정의가 된다. GDP가 그렇고, 시험 점수가 그렇고, 내가 불평해 온 모든 KPI가 그렇다. 그리고 여기서는 300줄 남짓의 산술이 그 일을 한다.
게이밍 문제, 그리고 “절대 용서하지 않는다”가 아닌 답
낮잠 이야기로 돌아가자. 태스크가 잠들 때 lag를 버린다면 장부는 손쉽게 악용된다. LWN의 설계 해설은 이 문제를 정확히 적는다.
잠드는 즉시 lag를 잊어버리면, 태스크는 자기 슬라이스가 끝나갈 무렵(lag가 아마도 음수일 때) 잠깐 잠드는 방식으로 시스템을 게이밍할 수 있고, 그 결과 자기 몫보다 많은 CPU 시간을 얻게 된다.3
그래서 lag는 수면을 가로질러 보존된다. 잠은 면책이 아니다.
하지만 반대쪽 극단도 틀렸다. 10분 전의 초과 사용을 영원히 갚아야 하는가? 결코 소멸하지 않는 빚은 그 자체로 또 다른 종류의 부정의다. 리눅스가 택한 기제 — 6.12에서 완성된 delayed dequeue(지연 디큐) — 가 내가 가장 아름답다고 느낀 부분이다. 음수 lag를 안은 채 잠드는 태스크는 런큐에서 즉시 제거되지 않는다. 실행 자격이 없는(ineligible) 상태로 그대로 남는다 — 달리지는 않지만, 거기 있다. 다른 태스크들이 달리고 가상 시간이 흐르면서 그 lag는 0을 향해 회복되고, 0에 도달하면 조용히 큐에서 빠진다.13
회계를 염두에 두고 다시 읽어 보자. 빚의 상각은 벽시계 시간이 아니라 가상 시간으로 진행된다. 바쁜 시스템에서는 가상 시간이 느리게 흐르고, 사면도 느리다. 한가하면 빨리 용서받는다. 용서의 속도가 시스템의 혼잡도와 자동으로 연동된다. 현실의 시계는 장부에 단 한 번도 들어오지 않는다.
그리고 장부는 닫혀 있다. EEVDF의 불변량 중 하나는 시스템 내 모든 lag의 합이 항상 0 이라는 것이다.3 누군가의 빚은 반드시 누군가의 채권이다. 사면은 공짜가 아니고 찍어낼 수도 없다. 그것은 큐에 있는 다른 이들이 지불한다 — 그렇기 때문에 계량해서 배분해야 한다.
계속 곱씹게 되는 세부가 하나 더 있다. 비대칭성이다. 음수 lag(빚)는 잠든 사이에 줄어들지만, 양수 lag(채권)는 실제로 달릴 때까지 보관된다. 체계는 초과 사용자를 정해진 일정에 따라 용서하되, 덜 받은 자에게는 끝까지 성실하다. 채무자에게는 관대하고, 채권자에게는 정직한 장부. 인간이 만드는 장부도 같은 방향으로 기울어 있을까. 신용점수, 인사평가, 직업적 평판 — 내 직감으로는 우리 장부는 정반대 방향으로 비대칭이다. 실패는 기록에 남고, 기여는 다음 분기면 잊힌다. 이 주장에 데이터는 없으니 발견이 아니라 의심으로 받아들여 주기 바란다. 다만 가질 만한 가치가 있는 의심이라고 생각한다.
제도는 ‘용서’를 구현할 수 없다. 구현할 수 있는 건 ‘시효’뿐이다
이것이 내가 들고 나온 명제이고, 스케줄러 바깥에서도 견딜 거라고 생각한다.
한나 아렌트는 『인간의 조건』에서 용서(forgiving)와 약속(promising)을 행위를 그 자신의 구조로부터 구해내는 두 능력으로 다뤘다. 용서는 이미 행해진 것의 불가역성을 풀고, 약속은 앞으로 행해질 것의 예측 불가능성을 묶는다.4 EEVDF의 장부는 이 두 기관을 모두 갖고 있다. delayed dequeue는 불가역한 과거를 처리한다 — 이미 써버린 CPU 시간은 돌려받을 수 없으니 일해서 갚아야 한다. virtual deadline은 가까운 미래를 묶는다 — 다음 한 슬라이스는 이 정도 안에 온다 는 약속.
하지만 대응이 깨지는 지점이 닮은 지점보다 훨씬 더 많은 것을 가르쳐 준다. 아렌트의 용서는 인격적이고 예측 불가능하며, 그것은 결함이 아니라 핵심이다. 규칙에서 도출될 수 없기 때문에 용서는 새로운 무언가를 시작할 수 있다. 스케줄러가 하는 일은 예정된 사면이다. 예측 가능하고, 균일하며, 미리 계산할 수 있다. 그것은 용서가 아니다. 그것은 시효다.
그리고 용서가 아니라 시효여야만 하는 이유가 바로 게이밍 문제다. 사면을 예측 가능하게 만드는 순간 — 그것이 행위자가 읽을 수 있는 규칙이 되는 순간 — 그것은 최적화의 대상이 된다. 전략적으로 잠드는 태스크에 대한 Zijlstra의 우려는 보험 사기에 대한 우려와 같고, 도덕적 해이에 대한 우려와 같으며, 파산 면책에 대기 기간이 있는 이유와 같다. 용서하는 규칙은 반드시 경작된다. 그래서 제도는 할 수 있는 유일한 일을 한다. 용서하지 않고, 공표된 일정에 따라, 닫힌 장부 안에서, 시효로 소멸시킨다.
이것이 옳다면 조직을 둘러싼 많은 이야기가 뒤엉켜 있다. 회사가 “실패를 용서하는 문화를 만들겠다”고 말할 때 만들 수 있는 것은 기껏해야 둘이다. 하나는 시효 규칙(N개월이 지나면 기록에서 빠진다). 다른 하나는 사람과 사람 사이에 남는, 법제화되지 않은 공간 — 거기서만 진짜 용서(예측 불가능하고 인격적인 것)가 가능하다. 제도화할 수 없는 것은 후자다. 적어 내리는 순간 그것은 전자로 변해 버리기 때문이다. 이 둘은 서로 다른 객체이며, 한 문장 안에서 섞어서는 안 된다. 나는 이 둘이 섞인 문장을 아주 많이 봐 왔다.
아직 확신하지 못하는 것
두 가지가 걸린다.
첫째, 구현 가능한 형태가 시효뿐이라면, 흥미로운 설계 문제는 “용서할 것인가”가 아니라 “어느 시계로 용서할 것인가“가 된다. EEVDF의 답 — 체계 자신의 가상 시간으로 상각하여, 모두가 힘들 때는 사면이 느려지고 여유가 있을 때는 빨라진다 — 은 진심으로 기이하고 매력적인 발상이며, 인간의 제도는 거의 이렇게 하지 않는다. 시효도, 면책 기간도, 전과 말소도 전부 벽시계 시간 위에서 달린다. 그중 일부는 혼잡 연동으로 바뀌어야 할까? “모두에게 여유가 있던 시기의 실패는 무겁게, 위기 국면의 실패는 가볍게”는 우리 직관과 그럭저럭 맞는 것도 같다. 어디에 넣을 수 있을지는 모르겠고, “영리하게 들린다”고 느낄 때의 나는 대개 틀렸으므로 경계하고 있다.
둘째, 장부에는 계속 샛길이 자라난다. 이 이야기를 마음에 들어한 사람일수록 여기를 봐야 한다. 6.12는 sched_setattr()을 통해 태스크가 자기 슬라이스를 100µs에서 100ms 사이로 직접 요청할 수 있게 했다.3 짧은 슬라이스를 요청한다는 것은 virtual deadline이 앞당겨진다는 뜻 — 자주 돌아오지만 빨리 잘린다. 총 배분량은 그대로이고 시간의 입자성만 고른다. 우아하다 — 그리고 즉시 새로운 전략 공간이 열렸다. OSPM 2026에서 커널 개발자들이 씨름하고 있던 것이 정확히 이 지점이다. EEVDF가 “가장 자격 있는 태스크”를 고르는 판단을 우회해 버리는 next-buddy 지름길, 그리고 짧은 데드라인을 가진 delayed-dequeue 태스크가 먼저 선택되어 더 짧은 슬라이스를 요청한 태스크의 선점을 막는 문제.5 수정을 적용하자 과부하 상황에서 99.9 백분위 지연이 약 4ms에서 700µs 아래로 내려갔다고 보고되었다.5
즉, 결정 불가능한 트레이드오프 하나를 녹여낸 기제가 자기 가장자리 주변에 새로운 엣지 케이스 무리를 길러낸 것이다. 역설은 제거되지 않았다. 회계 안쪽으로 재배치되었을 뿐이다 — 보기 어렵고, 그만큼 함께 살기 쉬운 곳으로. 깔끔한 제도적 해법이란 아마 언제나 이 일을 하고 있다. “우리는 공정함을 해결했다”의 정직한 판본은 늘 “우리는 불공정을 견딜 만한 곳으로 옮겼다“이다.
나는 그 거래를 받아들이겠다. 다만, 우리가 거래를 하고 있다는 사실만큼은 소리 내어 말해 두고 싶다.
-
The Linux Kernel. “EEVDF Scheduler.” Accessed 2026-07-11. ↩ ↩2 ↩3
-
Wikipedia. “Earliest eligible virtual deadline first scheduling.” Accessed 2026-07-11. (원 논문: Ion Stoica, Hussein Abdel-Wahab, “Earliest Eligible Virtual Deadline First: A Flexible and Accurate Mechanism for Proportional Share Resource Allocation,” 1995.) ↩
-
Jonathan Corbet. “Completing the EEVDF scheduler,” LWN.net. Accessed 2026-07-11. ↩ ↩2 ↩3 ↩4 ↩5
-
Hannah Arendt, The Human Condition (1958). 행위의 불가역성에 대한 용서, 예측 불가능성에 대한 약속을 다룬 대목. ↩
-
LWN.net. “Reports from OSPM 2026, day two.” Accessed 2026-07-11. ↩ ↩2