IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  리눅스  >

리눅스 커널 동적 적재 모듈 분석

커널 2.6에서 바라보기

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

M. Tim Jones, 컨설턴트 엔지니어, Emulex Corp.

옮긴이: 박재호 이해영 dwkorea@kr.ibm.com

2008 년 11 월 04 일

커널 버전 1.2부터 등장한 리눅스(Linux®) 커널 동적 적재 모듈은 리눅스 커널에서 가장 중요한 기술 혁신 중 하나입니다. 동적 모듈은 커널을 동적으로 확장 가능하게 만든 기술입니다. 동적 모듈 뒤에 숨겨진 아이디어를 분석하고 독립적인 모듈이 동적으로 리눅스 커널의 일부로 자리잡는 방법을 배워봅시다.

리눅스 커널은 모놀리틱 커널로 알려진 구조를 따른다. 모놀리틱 커널은 운영체제 기능 대부분이 커널에 들어있으며 특권 모드로 돌아감을 의미한다. 모놀리틱 커널과는 달리 마이크로 커널은 (IPC(Inter-Process Communication), 스케줄링, 기본 입출력, 메모리 관리와 같은) 기본 기능만 제공하며 (드라이버, 네트워크 스택, 파일 시스템과 같은) 나머지 기능을 특권 공간 외부로 밀어내버린다. 리눅스는 무척 정적인 커널이라고 생각할지도 모르겠지만, 실제로는 정반대다. 리눅스는 LKM(리눅스 커널 모듈) 활용을 통해 동적으로 변경이 가능하다.

developerWorks에 Tim이 쓴 ... 분석 연재물

동적으로 변경이 가능하다는 표현은 새로운 기능을 커널에 넣고 빼고 다른 LKM을 사용하는 새로운 LKM을 추가할 수 있다는 의미다. LKM을 사용하면 커널 메모리 크기를 줄인다는 장점이 있다. 필요한 구성 요소만 메모리에 올리면 되기 때문이다(임베디드 시스템에서 중요한 기능이다).

리눅스가 동적으로 변경할 수 있는 유일한 모놀리틱 커널은 아니다(처음이 아니라는 말이다), BSD(Berkeley Software Distribution), 썬 솔라리스, OpenVMS와 같은 옛날 커널, 기타 마이크로소프트 윈도(Microsoft® Windows®)나 애플 맥 OS X 같은 기타 유명한 운영체제에서도 동적 모듈을 지원한다.

커널 모듈 분석

LKM에는 커널로 직접 컴파일되는 구성 요소나 전형적인 프로그램과는 근본적인 차이점이 존재한다. 전형적인 프로그램은 main이 있는 반면에, LKM에는 모듈 entry와 exit 함수가 존재한다(2.6에서는 이 함수 이름을 마음대로 바꿀 수 있다). 모듈이 커널로 들어올 때 entry 함수가 호출되며, 나갈 때 exit 함수가 호출된다. entry와 exit 함수는 사용자 정의 형태이므로, module_init module_exit 매크로로 함수를 지정한다. LKM은 또한 필수거나 옵션인 모듈 매크로 집합을 포함한다. 이런 매크로는 모듈 라이선스, 모듈 작성자, 모듈 설명 등을 지정한다. 그림 1은 아주 간단한 LKM을 살펴본 모습이다.


그림 1. 간단한 LKM 모습
간단한 LKM 모습

버전 2.6 리눅스 커널은 LKM을 만들기 위한 (좀 더 간단한) 새로운 방법을 제공한다. 모듈 생성 후, (내부 구조가 바뀌긴 했지만) 모듈 관리를 위한 전형적인 사용자 도구를 사용한다. 표준 insmod(LKM 설치), rmmod(LKM 제거), modprobe(insmodrmmod 감싸개), depmod(모듈 의존성 생성), modinfo(모듈 매크로 값 확인) 등이 대표적인 모듈 관리 도구다. 2.6 커널에서 LKM 제작 방법을 설명하는 내용은 참고자료를 참조하자.




위로


커널 모듈 목적 파일 분석

