리눅스 3.16의 개발자 프리뷰로 등장한 통합 계층을 평가하고, 자동 그룹 스케줄링과 대비되는 ‘하인드사이트 그룹’이라는 가상 설계를 통해 cgroups의 쟁점을 되짚는다.
LWN을 한번 써볼 준비가 되셨나요? LWN을 구독하면 Linux 및 자유 소프트웨어 커뮤니티에서 벌어지는 일을 최신 상태로 따라갈 수 있고, 구독자 전용 사이트 기능도 활용할 수 있습니다. 신용카드 없이도 직접 확인하실 수 있도록 **무료 체험 구독**을 제공해 드리게 되어 기쁩니다. 지금 참여해 주세요!
이 연재의 최초 정당화는, 우리 모두가 Linux 컨트롤 그룹을 충분히 이해해서 그것을 둘러싸고 간간이 벌어지는 논쟁을 즐길 수 있게 하자는 것이었다. 이제 그게 얼마나 통했는지 확인할 때가 되었다 — 컨트롤 그룹을 둘러싼 것으로 보이는 모든 이슈의 맥락 속에서 어떤 제안이나 도전을 당신이나 내가 얼마나 잘 평가할 수 있는지 보는 것이다. 이 연재의 마지막 편인 이번 글에서는, 새로 얻은 기술을 시험해 볼 기회가 두 번 있다.
첫 번째로 눈에 띄는 목표는 Linux 3.16에서 개발자 프리뷰로 제공되는 “통합 계층(unified hierarchy)”이며, 이는 이 지면에서 최근 다뤄졌다. 아직 하지 않았다면, 지금쯤 그 글로 돌아가 다시 읽으며 우리가 발견했던 여러 이슈들이 (그리고 어떻게) 다뤄지고 있는지 확인해 보는 것도 좋겠다. 철저히 하고 싶다면 unified-hierarchy.txt 문서도 읽어 보라.
중요해 보였던 이슈 목록을 먼저 적어두는 것이 도움이 될 수 있다. 주목해야 할 설계 패턴, 또는 안티패턴을 나열해 두는 것도 도움이 된다. 내가 유용하다고 느끼는 네 가지는 “Ghosts of Unix Past”라는 이전 연재에서 식별된 것으로, full exploitation, conflated designs, unfixable designs, high maintenance designs이며, 모두 그 마지막 글 끝부분에 요약되어 있다.
핵심 이슈를 식별하고 결론에 도달했다면, 그것이 확인되기를 바라거나 그것을 방어할 기회를 원할 것이다. 이 필요를 충족하기 위해 내 결론 일부를 제시하겠다.
계층의 통합
클래식 cgroups가 허용하는 계층의 수가 과도하다는 점은 거의 논쟁의 여지가 없다. 그 수를 하나로 줄이는 것이 이상적인지는 덜 명확하다. 우리가 조사했을 때 계층은 매우 다른 방식으로 사용되었다. 어떤 서브시스템은 아래로 제어를 강제했고, 다른 것들은 위로 회계를 수집했다. 이는 구현상의 관심사가 다른, 매우 상이한 사용법이다. 별도의 계층을 정당화할 수도 있다고 주장할 수 있다.
통합 계층은 과도한 중복 제거를 분명히 지향하고 있으며, 이는 좋다. 서로 다른 서브시스템들이 진정으로 양립할 수 없는 요구를 가질 수도 있다는 점을 인정하는 것 같지는 않지만, 그렇다고 별도 계층의 가능성을 완전히 닫아버린 것도 아직은 아니다. 그래서 이 부분은 B — 잘하고 있지만 개선의 여지가 있다.
프로세스는 리프에서만 허용
통합 계층은 프로세스가 트리의 리프(leaf)에만 존재하도록 요구한다. 이를 강제하는 접근은 다소 서툴다. 리프는 “어떤 서브시스템도 자식에게 확장하지 않는 노드”이며, 계층에 새 레벨을 만들 때 2단계 춤이 필요하다. 먼저 프로세스를 아래로 옮겨야 하고, 그 다음에야 서브시스템을 아래로 확장할 수 있다.
이 복잡성은 어차피 이미 가능한 결과를 얻기 위한 것이다(시스템 관리자와 도구는 쉽게 프로세스를 리프에만 두기로 선택할 수 있다). 따라서 대체로 흥미롭지 않다. 커널이 “프로세스는 리프에만”이라는 건전한 정책을 강제해야 하는지조차 불명확하다. 마치 대부분 사용자에게 파일시스템 루트를 읽기 전용으로 두는 것이 합리적이라는 정책을 커널이 강제해야 한다고 보지 않는 것과 같다.
이 이슈에는 C(너무 복잡함)를 주려 했지만, 설계의 흠 하나를 강조할 필요가 있다. 내부 cgroup에서는 프로세스가 배제되는데, _예외_가 루트 cgroup이다. 루트는 “특별한 취급”이 필요하기 때문인 듯하다. 이 예외 때문에, 나중에 분명해질 이유로 점수는 **C+**가 된다.
혼란스러운 서브시스템 길들이기
cgroup 서브시스템과 기능 요소 사이의 상관관계가 상당히 혼란스럽다는 것을 우리는 보았다. 이는 새로운 관찰이 아니다. 2011 Kernel Summit에서 Paul Turner는 다음과 같이 말했다고 전해진다:
Google은 경험에 비추어 많은 컨트롤러를 뜯어고쳐 더 나은 형태로 다시 만들 것이다.
그 정도의 재작성은 과할 수도 있지만, 현재의 서브시스템 구분을 덜 강조해, 프로세스 제어와 다른 자원 제어 사이에서 더 의미 있는 묶음이 등장할 수 있기를 기대하는 것은 바람직하다. 통합 계층은 이 필요를 진전시킬 위치에 있는 듯하지만, 안타깝게도 반대 방향으로 간다. 이제 cgroup.controllers와 cgroup.subtree_control 파일을 통해 cgroups 파일시스템 전반에 서브시스템 목록이 등장한다. 속성 파일이 이미 서브시스템 이름을 따르고 있긴 하지만, freezer.state 파일은 “freezer”가 독립 서브시스템이든 단지 기능 요소든 상관없이 의미가 있다.
cgroup.controllers에서 활성화된 서브시스템을 명시적으로 나열하는 것은 현재 구조를 사실상 굳혀버린다. 그래서 이 이슈는 내게 D다.
자원 소비자 ID 제공
5부에서, 메모리의 페이지는 메모리가 해제될 때 누가 환불을 받는지 식별할 수 있지만, 내용이 디스크로 기록될 때 IO에 대해 누가 청구되는지는 식별할 수 없다는 것을 보았다. 모든 서브시스템이 단일 계층을 사용하도록 강제함으로써, 단일 cgroup이 모든 자원 유형에 대한 자원 소비자 ID 역할을 할 수 있다. 이는 분명 문제의 해결책이지만, 좋은 해결책인지(자원마다 성격이 크게 다를 수 있다) 판단하기는 어렵다. 그래서 지금은 판단을 유보하며 B만 준다.
프로세스냐 스레드냐
클래식 cgroups는 하나의 프로세스 내부 스레드들을 서로 다른 cgroup에 둘 수 있게 한다. 그럴듯한 사용 사례를 상상하기는 어렵지만, 완전히 불가능한 것은 아니다.
cpuset 컨트롤러는 프로세스를 CPU 집합에 제한하고, 별도로 NUMA 시스템의 메모리 노드 집합에 제한할 수 있다. 전자의 제한은 cgroups 없이도 sched_setaffinity() 시스템 콜이나 taskset 프로그램을 사용해 어떤 스레드에든 부과할 수 있다. 하지만 메모리 노드 집합은 cgroups를 통해서만 설정할 수 있다. 하나의 주소 공간을 공유하는 서로 다른 스레드에 서로 다른 메모리 노드를 강제하는 것은 별 의미가 없으니, 이것만으로 스레드별 cgroups를 정당화하기는 어렵다. 하지만 cgroups로만 설정 가능한 다른 값들이 있다.
Linux 스케줄러는 전통적인 40단계 “nice” 스케일보다 훨씬 세밀하게 스레드 우선순위를 설정할 수 있다. 각 스레드나 그룹에 “weight”를 줄 수 있는데, 최대 약 100,000까지이며(weight = 1024 * 0.8nice, 대략). 이 weight는 cgroups로만 설정할 수 있다. 개별 스레드를 그렇게 세밀하게 제어하고 싶다면, 스레드를 cgroup에 넣어야 한다.
이 둘은 내가 “procfs 문제”라고 부르는 것의 예다. procfs 파일시스템은 명확한 설계 가이드라인이 없었기 때문에, 최소한의 설계 검토만으로도 각종 임기응변 기능이 커널에 추가되는 통로가 되었다. 그 결과, procfs는 프로세스 이상의 것들을 다룬다. 마찬가지로, cgroups는 프로세스 그룹 제어와 전혀 무관한 기능에 대한 “뒷문” 접근을 허용하는 듯하다. 스레드-인-cgroups의 유일한 용도가 이런 뒷문 덕을 보는 것이라면, 이를 금지하는 것이 더 나은 API 설계를 장려할지도 모른다.
통합 계층은 정확히 이를 수행하며, 프로세스(스레드 그룹으로도 알려짐)만 서로 다른 cgroup에 둘 수 있게 한다. 이는 좋아 보이지만 한 가지 질문을 제기한다: 우리는 정확히 무엇을 제어하려는가? 스레드? 프로세스? 다른 무엇? 어떤 답이든, 개별 스레드를 이동시키는 기능 지원을 제거하는 것은 좋은 생각처럼 보이므로 A를 준다.
코드 단순성
통합 계층은, 길고 긴 과정이 될 수도 있는 것의 한 단계일 뿐이다. 현재 상태에 이르기까지 코드에는 많은 개선이 있었지만, 바뀐 것들의 전체 가치는 오래된 기능을 제거할 수 있을 때에야 완전히 실현된다. 그 시점이 언제일지는 알 수 없다.
하지만 우리가 아는 것은, 결국 cgroups에 들어가야 하는 것은 스레드가 아니라 프로세스이며, 각 프로세스는 단 하나의 cgroup에만 속하면 된다는 점이다. 이는 분명 단순성을 가져오므로 A가 마땅하다.
요약
위에서 준 점수가 그다지 훌륭하지 않은 데에는 여러 원인이 있겠지만, 내 개인적 편향도 그중 하나일 것이다. 가장 큰 단일 원인은 통합 계층이 구축되고 있는 기반일 가능성이 높다. 많은 첫 구현이 그렇듯, cgroups는 그다지 좋지 않다. 계층의 역할과 서브시스템의 목적이 기껏해야 혼란스럽다. sow's ear밖에 없다면, 실크 지갑을 요구하는 건 무리다.
통합 계층의 전제 중 하나는 어떤 형태로든 컨트롤 그룹을 유지해야 한다는 것이다. Tejun Heo는 프로세스 트리 위에 “세션이나 프로그램 그룹처럼” 얹는 다른 구조를 선호했을지 모르지만, “그 배는 오래전에 떠났다”고 한탄했다. 하지만 1년쯤 더 전에, 그렇게 우울하지만은 않을 수도 있는 함의를 가진 일이 있었다.
2010년 말에 보도된 바와 같이, 프로세스 그룹을 제어하는 방법은 cgroups만 있는 것이 아니다. cgroups를 위해 개발된 그룹 스케줄링 지원을 활용해 Mike Galbraith는 스케줄링을 위해 프로세스를 자동으로 묶는 다른 메커니즘을 만들었다.
표준 Unix 스케줄러와 대부분의 후속 스케줄러는 프로세스에 공정하려 하지만, 공정성의 초점으로 프로세스가 최선인 것은 아니다. 내가 학생 때 사용하던 AUSAM Unix 변종(Australian Unix Share Accounting Method, 이는 SHARE II로 발전했다)에서는 공정성의 대상이 먼저 사용자였다. 그래서 한 학생이 (당시 로컬 제한인) 여섯 개 프로세스를 돌린다고 해서, 하나만 돌리는 다른 학생보다 더 많은 CPU 시간을 얻지 못했다. 현대 개발자 데스크톱에서는 “잡(job)”(잡 제어 의미에서 — 프로세스 그룹)이 아주 논리적인 묶음이다. 서로 다른 잡(브라우저, 게임, make-j 40)은 동등한 조건에서 경쟁하는 것이 합리적이고, 한 잡 내부의 프로세스나 스레드는 서로 경쟁하되, 개별 단위로 다른 스레드들과 경쟁하는 것은 합리적이지 않다.
프로세스 그룹 기반 자동 스케줄링에는, 자동 그룹 스케줄링의 역사를 기록한 메일링 리스트 스레드에서 제기된 두 가지 이슈가 있다. 이 이슈들은 매우 다른 사람들이 제기했고, 매우 다른 반응을 받았다.
첫 번째는 Linus Torvalds가 제기한 것으로, 프로세스 그룹은 이 목적에 너무 세밀한 단위일 수 있다는 제안이다. 새 스케줄링 그룹을 만드는 데는 비용이 있으니, 너무 자주 하면 받아들일 수 없는 느려짐을 초래할 수 있다는 것이다. 불행히도 그 비용을 측정했다는 기록은 없고(Linus가 어느 정도 독려했음에도), 무엇이 “너무 자주”인지도 모호했다 — 대략 “셸에서 명령을 호출할 때마다 하나”와 “초당 수만 번” 사이 어딘가.
이 주장은 사실상 도전받지 않았다. 최종 구현은 “프로세스 그룹”이 아니라 “세션”을 사용했는데, 세션은 확실히 덜 자주 생성된다. 하지만 이것이 정말 올바른 묶음처럼 보이진 않는다. 만약 다음을 실행해:
make -j 40 >& log &
프로젝트를 컴파일하고, 이어서
frozen-bubble
시간을 때우기 위해 — 둘 다 같은 터미널 창에서 — 실행한다면, 게임은 하나의 잡과 경쟁하는 것이 아니라 40개의 프로세스와 경쟁하게 된다.
스케줄러 그룹도 함께 생성될 때 fork()+exec()가 겪는 오버헤드는 비교적 쉽게 시험할 수 있다: /bin/env /bin/echo hello와 /bin/setsid /bin/echo hello는 정확히 같은 일을 하지만, 후자는 새 세션을 만들고 따라서 새 스케줄러 그룹을 만든다(둘 다 대화형 셸이 아니라 셸 스크립트에서 실행된다면):
time bash -c 'for i in {1..10000}; do /usr/bin/setsid /bin/echo hi ; done > /dev/null'
time bash -c 'for i in {1..10000}; do /usr/bin/env /bin/echo hi ; done > /dev/null'
둘의 차이는 분명 노이즈 수준이다.
두 번째 이슈는 Lennart Poettering이 제기한 것이다: “데스크톱에서는 이것이 완전히 무의미하다.” 이 주장이 나왔을 당시에는 상당 부분 사실이었다. 자동 그룹핑이 “controlling tty”를 기준으로 이뤄졌고, 대부분의 데스크톱 애플리케이션은 controlling tty가 없었기 때문이다. 비디오 편집기와 브라우저가 같은 스케줄링 그룹에 들어가게 되어, 한쪽의 다중 렌더링 스레드가 다른 쪽의 단일 스레드를 압도할 수 있었다. 논의가 끝날 즈음에는 또 다른 의미에서 다시 사실이 되었다. 자동 그룹핑은 이제 “세션”을 기준으로 이뤄졌고, 대부분의 데스크톱 세션 매니저는 각 애플리케이션을 서로 다른 세션에 넣지 않았다. 하지만 개발 중이던 한 세션 매니저는 그렇게 했다: systemd는 이미 요구되는 대로 setsid()를 사용했다.
Lennart의 코멘트가 좋게 받아들여지지 않았음에도 불구하고, 그는 당시 자동 그룹 스케줄링의 이점을 더 많은 사용자에게 쉽게 가져다줄 수 있는 소프트웨어를 작업 중이었다. 아무도 그 사실을 알아차리지 못했던 듯하다.
하지만 본 이야기로 돌아오면, 자동 그룹 스케줄링에서 얻을 핵심 교훈은 cgroups 노력이 스케줄러에 유용한 기능을 प्रेर(영감)했으며, 이 기능은 cgroups와 별개로도 사용할 수 있다는 점이다. 어떤 프로세스가( cpu 서브시스템 관점에서) 루트가 아닌 cgroup에 있으면, cgroups가 지시하는 대로 스케줄된다. 루트에 있으면(자동 그룹이 비활성화되지 않았다면) 자동 그룹에 따라 스케줄된다. 이것이 통합 계층이, 루트가 더 이상 리프가 아니더라도 프로세스가 계층의 루트에 남을 수 있게 하는 것이 긍정적인 이유다. 이는 cgroups와 병행하면서도 독립적인 자원 관리가 발전할 수 있는 길을 남기며, 같은 시스템에서 cgroups 관리와 비-cgroups 관리가 모두 이뤄질 수 있게 한다. 이로 인해 두 번째 도전으로 이어진다.
우리가 가진 것은 원래 cgroups 개발자들이 시작할 때 갖고 있지 못했던 것이다: 수년의 경험과 동작하는 코드. 이는 우리가 분명 이점으로 전환할 수 있어야 하는 자산이다. 그래서 자원 관리에 대한 새 이해를 시험하기 위한 도전은 이것이다: 스케줄링에서 자동 그룹에 영감을 받아, cgroups와 나란히 존재하지만 cgroups와는 독립적인 형태로 Linux에서 자원 관리와 프로세스 제어를 어떻게 구현하겠는가? 충분히 생각해 본 다음, 다시 돌아와 내 결과와 비교해 보면 된다. 걱정 마라, 당신이 끝낼 때까지 우리는 여전히 여기 있을 것이다.
대비는 사물을 더 명확히 보게 해주는 강력한 도구다. 그래서 내가 중요하다고 느낀 이슈들을 다른 맥락에 심어 제시했다. 그 기원을 반영하는 이름인 하인드사이트 그룹(hindsight groups)은, 때로는 요점을 위해 다르게 만들고, 때로는 그냥 다르기 위해 다르게 만든다. 하인드사이트 그룹은 초점이 있다: 오직 프로세스 그룹을 제한하는 것만 다룬다. 그 설명에 맞지 않는 어떤 필요도 다른 곳에서 자리를 찾아야 한다.
하인드사이트 그룹(또는 “hgroups”)에서 제어의 기본 단위는, 대화형 셸이나 systemd, 그리고 잠재적으로 다른 어떤 세션 매니저에 의해 생성되는 프로세스 그룹이다. prlimit() 같은 명령으로 개별 프로세스에 제어를 가하는 것은 여전히 가능하지만, 그룹 제어의 세분성은 프로세스 그룹보다 더 미세해지지 않는다.
이 프로세스 그룹들을 위한 관리 구조를 제공하기 위해 PID 계층에 새로운 레벨이 추가된다. 세션과 프로세스 그룹 위에 “프로세스 도메인(process domain)”이 도입된다. 프로세스는 처음에 도메인 0에 속한다. 도메인 0에 있고 자신의 세션과 프로세스 그룹에서 혼자인 프로세스는 set_domainid()를 호출해 도메인 0에 종속된 새 도메인을 시작할 수 있고, 그 결과 도메인의 2단계 계층이 생성된다. 새 PID 네임스페이스가 생성되면, 시작 프로세스를 포함하는 도메인이 새 네임스페이스에서 도메인 0으로 나타나며, 그 네임스페이스에서의 새 도메인들은 로컬 도메인 0에 종속되어, 다단계 계층을 형성한다.
도메인이 형성하는 계층은 프로세스를 강하게 제약한다. 도메인 안에 들어가면, 프로세스는 그 도메인에서 나올 수 없다. 각 도메인은 하나의 프로세스 그룹과 연관되는데 — 그것은 도메인을 만든 프로세스의 프로세스 그룹이다. 같은 도메인 안의 다른 모든 프로세스 그룹은 그 첫 번째 프로세스 그룹에 종속된 것으로 간주된다. 이는 사실상 모든 프로세스 그룹을 계층에 넣는다. 이는 분류 계층이라기보다 조직 계층에 가깝다. “로그인 세션”, “컨테이너”, “잡” 같은 구조적 묶음을 제공한다. 행동 방식보다는 수행하는 작업에 따라 프로세스를 모은다.
프로세스 그룹의 새롭고 더 강하게 정의된 역할과 함께, 프로세스 그룹당 할당되는 새로운 자료구조가 생긴다. 이는 프로세스당 할당되는 signal_struct와 비슷하다. 여기에는 그룹 내 프로세스에 적용되는 제한 집합이 들어 있다. 그중 일부 — 예컨대 디바이스 접근 제어 목록(devices cgroup 서브시스템이 제공하는 것과 유사) — 은 어떤 동작이 허용되는지 확인할 때마다 참조되며, cgroups가 구성되지 않았거나 “루트” cgroup만 제공할 때 사용된다. 다른 일부 — 예컨대 사용할 수 있는 CPU 집합 — 는 변경될 때마다 프로세스 그룹 안의 모든 프로세스와 스레드로 전파되어야 한다. 이는 모든 프로세스에 가상 시그널을 보내는 방식으로 일괄 수행된다(freezer cgroup 서브시스템이 취한 접근과 유사). 그 가상 시그널을 처리하는 동안, 프로세스는 프로세스 그룹의 제한에 기반해 로컬 상태를 갱신한다.
프로세스 그룹당 제한은, 적절한 사용자 ID를 갖거나 슈퍼유저 권한을 가진 어떤 프로세스든 변경할 수 있다. 하지만 프로세스는 자신의 프로세스 그룹이 가진 것과, 계층에서 자신 위의 모든 프로세스 그룹이 가진 것보다 더 많은 권한만 줄 수 있다(즉 제한을 줄일 수 있다). 변경은 커널에 의해 아래로 전파되지 않지만, 사용자 공간 도구가 제한 해제나 부과를 신뢰성 있게 전파할 수 있다.
중요한 제한 중 하나는 프로세스가 수행할 수 없는 특정 동작을 식별하고, 이를 시도하면 블록되게 한다. 이 제한의 한 설정은 cgroups freezer처럼 그룹의 모든 프로세스를 사실상 동결한다. 다른 설정은, 프로세스가 새 프로세스 그룹을 만들려고 할 때만 동결한다. 이는 도메인 내 모든 프로세스 그룹에 어떤 제한을 레이스 없이 부과하는 것을 가능하게 한다.
공유 자원들 — 메모리, CPU, 네트워크, 블록 I/O — 은 각각 고유한 요구가 있으며, 별도로 관리된다. 이들은 이러한 묶음과 계층의 이점을 누리지만, 그것에 묶여 있지는 않다.
네트워킹과 블록 I/O는 대체로 처리량을 상한으로 두거나 공유한다는 점에서 유사하다. 또한 비교적 쉽게 가상화할 수 있어, 하위 도메인에 실제 디바이스로 데이터를 라우팅하는 가상 디바이스 접근을 줄 수 있다. 관리해야 할 디바이스가 여러 개일 수도 있고, 관련된 프로세스 외의 관심사도 있다. 네트워크 시스템은 자체 링크 제어 트래픽과, 다른 인터페이스에서 포워딩된 트래픽까지 관리해야 할 수 있다. 블록 I/O 서브시스템은 이미 메타데이터(REQ_META 플래그 사용)와 다른 데이터를 내부적으로 구분하므로, 요청을 여러 방식으로 분류할 필요가 있다.
따라서 이 두 시스템은 자체 큐잉 관리 구조를 가지며, hgroups는 이를 알지 못한다. 다양한 큐잉 알고리즘은 요청을 발생 도메인에 따라 분류할 수도 있고, 개별 프로세스에 어떤 라벨링(cgroups 네트워크 클래스 서브시스템과 유사)을 지원할 수도 있지만, 그것은 hgroups의 관심 밖이다.
메모리 사용 관리는 다른 공유 자원들과 상당히 다르다. 다른 자원들은 시간에 비례해 측정되는 반면, 메모리는 시간보다 공간으로 측정된다. 다른 셋(네트워크, 블록 I/O, CPU)은 프로세스가 언제든 사용을 시작하거나 멈출 수 있고, 일시적으로 자원 사용을 금지해도 큰 부작용이 없다. 메모리는 일정한 비(非)사소한 기간 동안 지속적으로 उपलब्ध하지 않으면 자원으로서 쓸모가 없다.
이는 이전 편에서 보았듯, 메모리가 꽤 오래 지속되는 어떤 주체에 청구되어야 함을 의미한다. 또한 비례적 공유를 강제하기가 어렵다는 뜻이기도 하다. cgroups 메모리 컨트롤러는 두 가지 제한을 부과한다: 절대 넘을 수 없는 하드 제한과, 메모리가 아주 빡빡할 때만 적용되는 소프트 제한이다. 소프트 제한을 바꾼다고 공유 비율이 달라지는 것이 아니라, 메모리를 해제해야 할 때 어느 쪽이 얼마나 고통을 받는지가 달라진다.
지속성과 제한 부과라는 이 두 요구는 프로세스 도메인이 완벽히 충족하며, mem 서브시스템이 사용하는 계층에서 cgroups가 수행하는 역할과 매우 유사한 역할을 한다. 각 프로세스 그룹이 사용하는 메모리 자원은 포함 도메인에 청구되고, 포함 도메인이 또 다른 도메인에 포함된다면 그쪽에도 청구된다. 어떤 제한에 도달하면 할당은 실패하고 메모리 회수가 유발된다. 하드/소프트 제한은 cgroups와 마찬가지다.
권한 있는 프로세스는 하위 도메인의 어떤 프로세스 그룹에 대해서도 메모리 회계 처리를 리다이렉트하여, 그 프로세스 그룹 내부의 사용량이 다른 도메인에 청구되도록 할 수 있다. 예를 들어, 기본 hgroups 구조는 사용자를 인식하지 않지만, 특정 사용자에 속한 모든 도메인에 단일한 전체 메모리 제한이 적용되도록 만들 수 있다. 어쨌든, 메모리 자원 소유자를 식별하기에는 PID 번호(현재 24비트)면 충분하다. 이는 서로 다른 자원을 위해 두 개 또는 세 개의 식별자를 요청이나 메모리 페이지에 붙여 이후 처리가 올바르게 청구되도록 하는 것을 가능하게 한다.
CPU 처리량 제한은 메모리 할당 제한과 거의 같은 방식으로 부과된다. 유일한 차이는, 제한이 도메인뿐 아니라 로컬 프로세스 그룹에도 부과될 수 있다는 점이다. 제한은 충분한 권한을 가진 프로세스에 의해 올릴 수도 내릴 수도 있다.
CPU 스케줄링은 아마 자원 관리자 중 가장 복잡할 것이다. 스케줄링 그룹은 대략 도메인/프로세스-그룹/프로세스 계층을 따라 형성되지만, 각 레벨마다 그룹핑이 선택 사항이다. 도메인 0에 대한 그룹핑이 활성화되면, 각 도메인의 프로세스들이 그룹으로 묶이고 그 그룹들끼리 스케줄된다. 활성화되지 않으면, 각 도메인의 개별 프로세스 그룹이 서로 스케줄되어, 현재 자동 그룹과 매우 유사한 결과가 나온다. 메모리 자원과 마찬가지로, 권한 있는 프로세스는 어떤 프로세스 그룹이 다른 프로세스 그룹의 맥락에서 스케줄되도록 지시할 수 있다.
단일 사용자 시스템에서는 도메인 스케줄링이 비활성화되고, 최상위 스케줄링은 프로세스 그룹 간에 이뤄질 가능성이 크다. 다중 사용자 시스템에서는 도메인 레벨 스케줄링의 추가 비용이 아마 정당화될 것이다. 컨테이너 내부에서도, 같은 선택을 각 컨테이너에서 독립적으로 할 수 있다.
각 레벨에서 CPU 스케줄링을 독립적으로 활성화할 수 있다는 점은, 통합 계층이 각 레벨에서 다른 서브시스템을 선택적으로 활성화하는 접근과 약간 비슷하다. 다만 더 일반적이다. 활성화된 레벨들의 집합이 연속일 필요가 없기 때문이다.
hgroups CPU 스케줄링은 cgroups와 자동 그룹 모두와 또 다른 중요한 차이가 있다. 자동 그룹 스케줄링의 문제 중 하나는, nice를 사용해 프로그램을 낮은 우선순위로 실행하려 할 때의 효과를 바꿔버린다는 점이다. nice가 더는 제대로 동작하지 않는다는 사실은 보고되기도 했지만 아직 고쳐지지 않았다. 하지만 어떤 회귀는 다른 회귀보다 덜 중요하다고 여겨지는 듯하며, 어쩌면 적절한 포럼에 보고되지 않았을 수도 있다.
문제는 각 스케줄링 그룹이, 그룹 안의 프로세스들과는 독립적인 우선순위를 가진다는 점이다. 어떤 프로세스의 nice 값을 설정하면, 같은 그룹(자동 그룹에서는 같은 세션) 안의 프로세스들에게만 양보하게 된다. 사용자가 여러 세션을 갖는다면(그것이 자동 그룹의 요지다) 세션들끼리 서로에게 양보하기가 쉽지 않다.
hgroups는 우선순위를 설정하는 사업을 하지 않고, 제한을 부과하는 것만 한다. 프로세스 그룹에 부과하는 제한은 그 그룹의 우선순위 weight에 대한 상한을 두는 것이다. 그러면 그룹의 유효 weight는, 활성 멤버들의 weight 합(또는 아마 최댓값)으로 되되, 상한을 넘지 않는 범위에서만 유효하다. 이는 낮은 우선순위 프로세스가 같은 스케줄링 그룹 안의 프로세스들뿐 아니라 다른 모든 사용자에게도 진정으로 양보하게 한다. 낮은 우선순위 프로세스가 없을 때는 현재 방식과 거의 같게 동작한다.
이 모험은 확실히 매우 교육적이었고, 함께해 준 당신에게 감사한다. 깊은 이해라는 목표는 달성했지만, 오락성 향상이라는 목표까지 달성할지는 아직 모르겠다. cgroups 이야기의 다음 장이 드러날 때, 나는 들뜨거나 실망할 준비가 되어 있다; 황홀하거나 역겨울 준비가 되어 있다; 도전받거나 확인받을 준비가 되어 있다. 하지만 내가 기대하지 않는 한 가지가 있다면, 지루함이다.
| 이 글의 인덱스 항목 |
|---|
| Kernel |
| GuestArticles |