클래식 cgroup 계층의 구조와 선택의 문제, 네트워크 트래픽 제어 계층과의 비교, 그리고 리소스 컨트롤러 계층 분리에 따른 함의를 살펴본다.
LWN.net에 도움이 필요합니다! 구독자가 없다면 LWN은 존재할 수 없습니다. 구독 신청을 고려하시고 LWN이 계속 발행될 수 있도록 도와주세요.
이전 기사들에서는 일반적인 계층과, 특정 cgroup 서브시스템에서 계층을 어떻게 다루는지를 살펴보았다. 이제는 이 모든 내용을 한데 모아, 어떤 종류의 계층(또는 여러 계층)이 필요하며 현재 구현에서 이를 어떻게 지원할 수 있는지 이해해 볼 차례다. 최근 보도된 바와 같이, Linux 커널 3.16에는 이른바 "통합 계층(unified hierarchy)"에 대한 개발 중 지원이 들어갈 예정이다. 그 개발에서 도입된 새로운 아이디어들은 아직 논하지 않을 텐데, 우리가 가진 것을 완전히 이해하기 전에는 그것들이 어떤 가치를 가져올지 제대로 평가할 수 없기 때문이다. 뒤의 기사에서 통합 계층을 풀어보겠지만, 지금은 먼저 "클래식" cgroup 계층이라 부를 만한 것을 이해하는 것부터 시작하자.
궁극적으로는 폐기(deprecate)될 수도 있으나 여전히 완전히 지원되는 클래식 모드에서는, 서로 분리된 여러 cgroup 계층이 존재할 수 있다. 각 계층은 처음에 루트 cgroup으로 시작하며, 초기에는 모든 프로세스를 담고 있다. 이 루트 노드는 "cgroup" 가상 파일시스템의 인스턴스를 마운트함으로써 생성되며, 이후 계층에 대한 모든 변경은 이 파일시스템을 조작하여 이루어진다. 특히 cgroup을 만들기 위한 mkdir, cgroup을 제거하기 위한 rmdir, 같은 부모 아래에서 cgroup 이름을 바꾸기 위한 mv 등이 사용된다. cgroup이 만들어지고 나면, 특수 파일에 프로세스 ID 숫자를 기록함으로써 프로세스를 cgroup 사이로 이동시킬 수 있다. 적절한 권한을 가진 사용자가 어떤 cgroup의 cgroup.procs에 PID 번호를 기록하면, 해당 프로세스는 현재 속해 있는 cgroup에서 대상 cgroup으로 이동한다.
이는 계층을 조작하는 매우 "조직적인" 방식이다. 새 그룹을 만들고, 누군가를 찾아 그 그룹을 채워 넣는 식이다. 파일시스템 기반 계층에서는 자연스러워 보일 수 있지만, 모든 계층을 조작하는 최선의 방식이라고 가정해서는 안 된다. 우리가 4.4BSD에서 본 세션과 프로세스 그룹의 단순한 계층은 완전히 다르게 동작한다. 거기에는 그룹을 만드는 것과 그 그룹에 첫 프로세스를 넣는 것 사이의 구분이 없다.
조작 메커니즘을 평가할 때는 목적 적합성뿐 아니라 구현의 편의성도 고려해야 한다. 목표가 계층 내부의 프로세스를 허용하는 것이라면 이 메커니즘은 꽤 적합하다. 반대로 모든 프로세스를 리프에만 두는 편을 선호한다면, 그다지 잘 맞지 않아 보인다.
계층이 생성되면 고정된 cgroup 서브시스템 집합과 연결된다. 이 집합은 바꿀 수 있지만, 루트 아래에 하위 그룹이 전혀 없을 때만 가능하므로 실용적으로는 대부분 고정이라고 보면 된다. 각 서브시스템은 최대 하나의 계층에만 붙을 수 있으므로, 어떤 특정 서브시스템의 관점에서는 계층이 하나뿐이다. 하지만 다른 서브시스템들이 무엇을 보게 될지는 아무 가정도 할 수 없다.
따라서 12개 서브시스템 각각에 대해 12개의 서로 다른 계층을 가질 수도 있고, 12개 서브시스템 모두가 붙은 단일 계층을 가질 수도 있으며, 그 사이의 어떤 조합도 가능하다. 또한, 연결된 서브시스템이 0개인 계층을 임의의 개수로 만들 수도 있다. 그런 계층은 다양한 cgroup 내 프로세스에 대해 어떤 제어도 할 수 없지만, 관련된 프로세스 집합을 추적하는 것은 가능하다.
systemd는 이 기능을 활용하여 컨트롤러 서브시스템이 없는 cgroup 트리를 /sys/fs/cgroup/systemd에 마운트해서 만든다. 여기에는 로그인 세션에서 비롯된 프로세스들을 먼저 사용자별로, 다음으로 세션별로 분류하는 user.slice 하위 계층이 포함된다. 따라서:
/sys/fs/cgroup/systemd/user.slice/user-1000.slice/session-1.scope
는 UID 1000인 사용자의 첫 번째 로그인 세션과 연관된 모든 프로세스를 담는 cgroup을 나타낸다(slice와 scope는 systemd에 특화된 용어다).
이러한 "세션 스코프(session scope)"는 V7 Unix의 원래 프로세스 그룹이 가졌던 가치 중 하나—어떤 프로세스가 어떤 로그인 세션에 속하는지를 명확히 식별하는 것—를 되살리는 듯하다. 단일 사용자 데스크톱에서는 그다지 흥미롭지 않을 수도 있지만, 더 큰 다중 사용자 머신에서는 꽤 가치가 있을 수 있다. 그룹에 대한 직접적인 제어는 불가능하지만, cgroup.procs 파일을 보면 (있다면) 정확히 어떤 프로세스들이 들어 있는지 알 수 있다. 프로세스들이 너무 빠르게 fork하지 않는다면, 다음과 같은 방식으로 모든 프로세스에 시그널을 보낼 수도 있다:
kill $(cat cgroup.procs)
클래식 계층 접근 방식에서 아마도 가장 큰 단일 문제는 선택의 폭정이다. 서브시스템들을 결합하는 방식에 많은 유연성이 있는 것처럼 보인다. 어떤 것은 한 계층에, 어떤 것은 다른 계층에, 또 어떤 것은 세 번째 계층에는 아예 없게 하는 식이다. 문제는 이 선택이 한 번 정해지면 시스템 전체에 영향을 미치고, 바꾸기 어렵다는 점이다. 어떤 요구는 특정한 서브시스템 배치를 시사하는데, 다른 요구는 다른 배치를 시사한다면, 같은 호스트에서 두 요구를 동시에 만족시킬 수 없다. 이는 특히 컨테이너를 사용하여 한 호스트에서 별도의 관리 도메인을 지원할 때 문제가 된다. 모든 관리 도메인은 cgroup 서브시스템과 계층의 동일한 연결 관계를 보아야 한다.
이는 표준에 합의할 필요가 있음을 시사한다. 자명한 선택지는 단일 계층을 두는 것(이는 "통합 계층" 접근이 향하는 방향으로 보인다) 또는 각 서브시스템마다 별도의 계층을 두는 것이다(내 openSUSE 13.1 노트북에서는 사실상 기본에 가깝다. cpu와 cpuacct만 결합되어 있다). 지금까지 cgroup 서브시스템에 대해 배운 것을 바탕으로, 서브시스템을 분리하거나 함께 둘 때의 함의 일부를 이해할 수 있을 것이다.
앞서 보았듯, 특히 3부에서, 꽤 많은 서브시스템은 어떤 계정(accounting)도 수행하지 않거나, 수행하더라도 그 계정을 어떤 제어를 부과하는 데 사용하지 않는다. 해당 서브시스템은 debug, net_cl, net_perf, device, freezer, perf_event, cpuset, cpuacct이다. 이들 중 어느 것도 계층을 매우 강하게 활용하지 않으며, 거의 모든 경우 계층이 제공하는 기능은 별도로도 달성할 수 있다.
좋은 예가 perf_event 서브시스템과 그것과 함께 동작하는 perf 프로그램이다. perf 도구는 여러 프로세스의 성능 데이터를 수집할 수 있고, 그 프로세스들을 선택하는 다양한 방법을 제공하는데 그중 하나가 UID를 지정하는 것이다. UID가 주어졌을 때 perf는 단지 이를 커널에 전달해 매칭되는 모든 프로세스를 요청하는 것이 아니라, /proc 파일시스템에 나열된 모든 프로세스를 검사해 해당 UID를 가진 프로세스를 선택한 뒤, 각 프로세스를 독립적으로 모니터링하도록 커널에 요청한다.
perf_event 서브시스템이 계층을 사용하는 유일한 목적은 하위 그룹들을 더 큰 그룹으로 모으는 것으로, perf가 하나의 더 큰 그룹만 식별하여 그 아래의 모든 그룹에 속한 모든 프로세스의 데이터를 수집할 수 있게 하는 것이다. 하지만 perf가 관심 있는 모든 리프 그룹들을(그것들이 단일 큰 그룹 아래에 있든 아니든) UID 기반 프로세스 선택과 유사한 방식으로 식별하게 하면 동일한 효과를 얻을 수 있으므로, 여기서 계층은 사실 사소한 편의에 불과하며 중요한 기능은 아니다. 비슷한 이유로, 나열한 다른 서브시스템들도 계층 없이도 쉽게 동작할 수 있다.
이 서브시스템들 중 계층 사용이 그렇게 쉽게 치부될 수 없는 경우가 두 가지 있다. 첫 번째는 cpuset 서브시스템이다. 이 서브시스템은 비상 상황에서 사용할 추가 리소스를 찾기 위해 때때로 계층을 위로 올라가며 살펴본다. 이는 계층에 대한 본질적인 의존성이다. 이 서브시스템을 처음 살펴볼 때 언급했듯, 비슷한 기능은 계층에 의존하지 않고도 쉽게 제공할 수 있으므로, 이는 작은 예외다.
다른 하나는 devices 서브시스템에서 가장 분명하다. 이는 부과되는 제어가 아니라 허용되는 설정과 관련이 있다. 하위 그룹은 부모가 거부한 접근을 허용할 수 없다. 이러한 계층 사용은 프로세스 분류라기보다는 관리적 제어를 위한 것이다. 상위 레벨이 하위 레벨이 따라야 하는 정책을 설정할 수 있게 해준다. 관리 계층은 권한을 분산하는 데 매우 효과적일 수 있는데, 사용자 그룹이든 개별 사용자든, 자체 사용자 집합을 가질 수 있는 컨테이너든 마찬가지다. (아마도 systemd가 기본으로 제공하는 계층을 기반으로 한) 단일 관리 계층을 갖는 것은 매우 자연스러운 선택이며, 이러한 비계정형 서브시스템들 모두에 잘 맞을 것이다. 이들 중 어느 것이라도 분리해서 유지하는 것은 정당화하기 어렵다.
남은 서브시스템들, 즉 "리소스 컨트롤러"라는 용어가 가장 어울리는 것들은 메모리(hugetlb 포함), CPU, 블록 I/O 리소스를 관리한다. 이들을 이해하기 위해 잠시 우회하여 네트워크 리소스가 어떻게 관리되는지 살펴보자.
네트워크 트래픽은 리소스 공유와 사용량 스로틀링의 이점을 분명히 누릴 수 있지만, 서로 다른 cgroup 서브시스템을 탐색하는 과정에서는 네트워크 리소스 제어의 증거를 보지 못했다. 적어도 블록 I/O와 CPU 리소스에서 본 것과 같은 방식의 제어는 없었다. 이는 특히, 이전 기사에서 언급된 것처럼 다중 계층을 정당화하는 문서화된 근거 중 하나가 예컨대 CPU 리소스와는 별개로 네트워크 리소스를 관리할 신뢰할 만한 필요가 있을 수 있다는 점이기 때문에, 더 관련이 깊다.
사실 네트워크 트래픽은 별도의 계층으로 관리되며, 이 계층은 cgroups와도 분리되어 있다. 이를 이해하려면 Network Traffic Control(NTC)에 대한 최소한의 소개가 필요하다. NTC 메커니즘은 tc 프로그램으로 관리된다. 이 도구는 각 네트워크 인터페이스에 "큐잉 규율(queueing discipline)"(또는 "qdisc")을 붙일 수 있다. 일부 qdisc는 "클래스형(classful)"이며, 이런 qdisc는 그 아래에 다른 qdisc를 붙일 수 있는데 패킷의 각 "클래스"마다 하나씩이다. 이차 qdisc 중 어떤 것이 또 클래스형이라면, 그 아래로 추가 레벨이 가능하고, 이런 식으로 계속된다. 이는 qdisc의 계층이 존재할 수 있음을 의미하며, 또는 네트워크 인터페이스마다 하나씩 여러 계층이 있을 수도 있다.
tc 프로그램은 또한 "필터"를 구성할 수 있게 해준다. 이 필터들은 네트워크 패킷이 서로 다른 클래스(따라서 서로 다른 큐)에 어떻게 할당되는지를 안내한다. 필터는 패킷 내부의 바이트, 패킷에 사용된 프로토콜 등 다양한 값을 기준으로 할 수 있는데, 현재 논의에서 중요한 것은 패킷을 생성한 소켓이다. net_cl cgroup 서브시스템은 각 cgroup에 "클래스 ID"를 할당할 수 있고, 이 값은 그 cgroup의 프로세스가 생성한 소켓에 상속된다. 이 클래스 ID는 패킷을 서로 다른 네트워크 큐로 분류하는 데 사용된다.
각 패킷은 다양한 필터에 의해 트리 내 큐들 중 하나로 분류된 뒤 루트로 전파되며, 그 과정에서 스로틀링(예: Token Bucket Filter, tbf, qdisc)되거나 경쟁적으로 스케줄링(예: Stochastic Fair Queueing, sfq, qdisc)될 수 있다. 루트에 도달하면 전송된다.
이 예시는 리소스의 스케줄링과 스로틀링을 관리하기 위해 계층, 심지어 별도 계층을 갖는 것이 갖는 가치를 강조한다. 또한 이것은 별도의 cgroup 계층일 필요가 없다는 점도 보여준다. 리소스 로컬 계층이 요구에 완벽히 부합할 수 있으며, 그 경우 별도의 cgroup 계층은 필요하지 않다.
CPU, 메모리, 블록 I/O, 네트워크 I/O를 위한 주요 리소스 컨트롤러 각각은 자기 리소스를 관리하기 위해 별도의 계층을 유지한다. 앞의 세 가지는 cgroups를 통해 관리되지만, 네트워킹은 별도로 관리된다. 이 관찰은 여기에는 두 가지 서로 다른 종류의 계층이 있는 것처럼 보일 수 있음을 시사한다. 어떤 것은 리소스를 추적하기 위한 것이고, 어떤 것은 (아마도 하나의 "관리 계층") 프로세스를 추적하기 위한 것일 수 있다.
Documentation/cgroups/cgroups.txt의 예시는 프로세스를 추적하는 단일 계층의 가능성을 인정하는 듯하지만, 그것이 "... cgroup의 확산(proliferation)"으로 이어질 수 있다고 우려한다. 앞서 설명한 systemd 계층에 net_cl 서브시스템을 포함한다면, 필요한 네트워크 클래스들에 맞춰 각 세션마다 여러 하위 cgroup을 만들 필요가 생길 수 있다. 다른 서브시스템(예: cpu 또는 blkio)이 각 세션 내에서 다른 분류를 원한다면, cgroup의 조합 폭발(combinatorial explosion)이 발생할 수 있다. 이것이 실제로 문제인지 여부는 내부 구현 세부사항에 달려 있으므로, 정확히 그 주제를 다룰 다음 기사까지는 추가 논의를 미루겠다.
NTC 계층에서는 분명하지 않은 cgroups 계층의 한 가지 특징은 컨테이너를 사용할 때 계층의 일부를 별도의 관리 도메인에 위임할 수 있다는 점이다. 어떤 컨테이너의 네임스페이스에서 cgroup 계층의 서브트리만 마운트하면, 컨테이너는 그 서브트리에만 영향을 미치도록 제한된다. 그러나 그러한 컨테이너는 서로 다른 cgroup에 어떤 클래스 ID를 할당할 수 있는지에 대해서는 제한되지 않을 것이다. 이는 의도된 격리를 우회하는 것처럼 보일 수 있다.
네트워킹에서는 가상화와 간접화로 이 문제가 해결된다. 컨테이너에 "veth" 가상 네트워크 인터페이스를 제공하면 컨테이너는 그것을 원하는 대로 구성할 수 있다. 컨테이너에서 나오는 트래픽은 실제 인터페이스로 라우팅되고, 어떤 컨테이너에서 왔는지에 따라 분류될 수 있다. 비슷한 방식은 블록 I/O에도 작동할 수 있지만, CPU나 메모리 리소스 관리는 완전한 KVM 같은 가상화 없이는 같은 효과를 낼 수 없다. 이런 것들은 cgroups가 제공하는 명시적 서브-마운트 지원 같은, 관리 위임을 위한 다른 접근을 필요로 한다.
지난번에 언급했듯, 계정형 리소스 컨트롤러는 레이트 리미팅을 효과적으로 부과하기 위해 cgroup의 조상(ancestor)을 볼 수 있어야 하고, 공정한 공유를 구현하기 위해 형제(sibling)들도 볼 수 있어야 하므로, 이들 서브시스템에서는 전체 계층이 정말로 중요하다.
NTC를 예로 들면, 이러한 계층들은 리소스마다 분리되어야 한다고 주장할 수도 있다. NTC는 인터페이스마다 별도의 계층을 허용함으로써 cgroups보다도 한 단계 더 나아간다. blkio는 서로 다른 블록 디바이스(스왑 vs 데이터베이스 vs 로깅)에 대해 서로 다른 스케줄링 구조를 원할 수도 있지만, cgroups는 이를 지원하지 않는다.
그러나 (일부에 따르면) 마이크로커널이 주장하는 리소스 관리의 분리에서 비용이 있듯이, 리소스 제어의 과도한 분리에도 비용이 있다. 그 비용은 Tejun Heo가 통합 계층의 정당화의 일부로 지목한 "효과적인 협력(effective co-operation)"의 부재다.
프로세스가 파일에 쓰면 데이터는 먼저 페이지 캐시에 들어가며, 그로 인해 메모리를 소비한다. 이후 어떤 시점에 그 메모리는 스토리지로 기록되며, 그 과정에서 블록 I/O 대역폭 또는 경우에 따라 네트워크 대역폭을 소비한다. 따라서 이 서브시스템들은 완전히 분리되어 있지 않다.
메모리가 기록될 때, 그것은 처음 데이터를 쓴 프로세스가 쓰지 않을 가능성이 높고, 심지어 같은 cgroup 안의 다른 프로세스가 쓰는 것도 아닐 수 있다. 그렇다면 이 블록 I/O 사용량을 어떻게 정확히 계정 처리할 수 있을까?
메모리 cgroup 서브시스템은 각 메모리 페이지에 추가 정보를 붙여, 페이지가 해제될 때 어디로 환불(refund)을 보내야 하는지 알 수 있게 한다. 그렇다면 그 페이지가 결국 기록될 때 I/O 사용량도 동일한 cgroup에 계정 처리할 수 있을 듯하지만, 한 가지 문제가 있다. 그 cgroup은 메모리 서브시스템과 연관되어 있으며, 완전히 다른 계층에 있을 수도 있다. 메모리 계정 처리에 사용되는 cgroup은 blkio 서브시스템에게는 의미가 없을 수 있다.
이 단절은 몇 가지 방법으로 해결할 수 있다:
각 페이지마다 프로세스 ID를 기록하고, 이를 사용해 메모리 사용량과 블록 I/O 사용량 모두에 대해 어떤 대상에 청구할지 식별한다. 두 서브시스템 모두 PID를 이해하기 때문이다. 한 가지 문제는 프로세스가 매우 짧게 살 수 있다는 점이다. 프로세스가 종료할 때, 미결 리소스 청구(outstanding resource charges)를 다른 프로세스나 cgroup으로 이전하거나, 아니면 그냥 버려야 한다. 이는 CPU 스케줄러에서 보았던 문제—프로세스에만 계정 처리하면 프로세스 그룹에 대해 적절한 공정성을 쉽게 얻지 못한다—와 유사하다. 미결 청구를 효율적으로 보존하는 것은 도전이 될 수 있다.
임의로 긴 시간 동안 안전하게 살아 있을 수 있고, 여러 프로세스와 연관될 수 있으며, 서로 다른 cgroup 서브시스템 각각에서 사용할 수 있는 다른 식별자를 고안한다. 이는 사실상 속담처럼 컴퓨터 과학의 어떤 문제든 해결할 수 있다는 "추가 간접화 수준(extra level of indirection)"이다.
net_cl 서브시스템과 NTC를 연결하는 클래스 ID가 그런 식별자의 예다. 인터페이스마다 여러 계층이 있을 수 있지만, 클래스 ID 식별자 네임스페이스는 단 하나뿐이다.
각 페이지에 여러 식별자를 저장한다. 하나는 메모리 사용량을 위한 것이고, 다른 하나는 I/O 처리량을 위한 것이다.
메모리 컨트롤러가 페이지별 추가 정보를 저장하는 데 사용하는 struct page_cgroup 구조체는 현재 64bit 시스템에서 페이지당 128비트 비용이 든다—소유 cgroup에 대한 포인터 64비트와 플래그 64비트이며, 그중 정의된 플래그는 3개다. 포인터 대신 배열 인덱스를 사용할 수 있고, 10억 개 그룹이면 충분하다고 본다면, 두 개의 인덱스와 추가 비트 하나를 절반 공간에 저장할 수 있다. 인덱스를 충분히 효율적으로 사용할 수 있는지는 관심 있는 독자에게 남겨 둔 또 다른 연습 문제다.
이 문제에 대한 좋은 해법은 다른 상황에도 적용될 수 있다. 한 프로세스가 다른 프로세스를 대신하여 리소스를 소비하는 모든 곳에서 말이다. Linux의 md RAID 드라이버는 종종 요청을 시작한 프로세스의 컨텍스트에서 I/O 요청을 직접 하위 디바이스로 전달한다. 다른 경우에는 어떤 작업을 헬퍼 프로세스가 수행한 뒤 요청을 제출해야 한다. 현재는 그 작업을 수행하는 CPU 시간과 요청이 소비하는 I/O 처리량이 요청을 시작한 프로세스가 아니라 md에 청구된다. 어떤 "소비자(consumer)" 식별자(또는 식별자들)를 각 I/O 요청에 붙일 수 있다면, md 및 유사한 다른 드라이버들이 리소스 청구를 그에 맞게 배분할 기회를 가질 수 있을 것이다.
불행히도, 현재 구현에는 이 문제에 대한 좋은 해법이 없다. 과도한 분리에 비용이 있기는 하지만, 그 비용은 단지 모든 서브시스템을 같은 계층에 붙인다고 해서 완화될 수는 없다.
현재 구현에서는 계정형 서브시스템 cpu, blkio, memory, hugetlb는 별도의 계층에 두어 네트워킹이 NTC 덕분에 이미 별도의 계층을 가진다는 점을 인정하고, 비계정형 서브시스템들은 모두 하나의 관리 계층에 모아 두는 것이 최선으로 보인다. 필요할 때 분리된 cgroup들을 효과적으로 결합하려면 영리한 도구에 의존해야 할 것이다.
이제 우리는 이 시리즈의 앞선 기사에서 제기되었던 몇 가지 이슈에 답할 위치에 와 있다. 하나는 그룹이 어떻게 이름 붙여지는가 하는 질문이었다. 위에서 보았듯, 이는 mkdir 명령을 시작하는 어떤 프로세스가 책임진다. 이는 프로세스가 setsid() 또는 setpgid(0,0)을 호출할 때 커널이 (거의) 임의의 방식으로 이름을 부여하는 잡 제어(job control) 프로세스 그룹 및 세션과 대조된다. 차이는 미묘할 수 있지만, 기대되는 권한 구조에 대해 무언가를 말해주는 듯하다. 잡 제어 프로세스 그룹에서는 새 그룹을 형성하기로 하는 결정이 새 그룹의 구성원 내부에서 나온다. 반면 cgroups에서는 그 결정이 외부에서 오리라 기대된다. 앞서 cgroups 계층에 관리 계층을 구현하는 것이 매우 말이 된다고 관찰했다. 이름이 외부에서 할당된다는 사실은 그 관찰과도 부합한다.
또 다른 이슈는 한 그룹에서 다른 그룹으로 탈출할 수 있는지 여부였다. 프로세스를 이동시키는 것은 cgroup 파일시스템의 파일에 프로세스 ID를 쓰는 것을 포함하므로, 정상적인 파일시스템 접근 검사로 그 파일에 대한 쓰기 권한이 있는 어떤 프로세스든 이를 수행할 수 있다. PID가 그 파일에 기록될 때, 추가로 쓰기를 수행하는 프로세스의 소유자가 추가되는 프로세스의 소유자이기도 하거나, 또는 특권을 가졌는지를 검사한다. 이는 어떤 사용자든 cgroup.procs에 대한 쓰기 권한이 있는 어떤 그룹으로든 자신의 어떤 프로세스라도 이동시킬 수 있음을 의미하며, 그 과정에서 계층의 얼마나 많은 부분을 가로지르는지는 상관없다.
다르게 말하면, 프로세스가 어디로 이동되는지 _to_는 제한할 수 있지만, 어디에서 이동되는지 _from_에 대해서는 제어가 훨씬 적다. cgroup은 그 안의 모든 프로세스 소유자들이 그 밖의 어떤 cgroup으로도 프로세스를 옮길 수 없도록 막혀 있을 때만 "닫혀(closed)" 있다고 볼 수 있다. 앞서 보았던 계층 조작과 마찬가지로, 이는 cgroup 계층을 파일시스템처럼 생각하면 어느 정도 말이 되지만, 분류 체계로 생각하면 그렇게까지는 아니다.
이 논의에서 나온 가장 큰 질문은 서로 다른 리소스들을 서로 다른 계층을 사용해 관리할 진정한 필요가 있는지 여부다. NTC가 제공하는 유연성은 필요를 한참 넘어선 것인가, 아니면 다른 것들이 따라야 할 가치 있는 모델을 설정하는 것인가? 부차적인 질문은 단일 계층에 상이한 요구가 부과될 때 조합 폭발이 발생할 가능성과, 그 비용이 가치에 비해 불균형한지 여부다. 어느 경우든, 어떤 서비스 프로세스가 여러 리소스 중 일부를 소비하게 되는 요청의 기원(originator)에게 올바르게 청구하는 방법에 대한 명확한 이해가 필요하다.
이 질문들 중 가운데 질문이 아마도 가장 쉽다. 즉, 여러 cgroup을 갖는 것의 구현 비용이 정확히 무엇인가? 그래서 다음번에는 바로 이 주제로 향해, 내부에서 모든 것을 엮는 다양한 데이터 구조를 살펴볼 것이다.
| 이 글의 인덱스 항목 |
|---|
| Kernel |
| GuestArticles |