LKM은 특수한 ELF(Executable and Linkable Format) 목적 파일 이상도 이하도 아니다. 일반적으로 목적 파일은 심볼을 찾아내기 위해 링크 절차를 밟아 실행 가능 파일로 떨어진다. 하지만 LKM은 커널로 올라오기 전까지 심볼을 찾아내지 못하므로, LKM은 ELF 목적 파일 형태로 존재한다. LKM에 표준 목적 파일 처리 도구를 사용할 수 있다(2.6 버전에서는 커널 목적 파일이라는 의미로 .ko 확장자를 사용한다). 예를 들어, LKM에 objdump 유틸리티를 적용하면, .text(명령어), .data(초기화된 자료), .bss(Block Started Symbol 또는 초기화되지 않은 자료) 같은 익숙한 섹션을 확인할 수 있다.

또한 동적 특성을 지원하기 위해 모듈 내에 자리 잡은 추가 섹션도 확인할 수 있다. .init.text 섹션은 module_init 코드를, .exit.text는 module_exit 코드를 포함한다(그림 2 참조). .modinfo 절은 모듈 라이선스, 작성자, 설명 등을 나타내는 다양한 매크로 텍스트를 포함한다.


그림 2. 다양한 ELF 섹션을 담은 LKM 예제
다양한 ELF 섹션을 담은 LKM 예제

지금까지 LKM을 소개했으므로, 지금부터는 커널 내부로 모듈을 넣는 방법과 내부적으로 관리하는 방법을 살펴보자.




위로


LKM 생명 주기

모듈이 메모리에 올라오는 과정은 사용자 영역에서 insmod(insert module) 명령어로 시작한다. insmod 명령은 메모리에 올릴 모듈을 정의하고 init_module 사용자 영역 시스템 호출을 불러서 메모리에 올리는 과정을 시작한다. 커널 2.6용 insmod 명령은 상당수 작업이 커널로 옮겨갔기 때문에 진짜로 간단하다(70행 정도 된다) insmod는 (kerneld와 함께 진행하는) 필요한 심볼을 찾는 작업 이외에 init_module 함수를 통해 커널로 모듈 바이너리를 복사하는 작업만 수행할 뿐이며, 나머지는 커널이 처리한다.

init_module 함수는 시스템 호출 층을 통해 동작하며, sys_init_module이라는 커널 함수로 바뀌어 호출된다(그림 3 참조). sys_init_module은 모듈을 메모리에 올리는 주 함수이며, 복잡한 작업을 수행하기 위해 무수한 다른 함수를 활용한다. 비슷하게 rmmod 명령어는 delete_module이라는 시스템 호출 층을 통해 동작하며, sys_delete_module이라는 커널 함수로 바뀌어 호출된다. sys_delete_module은 모듈을 커널에서 제거하는 주 함수다.


그림 3. 모듈을 메모리에 올리고 내리는 데 관련이 있는 주요 명령어와 함수
모듈을 메모리에 올리고 내리는 데 관련이 있는 주요 명령어와 함수

모듈을 메모리에 올리고 내리는 동안, 모듈 하위 시스템은 모듈 운영을 알려주는 간단한 상태 변수 집합을 유지한다. 모듈을 메모리에 올리는 중이라면, 상태는 MODULE_STATE_COMING이 된다. 모듈이 메모리에 올라왔으며 사용 가능하다면 상태는 MODULE_STATE_LIVE가 된다. 반면에 모듈이 메모리에서 내려가는 중이라면, 상태는 MODULE_STATE_GOING이 된다.




위로


모듈을 메모리에 올리는 방법

모듈을 메모리에 올리는 내부 함수를 살펴보자(그림 4 참조). 커널 함수인 sys_init_module을 호출할 때, 호출자가 실제로 연산을 수행할 수 있는지 (capable 함수를 사용해) 권한부터 점검하기 시작한다. 그런 다음에 load_module 함수를 호출해, 모듈을 커널에 올리고 필요한 연결 절차를 수행하도록 기계적인 작업에 신경을 쓴다(잠시 후에 살펴보겠다). load_module 함수는 새로 메모리에 올라온 모듈을 가리키는 모듈 참조값을 반환한다. 이 모듈은 시스템에서 모든 모듈을 연결하는 이중 리스트에 올라오며, 현재 모듈 상태 변경을 기다리는 스레드에 통지 목록을 통해 통지한다. 마지막으로 모듈에 포함된 init() 함수를 호출하고 모듈 상태를 갱신해서 메모리에 올라와서 살아있다고 알려준다.


