리눅스 커널에서는 모듈을 다루기 위해 내부적으로 struct module 형식의 구조체를 선언하고, 리스트 형태로 모듈을 관리한다. 이 구주체는 include/linux/module.h에 선언되어 있다.
모듈 적재 과정
커널에 모듈을 적재하려면 insmod 명령을 실행해야 한다. 이 insmod는 다음과 같은 과정을 실행한다.
1. 주어진 모듈명이 ".o" 나 ".ko"가 포함되면 파일명에서 모듈명을 얻는다. 그렇지 않다면 모듈명으로 간주하고, /lib/module/의 하부 디렉토리에서 해당 모듈에 해당하는 파일명을 찾아 파일을 읽는다.
2. 읽어온 파일의 코드와 모듈명 그리고 module 구조체를 저장하는데 필요한 메모리 영역의 크기를 구한다.
3. 이 정보를 이용해 create_module() 함수를 호출한다. 이 함수는 모듈을 처리할 수 있는 권한이 있는지 검사하고, find_module() 함수를 이용해 이미 적재된 모듈인가를 검사한다. 만약 커널에 적재되지 않았다면 vmalloc() 함수를 호출해 새로운 모듈을 위한 메모리 영역을 할당한다. 할당받은 메모리 영역 중 module 구조체의 내용을 초기하고 모듈명을 그뒤에 복사한다. 이후 모듈을 관리하는 커널 모듈 리스트에 적재한다. 마지막으로 모듈에 할당된 메모리의 시작 주소를 돌려준다.
4. query_module() 함수를 이용해 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다. 이때 query_module() 함수에 QM_MODULES, QM_INFO, QM_SYMBOL 값을 지정하여 필요한 정보를 가져오는데, QM_MODULES는 커널에 포함된 모듈명을 얻어올 때 사용하며, QM_INFO는 각 모듈의 시작 주소와 크기를 얻기 위해 사용한다. 앞에서 구한 모듈 정보를 통해 QM_SYMBOL로 실질적인 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다.
5. 커널 심볼 테이블, 모듈 심볼 테이블 그리고 커널에 적재하려는 현재 모듈의 메모리 시작 번지를 이용해 읽어온 모듈의 프로그램 코드 주소를 재배치한다. 이때 모듈에서 참조하는 외부 함수나 변수의 외부 심볼과 전역 심볼에 대응하는 논리 주소 오프셋으로 바뀐다.
6. 사용자 모드 주소 공간에 메모리 영역을 할당하고, 이곳에 모듈 구조체의 내용과 모듈명 그리고 앞으로 재배치된 모듈 코드를 복사한다.
7. 모듈 구조체의 init 필드의 함수 주소와 exit 필드의 함수 주소를 할당한다.
8. 사용자 모드 주소 공간에 할당된 메모리 주소를 이용해 init_module() 함수를 호출한다. 이 함수는 create_module() 함수와 유사한 행동을 반복한다. 모듈 처리를 할 수 있는 권한이 있는지 검사하고, find_module() 함수와 create_module() 함수를 사용해 추가된 위치를 찾고, 사용자 모드에 설정된 모듈 구조체의 내용을 덮어쓴다. 이 후 module 구조체에 있는 주소들이 올바른지 검사한다. 이 후 모듈에 할당된 메모리에 나머지 내용을 모두 복사한다. 마지막으로 init 필드에 선언된 주소를 이용하여 모듈에 포함된 모듈 초기화 함수를 호출한다. 이후 사용자 모드 메모리를 해제하고 종료한다.
모듈이 커널에서 제거되는 과정
커널에서 모듈을 제거하려면 rmmod() 함수를 실행해야 하는데, 그 실행 과정은 다음과 같다.
1. 주어진 모듈명이 "*.o"나 "*.ko"가 포함되면 파일명에서 모듈명을 얻는다. 그렇지 않다면 모듈명으로 간주한다.
2. query_module() 함수를 이용해 모듈에 대한 정보와 사용되는 커널 심볼 테이블과 모듈 심볼 테이블을 얻는다.
3. 제거해야 할 모듈 목록을 이용하여 delete_module() 함수를 호출한다. 이 함수는 시스템을 이용하여 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다. 이때 모듈을 제거할 수 있는 권한이 있는가를 검사하고, find_module() 함수를 통해 이미 적재된 모듈인지 검사한다. 해당 모듈을 다른 모듈에서 참조하고 있는가를 검사한다. 참조하지 않고 있으면 혹시 사용되고 있는지를 검사한 후 사용중이 아니라면 module 구조체의 exit 필드에 정의된 함수를 호출한다. 이후 커널 내의 모듈 관리 리스트에서 해당 모듈을 제거하고, vfree() 함수를 이용하여 메모리를 해제한다.
4. 제거해야 할 모듈 목록을 모두 처리하면 종료한다.