디바이스 드라이버
어느 프로그램이든지 만들려면 Makefile을 만들어야 합니다. 디바이스 드라이버도 역시 Makefile을 만들어 놓고 사용하면 편리합니다. 더욱이 커널 2.6에서는 gcc 를 이용하여 빌드하는 것이 아니기 때문에 반드시 Makefile을 만들어야 한다고 합니다.
저도 간단하게 프로그램을 작성해서 컴파일하려고 Makefile을 만들어 보았습니다.
Makefile 오류
MS-Windos에서 매우 편리한 RAPID 툴만 사용했던 저로서는 Makefile을 만들어야 한다는 것이 별로 반갑지 않습니다. 반갑지 않은 정도가 아니라 성가시고 때로는 알지 못하는 에러 때문에 고생해야 했습니다.
일반 에플리케이션을 만들기 위한 Makefile은 나름대로 큰 수정없이 사용할 수 있는 Makefile을 만들어서 사용할 정도가 되었습니다만 디바이스 드라이버를 학습하면서, 이것도 새로운 영역이라고 새로운 Makefile 형식에 사뭇 당황했습니다. 이것 또 암호문이네...으~
나름 빠르게 해본다고 책에 딸려 온 CD에 있는 소스 파일을 복사해서 main.c를 만들고 역시 CD에 있는 Makefile을 수정했습니다만 제대로 컴파일이 되질 않더군요. 가장 큰 잘못은 책에서 Makefile 부분을 대충 내용만 흝어 본 것이었습니다. 다시 책 내용을 정독하고 Makefile을 수정하고 컴파일해 보았지만 아래와 같은 에러가 계속 나더군요.
]$ make make -C /lib/modules/2.6.9-42.0.8.ELsmp/build SUBDIRS=/home/jwjw/prjs/forum_sample/115 DeviceDriver modules make[1]: Entering directory `/usr/src/kernels/2.6.9-42.0.8.EL-smp-i686' make[1]: *** 타겟 `DeviceDriver'를 만들 규칙이 없음. 멈춤. make[1]: Leaving directory `/usr/src/kernels/2.6.9-42.0.8.EL-smp-i686' make: *** [default] 오류 2 ]$ make
아~! 정말 미치겟더군요. 이런 기억이 예전에도 있었습니다. 바로 Tab 문자 때문이었죠.
그렇다면 이번에 발생한 에러는 무엇 때문일까요?
make[1]: *** 타겟 `DeviceDriver'를 만들 규칙이 없음. 멈춤.
문제는 바로 작업 디렉토리에 공백 문자가 있어서 였습니다. 공백만 없다면 한글 이름의 디렉토리 명을 사용해도 컴파일할 수가 있었습니다.
115_디바이스드라이버]$ make make -C /lib/modules/2.6.9-42.0.8.ELsmp/build SUBDIRS=/home/jwjw/prjs/forum_sample/115_디바이스드라이버 modules make[1]: Entering directory `/usr/src/kernels/2.6.9-42.0.8.EL-smp-i686' CC [M] /home/jwjw/prjs/forum_sample/115_디바이스드라이버/main.o Building modules, stage 2. MODPOST CC /home/jwjw/prjs/forum_sample/115_디바이스드라이버/main.mod.o LD [M] /home/jwjw/prjs/forum_sample/115_디바이스드라이버/main.ko make[1]: Leaving directory `/usr/src/kernels/2.6.9-42.0.8.EL-smp-i686' 115_디바이스드라이버]$
다른 프로그램 컴파일에서는 현재 디렉토리 정보가 중요하지 않았습니다만 디바이스 드라이버, 즉 모듈 프로그래밍에서는 현재의 디렉토리 명을 Makefile에 넘겨 주어야 하므로 Makefile 이 헤깔리지 않도록 디렉토리 명에 공백 문자가 있어서는 안되겠습니다.
또한 커널 별로 컴파일하는 방법이 다릅니다. 다음에 소개되는 커널별 Makefile을 참고하시기 바라며, 혹 사용하시는 시스템의 커널 버전을 확인하시려면 uname -r 을 이용하십시오.
]$ uname -r 2.6.9-42.0.8.ELsmp ]$
Makefile에 대해서는 리눅스 디바이스 드라이버, 저자 유영창의 책 내용 중 4장 4절의 내용을 그대로 사용하면서 읽기 편하게 편집했습니다. 원저자에 누를 끼치지 않기 설명 글은 원문 그대로 가져 오지 않고 요점만 간추렸음을 말씀드립니다.
2.4 커널 외부 모듈 컴파일 용 Makefile
우선 Makefile의 전체 모습을 보시겠습니다. 예에서는 소스 파일 이름이 test.c 라고 가정하겠습니다.
KERNELDIR = /lib/modules/$(shell uname -r)/build CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O all: test.o clean: rm -rf *.o
Makefile | 설 명 |
KERNELDIR = /lib/modules/... | 모듈은 커널과 밀접하게 연관되어 있으므로 버전 문제를 방지하기 위해서는 모듈이 동작하는 커널 소스와 일치시켜야 하는데, 이를 위해 컴파일할 모듈이 참조할 커널 소스의 디렉토리를 지정한다. |
CFLAGS = -D__KERNEL__ ... | D__KERNEL__ : 커널과 관련된 내용을 활성화 하라 |
test.o | 만들어낼 오브젝트 |
clean:
rm -rf $*.o |
컴파일 결과로 생긴 모든 파일을 삭제 |
test.o 가 하나의 소스가 아니라 여러 개의 소스 파일로 만들어 진다면 아래와 같이 내용을 추가하시면 됩니다.
all: test.o test.o : test1.o test2.o test3.o
2.6 커널 외부 모듈 컴파일 용 Makefile
2.6 커널 용은 2.4보다 조금 복잡해 보이는 듯 합니다. 프로그램 소스가 어디에 있는지도 보여 주어야 하고 말이죠. 그러다 보니 위에서 말씀드린 Makefile 오류 경험도 했습니다만 내용을 보면 모듈을 만들어 내기 위해서 2.4보다는 더욱 전문적이고 체계적으로 보입니다.
KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m := test.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o
Makefile | 설 명 |
KERNELDIR = /lib/modules/... | 모듈은 커널과 밀접하게 연관되어 있으므로 버전 문제를 방지하기 위해서는 모듈이 동작하는 커널 소스와 일치시켜야 하는데, 이를 위해 컴파일할 모듈이 참조할 커널 소스의 디렉토리를 지정한다. |
obj-m := test.o | 생성할 모듈 이름 |
KDIR := /lib/modules/$(shel ... | 커널의 소스 위치 |
PWD := $(shell pwd) | 컴파일 대상 프로그램 소스 위치 |
default: $(MAKE) -C $(KDIR) ... |
모듈 컴파일 명령 |
clean: rm -rf *.ko ..... |
컴파일 결과로 생긴 모든 파일을 삭제 |
test.o 가 하나의 소스 파일로 되어 있지 않고 여러 파일로 만들어져 잇다면 아래와 같이 행을 추가하시면 됩니다.
obj-m := test.o module-objs := test1.o test2.o test3.o
이제 컴파일해서 모듈을 만들어 내는 방법까지 알게 되었으므로 다음 시간부터는 디바이스 드라이버 프로그램에 작성에 대해 하나씩 알아 보도록 하겠습니다.
태그: *디바이스드라이버