그림 4. (단순화된) 내부 모듈을 메모리에 올리는 과정
(단순화된) 내부 모듈을 메모리에 올리는 과정

모듈을 메모리에 올리는 내부적인 세부 사항은 ELF 모듈 해석과 가공(manipulation)이다. (/linux/kernel/module.c에 들어있는) load_module 함수는 전체 ELF 모듈을 담을 임시 메모리 블록을 할당하는 작업부터 시작한다. copy_from_user를 사용해 ELF 모듈을 사용자 영역에서 임시 메모리로 읽어들인다. 모듈은 ELF 목적 파일 형태이므로, 쉽게 해석하고 검증이 가능하도록 만드는 아주 구체적인 구조를 따르고 있다.

다음 단계로 메모리에 올라온 이미지가 정상인지 점검하는 작업을 수행한다(유효한 ELF 파일인가? 현재 아키텍처에 맞춰 정의되어 있는가? 등). 정상인지를 점검하는 작업이 끝나면, 나중에 접근하는 작업을 단순하게 만들기 위해 ELF 이미지를 해석해서 각 섹션 헤더마다 편의 변수 집합을 생성한다. ELF는 (재배치 전까지) 변위 0부터 기준을 잡으므로 편의 변수는 임시 메모리 블록을 기준으로 상대적인 변위를 포함한다. 편의 변수를 생성하는 과정 동안 ELF 섹션 헤더를 검증해서 유효한 모듈이 메모리에 올라왔는지를 확인한다.

추가 모듈 인수는 사용자 영역에서 또 다른 커널 메모리 할당 블록(4단계)으로 올라온다. 모듈 상태는 현재 메모리에 올라오는 중(MODULE_STATE_COMING)이라고 알려주기 위해 갱신된다. (섹션 헤더 점검 과정에서 결정된) CPU 단위 자료가 필요하면, CPU 단위 블록을 할당한다.

직전 단계에서, 모듈 섹션은 커널 (임시) 메모리에 올라왔으며, 무엇이 영속적이며 무엇이 제거 가능한지를 할 수 있다. 다음 단계(7)는 메모리에서 모듈이 들어설 최종 위치를 할당하고, 필요한 섹션을 이동한다(SHF_ALLOC으로 ELF 헤더에 지정하거나 실행 중 메모리를 점유하는 섹션을 사용한다). 또한 모듈에서 요구하는 섹션에 필요한 크기만큼 메모리를 할당한다. 임시 ELF 블록에서 각 섹션을 순회하면서 수행이 필요한 섹션을 새로운 블록으로 복사한다. 그러고 나서 몇 가지 추가적인 부가 작업을 진행한다. 심볼을 찾아서 (커널 이미지에 컴파일된) 커널 내부 심볼을 사용하도록 만들거나, (다른 모듈이 외부로 공개한) 임시 심볼을 사용하도록 만든다.

그런 다음 새로운 모듈에서 남아 있는 섹션을 순회하며 재배치를 수행한다. 이 단계는 아키텍처에 의존적이므로 아키텍처(./linu/arch/<arch>/kernel/module.c)마다 별도로 정의된 도우미 함수에 의존한다. 마지막으로 명령 캐시를 플러시하고(임시 .text 섹션을 사용했기 때문에), (임시 모듈 메모리를 해제하고 sysfs를 설정하는 등) 최종적인 부가 작업을 수행하며, 마지막으로 load_module로 모듈을 반환한다.




위로


모듈을 메모리에서 내리는 방법

모듈을 메모리에서 내리는 방법은 모듈을 메모리에 올리는 방법과 대칭이다. 여기서 모듈을 안전하게 삭제하도록 여러 가지 안전 검사를 수행해야 한다는 점이 다르다. 모듈을 메모리에서 내리는 작업은 rmmod(remove module)명령을 수행하는 방식으로 사용자 영역에서 시작한다. rmmod 명령 내부에서는 delete_module로 시스템 호출을 수행하는데, 이 시스템 호출은 최종적으로 커널 내부에 있는 sys_delete_module을 호출한다(그림 3을 다시 살펴보자). 그림 5는 모듈 제거 과정에서 일어나는 기본적인 동작 과정을 보여준다.


그림 5. (단순화된) 내부 모듈을 메모리에서 내리는 과정
(단순화된) 내부 모듈을 메모리에서 내리는 과정

