III) Hướng dẫn viết module cho Linux
Như đã biết, Linux là 1 hệ điều hành có tính module rất cao, cho phép người dùng có thể chỉnh sửa các module, driver cần thiết tương ứng với thiết bị đang sử dụng
Đối với ng dùng bình thường, modularity cho phép chọn lựa cách biên dịch các drivers của nhân theo dạng modules hay theo dạng biên dịch trực tiếp vào nhân.
Có những "driver" không thể biên dịch như một module vì nó phải được load and link trực tiếp ngay khi nhân khởi động . Cũng có những "driver" cho phép chọn như một module và được tải trong khi và sau khi nhân được khởi động. Điểm chính yếu cần nắm là khi nào nên chọn M (cho module), Y (cho biên dịch trực tiếp) và N (không dùng) các drivers này.
Biên dịch trực tiếp nghĩa là các driver có dùng hay không cũng đều được load ngày từ khi hđh khởi động, tất nhiên nó sẽ chiếm 1 phần memory. Lợi điểm là "tính trung thực" của nhân và driver. Các hệ thống bảo mật cao thường biên dịch trực tiếp để tránh các module "lạ" bị cài vào nhân trong quá trình hoạt động của máy.
Biên dịch module nghĩa là biên dịch các driver như những module, chỉ khi cần sử dụng mới được tải vào nhân. Lợi điểm của phương pháp này là hiệu quả sử dụng tài nguyên. Bạn có thể tạo ra 1 nhân rất nhỏ gọn và thuận tiện trong việc biên dịch lại 1 số module nào đó (thay vì phải biên dịch lại toàn bộ nhân).
Các bạn nào muốn biết thêm về biên dịch nhân và cách viết driver và biên dịch driver cho Linux (cơ bản là cho máy vi tính nhé) thì tham khảo các tài liệu sau:
Biên dịch nhân linux - Hoàng Ngọc Diệu
Linux device driver - Third Edition - Jonathan Corbet
Hoặc vào website http://kernelnewbies.org/ để làm quen với kernel.
Nào Cùng Viết 1 Module loadable thui
PHP Code:
// hello_world_module.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("abc@googlegroups.com");
MODULE_DESCRIPTION("Hello world module");
MODULE_VERSION("v1.0");
static char *whom = "world";
module_param(whom, charp, S_IRUGO);
MODULE_PARM_DESC(whom, "Say hello to whom");
static int array[4];
static int array_length;
module_param_array(array, int, &array_length, S_IRUGO);
MODULE_PARM_DESC(array, "array of 4 integers");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, %s\n", whom);
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, %s\n", whom);
}
module_init(hello_init);
module_exit(hello_exit);
Module này không làm việc gì khác ngoài gọi lệnh printk (tương đương printf trong user space, printk chạy trong kernel space), nội dung được in ra file /var/log/messges và có thể được xem bằng lệnh dmesg. Xem xét module này chúng ta thấy được một module căn bản nhất cần include 2 file <linux/init.h> và <linux/module.h>, và cần hiện thực ít nhất 2 hàm module_init và module_exit. Hàm module_init sẽ được gọi khi lệnh insmod thực thi và hàm module_exit sẽ được gọi khi chúng ta rmmod module. Linux kernel cung cấp nhiều macro cho module, sau đây là một số macro thường gặp:
Code:
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR(“author”);
MODULE_DESCRIPTION(“description”);
MODULE_VERSION(“version”);
Các thông tin này sau khi biên dịch module thành file .ko có thể được xem với lệnh:
@host:# modinfo ./hello_world.ko
filename: ./hello_world.ko
version: v1.0
description: Hello world module
author:
abc@googlegroups.com
license: Dual BSD/GPL
srcversion: D18FE01C06E9DB883BF5E18
depends:
vermagic: 2.6.32.2-FriendlyARM mod_unload ARMv4
parm: whom:Say hello to whom (charp)
parm: array:array of 4 integers (array of int)
Module còn có thể nhận biến được truyền từ lệnh insmod với macro
Code:
#include <linux/moduleparam.h>
module_param(variable_name, type, permission);
module_param_array(name,type,num,perm);
Hiện tại các type được hỗ trợ gồm bool, invbool, charp, int, long, short, uint, ulong, ushort. Permission được định nghĩa trong file <linux/stat.h>. Phiên bản khác của module_param là module_param_array cho phép chúng ta truyền vào một mảng các biến thay vì một biến riêng lẻ. Các biến này có thể được truyền vào module thông quan hàm inmode với cú pháp sau
Code:
@host:# insmod my_module my_variable=value
@host:# insmod ./hello_world.ko whom="foo" array=1,2,3,4
IV) Phương pháp biên dịch
Việc đầu tiên cần thiết để compile được module là chúng ta phải có một kernel cùng phiên bản với kernel hệ thống chúng ta sử dụng và kernel này phải được config và compile thành công .
Có hai cách để compile một loadable module: compile trong kernel source và compile bên ngoài kernel source. Cách thứ nhất có thể dùng khi ta thêm driver vào source tree của kernel và biến nó thành một tùy chọn trong Kconfig. Cách thứ hai có tính linh hoạt hơn khi phát triển driver, nhưng module không phải là một option của kernel source nên chúng ta phải compile riêng và chép file kết quả (file có đuôi .ko) vào hệ thống Linux của mình.
Hai cách được lần lượt trình bày như sau:
---------Compile trong kernel:
Trong ví dụ này chúng ta giả sử muốn add driver hello_world phía trên của mình vào source tree. Trước hết chúng ta sẽ tạo một directory riêng cho module của mình
(Chú ý: bạn sẽ nhìn thấy các config của bạn khi gõ make menuconfig )
@host:# mkdir /linux/driver/hello_world
Để kernel có thể nhận ra thư mục này chúng ta cần chỉnh sửa 2 file thuộc thư mục cha của hello_world (ở đây là thư mục driver) là Kconfig và Makefile
@host:# gedit /linux/driver/Kconfig
Thêm dòng này vào file Kconfig
source "drivers/hello_world/Kconfig"
@host:# gedit /linux/driver/Makefile
Thêm dòng này vào Makefile
Bây giờ chúng ta phải vào thư mục hello_world và tạo ra 2 file Kconfig và Makefile.
@host:# gedit /linux/driver/hello_world/Kconfig
config EXAMPLE_HELLO_WORLD
tristate "Example driver"
default n
help
Say yes if you want to compile this example driver...
@host:# gedit /linux/driver/hello_world/Makefile
obj-$(CONFIG_EXAMPLE_HELLO_WORLD) += hello_world.o
Sau này khi config kernel chúng ta sẽ thấy module của mình là một option, chúng ta có thể chọn link static vào kernel hoặc tạo thành loadable module hoặc không đưa module này vào kernel.
Nếu chỉ cần compile module này mà không cần compile kernel, chúng ta có thể dùng lệnh
@host:# make M=./drivers/hello_world/ modules
File biên dịch thành công là hello_world.ko đặt trong cùng thư mục với file source hello_world.c
----------------Compile bên ngoài kernel:
Chúng ta cần tạo một Makefile trong cùng thư mục source của module có nội dung như sau:
@host:# gedit /linux/driver/hello_world/Makefile
KERNELDIR ?= path_to_kernel
PWD := $(shell pwd)
ifneq ($(KERNELRELEASE),)
#hello_world-objs := file1.c file2.c ... filen.c
obj-m := hello_world.o
else
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
Ta thay thế “path_to_kernel” bằng đường dẫn tới thư mục kernel. hello_world.o bằng tên module cần compile và nếu module phụ thuộc vào nhiều file source C thì cần chỉnh thêm objs. Để thực hiện compile đơn giản ta chỉ sử dụng lệnh make tại thư mục chứa source của module.
Code:
@host:/linux/driver/hello_world# make





source code được đính kèm phía dưới