ต่อจากตอนที่แล้วนะครับ ใครยังไม่ได้อ่านตอนที่แล้วขอแนะนำให้เข้าไปอ่านก่อน
ดีไวซ์ไดรเวอร์ ตอนที่ 1 "Hello World\n" ถ้าอ่านแล้ว เรามาเริ่มกันเลยดีกว่า
การคอมไพล์โมดุลสำหรับ Linux Kernel ใน 2.4 นั้นเราอาจจะใช้คอมมานด์ง่ายๆแบบนี้:
$ gcc -D__KERNEL__ -c module.c -o module.o
หรือบางคนอาจจะเขียน Makefile เป็นแบบนี้:
module.o: module.c
gcc -D__KERNEL__ -c module.c -o module.o
แต่ทั่วๆไปที่เห็นจะเป็นแบบนี้:
KERNELDIR = /usr/src/linux
CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O
all: module.o
ในโลกของความเป็นจริง makefiles จะวุ่นวายกว่านี้เยอะ ลองเข้าไปดูใน kernel source ดูก็ได้ เรียกกันไปเรียกกันมา พันกันยุ่งเหยิงไปหมด แต่เรากำลังพูดกันถึงเฉพาะ makefile สำหรับคอมไพล์โมดุลซึ่งมีการทำงานจริงๆแค่ขั้นเดียว (สำหรับ c code ไฟล์เดียว)
ถ้าทำงานกับ kernel 2.6 ถึงไม่ชอบเขียน makefile ก็ต้องถึงเวลาต้องชอบแล้วล่ะ (ไม่งั้นก็ถึงเวลาต้องทน) เราไม่สามารถเรียก gcc ขึ้นมาคอมไพล์โต้งๆ step เดียวง่ายๆสั้นๆได้เหมือนเมื่อก่อน แล้วถ้าจะคอมไพล์โมดุลเนี่ย เราต้องมี kernel source tree ที่ผ่านการ configured มาแล้ว จะด้วย oldconfig, menuconfig, xconfig หรืออะไรก็แล้วแต่ แต่ต้องทำให้เห็น ส่วนตัวผมใช้ source tree สำหรับ running system นั่นแหละ แล้วใน source tree เนี่ย ยังมี makefiles ชุดนึงที่อธิบายกระบวนการคอมไพล์โมดุล ซึ่งก็แน่นอน เราต้องการมันเช่นกัน เหตุผลสั้นๆที่เราต้องการ(และต้องทำ)อะไรต่อมิอะไรให้วุ่นวายปวดหัวก็เพราะ:
module loader (ตย. insmod) สำหรับ 2.6 ต้องการ symbols บางอย่างในโมดุลซึ่งจะถูกเพิ่มเข้าไปตอนคอมไพล์ แน่นอน ถ้าไม่มี symbols เหล่านี้เราก็ไม่สามารถ insmod เข้าไปได้ ตัวอย่างของ symbols บางตัวก็ KBUILD_BASENAME และ KBUILD_MODNAME
ใน 2.6 ทุกๆโมดุลต้องผ่านการ link ถึงแม้ว่าจะมีแค่ c source file เดียวก็ตาม ในขั้นตอนของการลิงก์ระบบจะเรียก init/vermagic.oจากใน soure tree โดย object ตัวนี้จะแทรก symbols บางตัวเข้าไปใน module ของเรา ตัวอย่างเช่น compiler version, SMP หรือเปล่า, preemtive หรือไม่ symbols บางตัวสำคัญมาก ถ้าค่าไม่ตรงกับ running kernel แล้วเรายังรั้นโหลดเข้าไป ระบบอาจจะเดี้ยงได้
ระบบการ versioning ตัวใหม่ ("modversions") ต้องใช้ post-compiling ต่างหาก กับ ต้องการ linkable object อีกตัวสำหรับเก็บค่า checksums (แปลแล้วงงๆแฮะ)
(สามข้อนี้ถ้าอ่านแล้วงง แนะนำให้ไปอ่านภาษาอังกฤษดีกว่า แปลยากจริงๆ แปลอังกฤษเป็นไทย แล้วต้องแปลไทยเป็นไทยอีกรอบ)
สรุปว่ายังไงเราก็ต้องเขียน makefile เพื่อใช้ในการคอมไพล์โมดุล ในเอกสารนี้จะอธิบายสั้นๆ ถ้าชอบแบบยาวๆแนะนำให้ไปตามต่อที่ 2.5.59+ kernel makefile documentation เลย
ตัวอย่างแรก เป็น makefile สำหรับคอมไพล์โมดุลอย่างง่ายที่สุด ใช้สำหรับโมดุลที่มาจาก c source file ไฟล์เดียว
obj-m := module.o
ชื่อ module.o ก็แทนด้วยชื่อไฟล์ของเรานะครับ เช่น xyz.o makefile บรรทัดเดียวนี้จะคอมไพล์ xyz.c ออกมาเป็น xyz.o แล้วเอาไปลิงก์กับ vermagic.o ออกมาเป็น xyz.ko จำไว้นะครับ .ko ไม่ใช่ .o เหมือนเมื่อก่อนแล้ว ไฟล์ .ko นี้แหละที่เราจะเอาไป insmod ยัดเข้าไปใน running kernel ของเรา ถ้าเราคอมไพล์ตรงๆให้ได้ object file .o ออกมาแบบเมื่อก่อน ไฟล์นั้นจะโหลดเข้าไปในเคอร์เนลไม่ได้ (ตอนแรกเราก็มึนๆเหมือนกัน)
แล้วถ้าโมดุล xyz ของเราสร้างจากหลายๆไฟล์เช่นจาก x.c, y.c และ z.c ล่ะ? ถ้าเป็นอย่างนั้นเราต้องการอย่างน้อย ๒ บรรทัดนะครับ หน้าตาของ makefile ก็จะออกมาเป็นแบบนี้
obj-m := xyz.o
xyz-objs := x.o y.o z.o
ถ้าไม่มีปัญหาอะไรตอนคอมไพล์(หรือตอนลิงก์) เราจะได้ xyz.ko ซึ่งสร้างมาจากน้ำปลาเอ๊ยซอสโค้ด 3 ไฟล์ดังที่กล่าว
โอเค ตอนนี้เรามี makefile ตัวอย่างง่ายๆแล้ว เราจะสั่งรัน makefile ได้ยังไงน๊อ? นี่เลยครับ รันคำสั่งข้างล่างเลย อ่อ อย่าลืมแก้ path ให้ถูกต้องก่อนนะครับ
make -C /path/to/source SUBDIRS=$PWD modules
คำสั่งนี้จะบอกว่า นี่นาย make อ่าน Makefile จาก /path/to/source นะ แล้ววิ่งกลับมาที่อยู่ปัจจุบันของเรา อ่าน Makefile ของเรา แล้วคอมไพล์มันซะ
แต่แหม จะให้พิมพ์คำสั่ง make ยาวเหยียดอย่างนี้คงไม่ไหว เราเอาพวกอากิวเมนต์ยาวๆยัดเข้าไปใน makefile ดีกว่า เราจะได้ makefile สำหรับสร้างโมดุลสำหรับ running kernel ออกมาหน้าตาแบบนี้
ifneq ($(KERNELRELEASE),)
obj-m := xyz.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
endif
แต่สำหรับเรา เราทำงานกับ embedded systems เป็นหลัก เครื่องที่ใช้ทำงานกับเครื่องปลายทาง (target machine) มักเป็นคนละเครื่องกัน makefile ที่เราเริ่มเขียนและใช้มาจนถึงปัจจุบันมีหน้าตาแบบนี้ อ่อ ใน top makefile ของ source tree จะไปเลือกใช้ cross-compiler ที่ถูกต้องเอง กรณีของเราขะใช้ arm-linux-gcc
KDIR := /usr/src/linux-2.6.8.1-altair
PWD := $(shell pwd)
obj-m := ich.o liebe.o cell.o
liebe-objs := u.o me.o
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm *.o *.ko *.mod.c
ไม่จำเป็นว่า source tree ต้องอยู่ใน /usrc/src นะครับ ที่ผมไว้ที่นั่นเพราะผมไว้ตรงนั้นมานานแล้ว แล้วก็ติดที่จะพิมพ์ /usr/src ไปแล้วด้วย
ตอนนี้เพื่อนๆคงเขียนและคอมไพล์โมดุลสำหรับ Linux Kernel 2.6.x กันได้แล้ว หวังว่าเพื่อนๆคงสนุกกับเคอร์เนลเวอร์ชั่นใหม่กันนะครับ ถ้าอยากรู้อะไรเกี่ยวกับ Linux Kernel 2.6.x อีกก็เรียกร้องกันมานะครับ จะจัดหาไว้ให้ สวัสดีครับ..
บอมบ์
ปล. เนื้อหาส่วนใหญ่อ้างอิงจาก compiling external modules ใน ชุด ในชุด Porting device drivers to the 2.6 kernel โดย Jonathan Corbet