 |  |
|
난이도 : 중급 Christian Kaiser, 리서치 인턴, IBM Christian Rund, 연구 개발 엔지니어, IBM
옮긴이: 박재호 이해영 dwkorea@kr.ibm.com
2008 년 5 월 06 일 이 연재는 하드웨어 자원에 초점을 맞춘 컨테이너 가상화(또는 운영체제 가상화)로 알려진 소프트웨어 가상화 형태와 오픈 소스 프로젝트인 OpenVZ를 설명합니다. 이 연재물은 소프트웨어 방법론을 통해 셀/B.E. 프로세서 가상화에 필요한 모든 컴포넌트와 기법을 상세하게 개괄합니다. 두 번째 기사는 연재물 Part 1에서 설명한 전용 가상화 구현 방법과 파티셔닝에 대한 세부 사항을 다룹니다.
이 기사는 첫 번째 기사(Part 1, 그림 3)에서 예시한 전용 가상화(파티셔닝) 개념을 구현하는 방법을 소개한다. 이 기사는 (Part 1 그림 4에 나오는) 공유 디바이스 개념 개발은 설명하지 않는다.
시스템 구현을 보여주기 위해 이 기사는 다음 내용을 다룬다.
- spufs 가상화: 각 마운트 지점에 다른 루트 아이노드를 사용
- sysfs 조정: 실행 중에 반드시 변경해야 하는 항목
- SPU 스케줄러 변경: 가상화 실행을 위한 단계 추가
- OpenVZ 도구 변경: 새로운 기능 활용
spufs 가상화
기본적으로 spufs는 마운트할 때마다 매번 같은 root-inode를 사용한다. 예를 들어, chroot로 환경을 두 개 구축한다면(A와 B), 둘 다 /spu에 spufs를 마운트한다. 환경 A에서 SPE 스레드를 만들고 /spu 목록을 열거하면, 직전에 생성한 SPE 스레드가 보인다. 하지만 환경 B에서도 동일한 /spu 목록이 보인다. 환경 A와 똑같은 root-inode에 접근하기 때문이다.
이런 행동 양식을 변경해 양쪽 환경 모두 각자 생성한 SPE 스레드만 볼 수 있게 하려면, spufs가 항상 매번 마운트 지점에 대해 동일한 root-inode를 사용하지 않도록 변경해야 한다. spufs를 마운트할 때, 내부적으로 슈퍼 블록을 반환하는 커널 함수를 호출한다. 일반적으로 함수 get_sb_single()을 사용하는데, 항상 같은 슈퍼 블록을 반환한다. get_sb_nodev()는 항상 다른 슈퍼 블록을 반환하므로 이게 바로 우리가 바라는 동작 방식이다(그림 1은 가상화 직전과 직후에 spufs를 보여준다).
그림 1. 가상화 직전과 직후 spufs
OpenVZ는 컨테이너 내부에 마운트 가능한 파일 시스템을 몇 종류만 허용한다. 파일 시스템마다 파일에 밀접한 특성을 정의하는 struct file_system_type 인스턴스를 구현한다. OpenVZ는 (다른 구조체 중에서도) 이 구조체를 확장해 파일 시스템이 마운트가 가능한지를 알려주는 멤버를 정의한다. 그래서 .fs_flags 멤버는 FS_VIRTUALIZED로 설정해야 한다.
컨테이너 내부에 생성되는 프로세스는 PID가 둘이다. (Part 1에서 각 컨테이너마다 커널이 제공하는 독자적인 자원 집합이 있다고 설명했다. 프로세스 트리는 자원 집합 중 하나다. 컨테이너는 자신이 만든 가상화된 PID가 달린 프로세스 집합만 볼 수 있다.) 컨테이너 내부에는 가상화된 PID가 있으며, 호스트 시스템에는 동일한 프로세스가 전역 PID라는 다른 PID로 나타난다. 언급한 바와 같이 spufs 디렉터리는 spethread-<PID>-<thread-ID> 형태로 이름을 붙이는데, <PID>는 SPE 스레드를 담은 프로세스 ID이며, <thread-ID>는 해당하는 스레드 ID다. 컨테이너 환경에서 가상화된 ID의 결과로 컨테이너 내부의 spufs 목록은 전역 PID가 아니라 가상화된 PID를 보여줘야만 한다. 다행스럽게 이런 기능은 이미 OpenVZ에 구현되어 있다.
리눅스(Linux®) 커널에서 arch/powerpc/platforms/cell/spufs/inode.c 파일을 변경하자.
sysfs 조정하기
sysfs는 이미 가상화되어 있기에 컨테이너 내부에서 사용할 수 있다. 호스트 시스템에 보이는 내용은 sysfs 복사본이 아니라 일부에 불과하다. sysfs의 목표는 실행하는 동안에 SPU를 할당하고 해제하는 데 있다. sysfs 항목은 실행하는 동안에 수정이 일어나야 함을 의미한다. 수정에 앞서 SPU 목록을 열거한 디렉터리를 생성한다.
이 디렉터리는 /sys/devices/system/spu다. 기본적으로 컨테이너 환경에 있는 sysfs에는 /sys/devices/system/spu도, /sys/devices/system도, /sys/devices도 존재하지 않는다. 이 세 항목은 컨테이너 초기화(시동 과정) 과정에서 생성되어야 한다. /sys/devices/system/spu의 하위 디렉터리는 spu0에서 <spu>이 될 수 있는데, 여기서 <N>은 시스템에 존재하는 사용 가능한 SPU 수에서 1을 뺀 값이다. 디렉터리는 SPU가 컨테이너에 할당될 때 생성되어야 하며, 컨테이너에서 할당이 해제될 때 삭제되어야 한다.
sysfs 목록에서 각 디렉터리는 커널 영역에서 대응하는 kobject 인스턴스를 포함한다. kobject는 디렉터리 이름과 어버이 kobject(다시 말해, 대응하는 어버이 디렉터리)를 정의하는 구조체다. 예를 들어, /sys/devices/system/spu/spu3 디렉터리에 보이는 kobject는 나중에 설정해야 하는 값을 두 개 포함한다.
- spu3 이름
- /sys/devices/system/spu를 표현하는 kobject를 가리키는 포인터
subsystem_register() 호출을 사용해 kobject를 sysfs에 등록하고 나면, 객체가 사용자 영역에 보인다. 반대는 sysfs에서 디렉터리를 삭제하는 작업이다. 이렇게 하려면 삭제 대상 kobject를 매개변수로 넘기며 subsystem_unregister() 함수를 호출하면 된다. 그림 2에 예제가 나온다.
그림 2. sysfs와 kobject
spu3이라는 이름의 kobject는 spu라는 이름의 어버이 kobject를 가리키는 포인터를 포함한다.
커널 파일인 kernel/ve/vecalls.c에 핵심 변경 내역을 적용한다. 이 파일은 OpenVZ에 밀접한 파일로, 컨테이너 초기화와 매개변수 설정 과정에서 호출되는 대다수 함수가 정의되어 있다.
SPU 스케줄러 변경하기
시스템에 있는 각 물리 SPU는 커널 영역에서 spu 구조체 인스턴스로 표현된다. 이 구조체는 다음 내역을 포함하여 여러 가지 정보를 담고 있다.
- SPU ID(숫자)
- SPU가 속한 셀/B.E. 노드
- SPU의 LS를 가리키는 포인터
새로운 멤버 변수는 컨테이너 ID 형태로 SPU 소유주를 저장한다. SPU 스케줄러는 SPU에서 수행하는 SPE 스레드를 실행하기 위해 자유로운 SPU를 찾는 spu_alloc() 함수를 구현한다. 따라서 이 함수는 시스템에서 사용 가능한 SPU 목록을 탐색한다(어떤 SPE 스레드도 명령 즉시 실행되지는 않는다).
일반적인 동작 방식에 따르면 목록에서 첫 번째 SPU를 찾은 다음 여기서 SPE 스레드를 수행한다. 가상화를 위해, 이 함수는 SPE 스레드와 자유로운 SPU가 포함하고 있는 컨테이너 ID가 같은지 점검해야 한다. 그림 3은 프로세스 변경 직전과 직후에 spu_alloc()이 동작하는 방식을 보여준다.
그림 3. 변경 직전과 직후의 spu_alloc()
추가적인 점검 결과가 참이 아니면, 함수는 자유 SPU 목록에서 다음 SPU를 점검한다. SPE 스레드를 수행한 컨테이너에 유효한 자유 SPU가 없다면, SPU 스케줄러는 목록이 비어있는 듯이 행동하며, 자유 SPU가 나타날 때까지 기다린다.
spu_alloc() 함수는 리눅스 커널 원시 파일인 arch/powerpc/platforms/cell/spu_base.c에 구현되어 있다.
OpenVZ 도구 변경하기
필요한 함수는 이미 대부분 존재하지만, 새로운 기능을 활용하려면 OpenVZ 도구를 수정해야만 한다. vzctl 도구는 실행 중에 SPU 할당을 관리한다. vzctl은 OpenVZ에서 컨테이너 매개변수를 설정하는 핵심 도구다. 컨테이너에 할당할 SPU 숫자를 설정하는 새로운 매개변수는 --spus <nr_spus>이다.
<nr_spus> 값은 컨테이너에 할당된 SPU 개수를 나타낸다. 절대값이므로, 이 값을 6으로 설정한 SPU 여덟 개를 컨테이너에 할당하면, SPU를 6개 더 추가하는 대신 컨테이너에서 SPU 두 개를 풀어버린다(8 - 6 = 2).
예를 들어, 여기에 ID가 101인 컨테이너에 SPU 여덟 개를 할당하는 명령행 결과가 있다.
[root@c02b12-0 ~]# vzctl set 101 --spus 8
Setting SPUs: 8
Configure meminfo: 1024000
WARNING: Settings were not saved and will be reset to original
values on next start (use --save flag)
[root@c02b12-0 ~]#
|
이렇게 동작하려면, vzctl 도구는 사용자 영역 경계를 가로질러 몇 가지 관리 작업을 수행해야 한다. 이 도구는 다른 컨테이너가 아직 사용하지 않는 SPU를 찾아야만 한다. vzctl 도구는 사용 가능한 SPU 목록을 뒤져 spu 구조체에 있는 새로 구현한 컨테이너 ID 값을 점검한다(SPU 스케줄러를 변경하는 절에서 설명했다). 이 값이 0인 경우에 SPU는 요청 중인 컨테이너에 할당될 수 있다. 이 값을 0으로 사용한 이유는 컨테이너 ID 값은 0보다 커야 하기 때문에 값을 0으로 두면 SPU가 어떤 컨테이너에도 할당되어 있지 않음을 암시하기 때문이다. 이 함수가 요청을 처리하는 과정에서 충분한 자유 SPU를 발견하지 못할 경우, 절차가 종료되며 SPU를 하나도 컨테이너에 할당하지 않는다. 이미 컨테이너에 할당된 SPU 숫자가 요청받은 SPU 숫자보다 클 경우 그 차이만큼 할당을 해제한다.
사용자 영역과 커널 영역 경계를 가로지르기 위해 조금 다른 구현 모델을 활용할 수 있다(구현 모델에 대한 세부 사항은 참고자료 절에 소개하는 Arnd Bergmann이 쓴 "How to not invent kernel interfaces"를 살펴보기 바란다). 가장 손쉬운 방법은 새로운 시스템 호출을 구현해 매개변수 <containerID>와 <nr_spus>를 시스템 호출을 위한 매개변수로 사상하면 된다.
컨테이너의 SPU 매개변수 설정을 다루는 함수는 커널 모듈로 만들어지도록 커널의 일부로 구현해야 한다. 하지만 이렇게 하면 심각한 문제가 생긴다. 커널 모듈이 메모리에 올라오지 않으면, 커널 영역에 있는 시스템 호출 처리 함수가 아무 일도 하지 않아야 한다. 하지만 모듈이 메모리에 올라오면, 모듈 내부에 구현된 함수를 호출할 수 있어야 한다. 이는 단순한 작업이 아닌데, (시스템 호출 처리 함수를 가리키는 함수 포인터가 들어있는) 시스템 호출 테이블은 정적 커널 빌드의 일부이기 때문이다.
모듈은 정적 함수의 일부가 아니며, 정적으로 붙박이된 시스템 호출 함수가 모듈의 일부인 함수를 호출하지 못하는 이유가 바로 여기에 있다. 해법으로 모듈에 있는 함수 포인터를 정적이면서 붙박이 시스템 호출 함수로 복사하는 함수 감싸게를 구현해 정적으로 붙박이 시스템 호출 처리기가 모듈 내부 함수를 호출하도록 만든다. 함수 감싸게는 모듈 초기화 과정과 해제 과정에서 호출된다. 그림 4에서 실선 화살표는 함수 감싸게 메서드를 보여준다.
그림 4. 시스템 호출과 모듈 함수
모듈 내부에 구현된 함수 포인터를 정적이면서 붙박이 커널 영역으로 복사하는 방법을 살펴보았다. 점선 화살표는 사용자 영역 응용 프로그램이 정적이면서 붙박이 시스템 호출 처리 함수를 전달해 모듈 내부에 있는 함수를 호출하는 모습을 보여준다.
변경이 필요한 커널 원시 코드 파일은 다음과 같다.
- include/asm-powerpc/systbl.h
- include/asm-powerpc/unistd.h
- include/linux/syscalls.h
- kernel/sys.c
- kernel/sys_ni.c
- kernel/ve/vecalls.c
또한 OpenVZ vzctl에서 변경이 필요한 파일은 다음과 같다.
- include/res.h
- include/vzctl_param.h
- include/vzsyscalls.h
- src/lib/config.c
- src/lib/res.c
- src/vzctl.c
OpenVZ 빌드 시스템에서 파일 두 개를 추가로 수정해야 한다.
- include/spu.h
- src/lib/spu.c
Part 3를 위한 준비
Part 3는 의사 가상화나 완전 가상화 같은 다른 소프트웨어 가상화와 비교해 컨테이너 가상화 성능을 분석하고 시스템을 활용하고 테스트하는 방법을 설명한다.
참고자료 교육
-
RSS
피드를 구독해 이 연재물이 새로 등록되면 통보를 요청하기 바란다(더 많은 정보는 developerWorks 내용에 대한 RSS 피드 정보를 살펴보자).
- OpenVZ와 가상화에 대해 "Virtualization in Linux"(2006년 10월)를 찾아보기 바란다. OpenVZ의 세 가지 핵심 가상화 접근 방법(에물레이션, 의사 가상화, 운영체제 수준 가상화)를 다룬다.
- Arnd Bergmann이 쓴 "Spufs: The Cell Synergistic Processing Unit as a virtual file system"(developerWorks, 2005년 6월)을 읽고 셀/B.E.에서 리눅스가 동작하도록 허용하는 SPU 파일 시스템 인터페이스 세부 내역을 확인한다. Bergmann이 쓴 "How to not invent kernel interfaces"(LinuxConf Europe, 2007년 7월에 실린 논문)는 커널 코드가 사용자 영역 인터페이스 형식을 선택하는 방법을 설명한다. Bergmann은 또한 스레드 모델, 리눅스 실행 전략, PPU/SPU 실행 요구 사항, spufs, 시그널 처리를 다루는 virtual class on Linux on Cell/B.E. platforms를 작성했다.
- Daniel Hackenberg가 만든 발표 자료인 "Performance Measurements on Cell SMP Systems"(Center for Information Services and High Performance Computing's Cell/B.E. cluster meeting, 2007년 5월)를 찾아 매트릭스 연산, XDR DMA 대역폭, SPE-to-SPE DMA 대역폭과 같은 성능 척도를 살펴본다.
- Duc Vianney가 쓴 "Cell Software Solutions Programming Model" 발표 자료(2006년 3월)를 읽고 PPE와 SPE 중심 셀/B.E. 프로그래밍 모델 쟁점, 함수 오프로드, DMA와 계산 중첩, 이질적인 멀티 스레드 관련 정보를 얻자.
- 공통 패턴이라는 수단을 통해 기본 가상화 개념을 다루는 소개 기사는 "가상화: 패턴의 관점에서 본 가상화"(한국 developerWorks, 2006년 7월)다. "가상 리눅스"(2007년 2월)는 리눅스 관점에서 다양한 가상화 형태(그리고 현재 가상화 프로젝트)를 설명한다.
- 의사 가상화에 대한 내용은 "coLinux를 이용한 가상화 (영문)"(developerWorks, 2007년 3월)과 "QEMU를 이용한 시스템 에뮬레이션"(2007년 9월)을 읽어본다.
- SDK 3.0 최신 주제를 요약해 다루는 블로그 연재물을 살펴본다.
Infobombs. The first three
introduce the Accelerated Library Framework (ALF),
illustrate the 10 most important
concepts of ALF, introduce the Data Communication and Synchronization (DaCS) library services.
- libspe2 개념과 libspe2와 통신하고 기본적으로 SPE 프로세스를 관리하는 방법을 익히려면 "Changes in libspe: libspe2가 Cell Broadband Engine 프로그래밍에 미치는 영향 (영문)"(developerWorks, 2007년 7월)을 읽어본다.
- "Introduction to the Cell Multiprocessor"(IBM Journal of Research and Development, 2005년)을 참조해 셀/B.E. 개괄, 다중 프로세서 역사, 프로그램 목적과 도전, 설계 개념, 아키텍처와 프로그래밍 모델, 구현에 대해 살펴본다.
- 셀/B.E. 프로그램을 익히려면 다음 연재물을 살펴보자.
- 풍부한 매뉴얼, 명세를 제공하는 IBM 반도체 솔루션 기술 도서관에서 셀/B.E. 문서를 살펴보기 바란다.
-
developerWorks 소식지를 구독해 최신 개발자 소식과 셀/B.E. 관련 소식을 매주 받아보자. 구독 과정에서 Power Architecture를 선택해 소식지에 셀/B.E. 소식을 받도록 하자.
-
리눅스 영역과 오픈 소스 영역을 포함한 다른 developerWorks 자료를 찾아 가상화와 관련된 흥미있는 자료를 살펴보기 바란다.
제품 및 기술 얻기
토론
- 포럼에 참여하기.
- 프로세서에 대한 기술 질문 대답을 얻기 위해 셀/B.E. 아키텍처 포럼을 살펴본다. 포럼에서 유용한 문제와 대답이 주기적으로 올라오며, 중요 질문과 대답은 "포럼 감시" 블로그 연재물에서 다시 한번 강조될 것이다.
-
파워 아키텍처 블로그를 살펴 새소식, 내려받기, 도움이 되는 자원, 셀/B.E.와 다른 파워 아키텍처 관련 기술을 위한 이벤트 정보를 얻자. 인기있는 "포럼 감시" 블로그 연재(Q&A 정리), "FixIT" 기술 갱신, Infobomb 기술 요약 정리를 찾을 수 있을 것이다.
필자소개  | |  | Christian Kaiser는 독일 아헨에 있는 RWTH 대학교에서 컴퓨터 공학을 공부했다. 2007년에 Kaiser는 독일 뵈블링엔에 위치한 IBM 독일 연구소에서 인턴으로 근무했다. IBM에서 인턴으로 근무하는 동안 셀/B.E. 프로세서에서 시각화 방법을 연구했다. 인턴을 마치고 Kaiser는 RWTH 아헨에서 운영체제 관련 논문을 마치러 돌아갔다. 논문 제목은 "Analysis of asynchronous collective communication in memory-coupled high-speed networks"다. |
 | |  | Christian Rund는 독일 뵈블링엔에 있는 IBM 개발 연구소에서 근무하고 있다. Rund는 슈튜트가르트 대학교에서 컴퓨터 과학을 공부했으며, 스웨덴 웁살라 대학교를 1997년에 졸업했다. 학창 시절에 Rund는 독일 헤른베르크에 있는 IBM에서 실습생으로 일했다. 1988년 Rund는 슈투트가르트에 있는 란데스쩬트랄방크(도이체 분데스방크)의 시스템 개발 부서에 합류했다. 2001년에 Rund는 IBM에 합류해 zSeries FCP 채널을 위한 개발 팀에서 연구 개발 엔지니어로 일했다. 2006년 중반 셀/B.E. 기반 서버를 위한 호스트 펌웨어 개발 연구 엔지니어로 근무하고 있다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|  |