커널 함수인 sys_delete_module을 호출할 때(제거될 모듈 이름을 인수로 전달한다), 첫 번째 단계는 호출자가 권한이 있는지를 확인한다. 다음으로 목록을 점검해 이 모듈에 의존하는 다른 모듈이 있는지 확인한다. 의존하는 모듈 단위로 항목을 포함하는 modules_which_use_me라는 목록이 존재한다. 이 목록이 비어 있으면, 모듈 의존성이 없으며 모듈은 제거 후보가 된다(그렇지 않으면 오류를 반환한다). 다음 단계 테스트는 모듈이 메모리에 올라와 있는지 확인하는 작업이다. 현재 설치되는 모듈에 rmmod 명령을 사용할 수 있기에, 이런 점검 과정을 통해 모듈이 살아있는지 확인한다. 몇 가지 추가적인 부가 점검을 마친 다음에, 마지막에서 하나 앞 단계인 (모듈 내부에서 제공하는) exit 함수 호출을 진행한다. 마지막으로 free_module 함수를 호출한다.

free_module을 호출할 때, 모듈은 안전하게 제거가 가능한 상태로 확인이 끝났다. 모듈에 대한 의존성은 없으며, 이 모듈을 차지하는 커널 공간을 정리하는 작업을 수행할 수 있다. 이런 과정은 설치 과정 동안 (sysfs, 모듈 목록 등에) 올린 다양한 목록에서 모듈을 제거하는 작업부터 시작한다. 다음으로 아키텍처에 밀접한 정리 루틴을 호출한다((./linux/arch/<arch>/kernel/module.c). 그러고 나서 그 모듈이 의존하는 모듈을 순회하며 해당 모듈 목록에서 이 모듈을 차례로 제거한다. 마지막으로 커널 관점에서 정리 작업을 완료한다. 인수 메모리, CPU당 메모리, 모듈 ELF 메모리(coreinit) 등 모듈에 할당된 다양한 메모리를 해제한다.




위로


모듈 관리를 위한 커널 최적화

여러 응용 프로그램에서, 모듈을 동적으로 적재할 필요가 있다. 하지만 동적으로 적재하고 나서, 모듈을 내릴 필요는 없다. 커널이 시작 과정에서는 동적으로 움직이지만(수행 중에 찾아낸 디바이스를 지원하는 모듈을 메모리에 올린다), 연산을 수행하는 동안에는 동적으로 움직이지 않는다. 메모리에 올라온 다음에 모듈을 내릴 필요가 없다면, 모듈 관리를 위해 필요한 상당수 코드를 줄이는 방식으로 여러 가지 최적화가 가능해진다. CONFIG_MODULE_UNLOAD 커널 환경 설정 옵션을 꺼버리는 방법으로 모듈을 내리기 위한 커널 기능 상당수를 제거할 수 있다.




위로


한 걸음 더 나가면

이 기사는 커널에서 일어나는 모듈 관리 과정을 조감했다. 모듈 관리에 대한 세부 사항은 원시 코드 자체에 가장 잘 문서화되어 있다. 모듈 관리에 개입하는 핵심 함수는 ./linux/kernel/module.c와 관련 헤더인 ./linux/include/linux/module.h에 잘 나타나있다. 아키텍처에 밀접한 여러 함수는 ./linux/arch/<arch>/kernel/module.c에서 찾을 수 있다. 마지막으로 커널 자동 적재 함수(필요할 때마다 커널 기반에서 모듈을 적재하는)는 ./linux/kernel/kmod.c에서 찾을 수 있다. 이 기능은 CONFIG_KMOD 환경 설정 옵션으로 활성화한다.



참고자료

교육
  • Rusty Russell이 쓴 "Bleeding Edge" 블로그는 현재 리눅스 커널 개발에 대한 내용을 담고 있다. Rusty는 신형 리눅스 모듈 아키텍처를 만든 선도 개발자다.

  • Linux Kernel Module Programming Guide: 오래되긴 했지만, LKM과 개발 방법에 대한 자세한 정보를 많이 제공한다.

  • "Access the Linux Kernel using the /proc filesystem"(developerWorks, 2006년 3월): /proc 파일 시스템을 사용한 LKM 프로그래밍 기법을 자세히 들여다본다.

  • "리눅스 시스템 호출을 활용한 커널 명령"(developerWorks, 2008년 6월)에서 시스템 호출 뒤에 숨겨진 세부 사항을 설명했다.

  • 리눅스 커널을 좀 더 배우고 싶다면 팀이 쓴 첫 번째 연재물 기사인 "리눅스 커널 분석 (developerWorks, 2007년 6월)을 읽고 리눅스 커널에 대한 흥미로운 사항을 살펴보기 바란다.

  • "Standards and specs: An unsung hero: the hardworking ELF"(developerWorks, 2005년 12월)에 나오는 ELF 소개 글을 읽어보자. ELF는 리눅스에서 사용하는 표준 목적 파일 형식이다. ELF는 실행 가능한 이미지, 목적 파일, 공유 라이브러리, 심지어 코어 덤프에 이르기까지 다양한 파일을 다루는 유연한 형식이다. 형식을 담은 참조 문서(PDF 형식)나 ELF 세부 형식을 다루는 책에서 좀 더 자세한 정보를 찾아보자.

  • Captain's Universe는 예제 makefile을 사용해 LKM을 만들어내는 훌륭한 소개글을 제공한다. LKM 제작 방식은 커널 2.6부터 (더 나은 방향으로) 변경되었다.

  • 모듈을 넣고, 빼고, 관리하는 몇 가지 모듈 유틸리티가 있다. insmod 명령으로 커널에 모듈을 넣고, rmmod 명령으로 커널에서 모듈을 뺀다. 커널에 현재 존재하는 모듈을 질의하려면, lsmod 명령어를 사용한다. 모듈은 다른 모듈에 의존할 수 있기에, 의존성 파일을 생성하도록 depmod 명령이 존재한다. 모듈을 올리기 앞서 자동으로 의존 모듈을 올리도록 (insmod 감싸개인) modprobe 명령을 활용할 수도 있다. 마지막으로 modinfo 명령을 사용해 LKM에 대한 모듈 정보를 읽을 수도 있다.

  • Linux Journal 기사인 "Linkers and Loaders"(2002년 11월)는 (심볼 결정과 재배치를 포함하여) ELF 파일을 사용하는 링커와 로더 뒤에 숨겨진 목적을 소개하는 훌륭한 기사다.

  • developerWorks 리눅스 영역에서 리눅스 개발자를 위한 자료를 찾아보고 가장 인기있는 튜토리얼과 기사를 살펴보자.

  • 리눅스 팁리눅스 튜토리얼을 developerWorks에서 읽어보자.

  • developerWorks 기술 행사와 웹 캐스트를 놓치지 말자.


제품 및 기술 얻기
  • SEK for Linux 주문: DB2®, Lotus®, Rational®, Tivoli®, WebSphere®를 포함한 최신 IBM 평가판 소프트웨어를 담고 있는 두 장짜리 DVD 세트를 주문하자.

  • IBM 평가판 소프트웨어: developerWorks에서 직접 내려받아, 다음번 리눅스 프로젝트 개발에 활용하자.


토론


필자소개

M. Tim Jones 사진

M. Tim Jones는 임베디드 펌웨어 아키텍트이자 Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming(2판이 나왔다), AI Application Programming(2판이 나왔다), BSD Sockets Programming from a Multilanguage Perspective의 저자이기도 하다. Jones의 공학 배경은 정지 위성을 위한 커널 개발에서 시작해 임베디드 시스템 아키텍처와 네트워크 프로토콜 개발에 이르기까지 다양한 분야를 아우른다. Jones는 콜로라도 주, 롱몬트 소재 Emulex 사에서 컨설턴트 엔지니어로 활약한다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


IBM, the IBM logo, ibm.com, DB2, developerWorks, Lotus, Rational, Tivoli, and WebSphere are trademarks or registered trademarks of International Business Machines Corporation in the United States, other countries, or both. These and other IBM trademarked terms are marked on their first occurrence in this information with the appropriate symbol (® or ™), indicating US registered or common law trademarks owned by IBM at the time this information was published. Such trademarks may also be registered or common law trademarks in other countries. See the current list of IBM trademarks. Linux is a trademark of Linus Torvalds in the United States, other countries, or both. 기타 회사, 제품, 및 서비스명은 다른 상표나 서비스 마크일 수 있습니다.

developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의