Linux下編寫和載入 .ko 檔案(驅動模組檔案)
一、.ko 檔案介紹
.ko檔案是kernel object檔案(核心模組),該檔案的意義就是把核心的一些功能移動到核心外邊, 需要的時候插入核心,不需要時解除安裝。
二、優點
(1)這樣可以縮小核心體積;
(2)使用方便。
三、.ko檔案一般的用處
(1)作為一個功能模組,需要使用時,直接插入執行就行。如在imx6上連線模擬攝像頭,先執行模擬攝像頭對應的驅動模組 camera.ko檔案,然後對應的工程執行檔案執行就行。
四、使用.ko 檔案
1、載入驅動模組test.ko
(1)方法一
進入test.ko驅動模組檔案所在的目錄,然後直接 insmod test.ko
(2)方法二
將test.ko檔案拷貝到/lib/module/#uname-r#/目錄下,這裡,#uname -r#意思是,在終端中輸入
uname -r後顯示的核心版本及名稱,例如mini2440中#uname-r#就是2.6.32.2-FriendlyARM。
然後 depmod(會在/lib/modules/#uname -r#/目錄下生成modules.dep和modules.dep.bb檔案,表明模組的依賴關係)
最後 modprobe test(注意這裡無需輸入.ko字尾) 即可
注:兩種方法的區別
modprobe和insmod類似,都是用來動態載入驅動模組的,區別在於modprobe可以解決load module時的依賴關係,它是通過/lib/modules/#uname -r/modules.dep(.bb)檔案來查詢依賴關係的;而insmod不能解決依賴問題。也就是說,如果你確定你要載入的驅動模組不依賴其他驅動模組的話,既可以insmod也可以modprobe,當然insmod可以在任何目錄下執行,更方便一些。而如果你要載入的驅動模組還依賴其他ko驅動模組的話,就只能將模組拷貝到上述的特定目錄,depmod後再modprobe。
2、檢視已載入的驅動模組列表
在任何目錄下輸入命令
//
lsmod
//
3、解除安裝驅動模組
在任何目錄下, 輸入命令
//
rmmod <module_name> 注:“module_name”是lsmod顯示的模組名稱,而不是對應的ko檔名
//
注:“module_name”是lsmod顯示的模組名稱,而不是對應的ko檔名
//
五、編寫生成.ko 檔案
Linux下hello.ko核心模組製作的全過程
1. linux系統用的是Redflag 6.0 SP1 下載地址:ftp://ftp.redflag-linux.com/pub/redflag/dt6sp1/SP1/redflag-6-sp1.iso, 系統安裝很容易,安提示做就好。
所用的核心原始碼目錄樹下載地址:ftp://ftp.redflag-linux.com/pub/redflag/dt6sp1/SP1/redflag-6-tool-sp1-src1.iso,將此iso檔案掛載到/mnt下,安裝其中的核心rpm包。
掛載方法:mount -t iso9660 redflag-6-tool-sp1-src1.iso /mnt/ -o loop
核心目錄樹安裝方法:cd /mnt/RedFlag/SRMPS/
rpm -i kernel-2.6.23.1-4.src.rpm
3. 編寫hello模組程式碼,原始碼如下:
hello.c
- #include <linux/init.h>
- #include <linux/module.h>
- MODULE_LICENSE("GPL");
- static int hello_init(void)
- {
- printk(KERN_ALERT "Hello, world\n");
- return 0;
- }
- static void hello_exit(void)
- {
- printk(KERN_ALERT "Goodbye, cruel world\n");
- }
- module_init(hello_init);
- module_exit(hello_exit);
4. 編寫hello模組的Makefile檔案,Makefile內容如下:
Makefile
- #Makefile 2.6
- obj-m :=hello.o
- KERNEL :=/usr/src/kernels/$(uname -r)/
- PWD :=$(shell pwd)
- modules :
- $(MAKE) -C $(KERNEL) M=$(PWD) modules
- .PHONEY:clean
- clean :
- rm -f *.o *.ko
5. 編譯模組
在命令列進入hello.c所在的資料夾下執行make命令即可完成hello模組的編譯。用ls命令可以檢視到hello.ko檔案,此檔案就是我們自定義的核心模組。
6. 安裝hello模組
命令列下執行命令:insmod hello.ko 。通過命令:cat /var/log/messages
可以看到下面這樣的資訊:“Aug 6 13:37:59 localhost kernel: Hello, world”,說明模組載入成功了。
7. 另外一種模組Makefile的編寫方法
Makefile
- # If KERNELRELEASE is defined, we've been invoked from the
- # kernel build system and can use its language.
- ifneq ($(KERNELRELEASE),)
- obj-m := hello.o
- # Otherwise we were called directly from the command
- # line; invoke the kernel build system.
- else
- KERNELDIR ?= /lib/modules/$(shell uname -r)/build
- PWD := $(shell pwd)
- default:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
- endif
7. 解除安裝hello模組
命令列下執行命令:rmmod hello.ko即可。通過命令:cat /var/log/messages.
可以看到下面這樣的資訊:“Aug 6 13:40:36 localhost kernel: Goodbye, cruel world”,說明模組解除安裝成功。
8. 檢視模組資訊
命令列下執行命令:modinfo hello
二 Debian 6下製作hello核心模組的過程
1. 軟體
Debian 6.02
linux-2.6.32.10.tar.bz2
2. 解壓核心原始碼到一個目錄下, 我解壓到/home/kernel/下來
3. 查詢安裝核心標頭檔案
- aptitude search linux-headers-2.6.32*
- aptitude install linux-headers-2.6.32-5-686
注意:2.6.32-5-686來自命令uname -r的結果
上面這個命令比較麻煩,還可以選擇下面的命令實現同樣的目的
- apt-get install linux-headers-`uname -r`
注意這個命令裡使用的不是單引號,而是反單引號,位於鍵盤的左上角, 一般和數字1是鄰居。
4. 寫個Hello模組測試
5. FAQ
<Q1> 核心程式碼下載後, 要簡單的執行倆命令配置一下,我這裡的命令如下[當然, 您也可以不嘗試這一步, 當你make時, 系統會提示你該怎麼做]
<A1>
- cd /home/kernel/linux-2.6.32.10
- make oldconfig && make prepare
<Q2>WARNING: Symbol version dump /home/kernel/linux-2.6.32.10/Module.symvers
is missing; modules will have no dependencies and modversions.
<A2>
- cd /home/kernel/linux-2.6.32.10
- make modules
[參考內容]
The Module.symvers is (re)generated when you (re)compile modules. Run make modules, and you should get a Module.symvers file at the root of the kernel tree.
Note that if you only ran make and not make modules, you haven't built any modules yet. The symbols from the kernel itself (vmlinux or one of the architecture-dependent image formats) are in System.map.
經測試問題得到很好的解決, make modules要花費好長編譯時間段。
[問題] type defaults to "int' in declaration of module_init
[解答] module_init(hello_init);這句中某個字元弄成漢字編碼導致的
[問題] XP與虛擬機器裡的debian通訊我用倆辦法一個samba傳輸資料,一個是ssh傳輸命令
[解答] 啟動sshd服務的命令: /etc/init.d/ssh start
6. hello驅動涉及到linux驅動模型的方方面面
hello.h程式碼檔案
- #ifndef _HELLO_ANDROID_H
- #define _HELLO_ANDROID_H
- #include <linux/cdev.h>
- #include <linux/semaphore.h>
- #define HELLO_DEVICE_NODE_NAME "hello"
- #define HELLO_DEVICE_FILE_NAME "hello"
- #define HELLO_DEVICE_PROC_NAME "hello"
- #define HELLO_DEVICE_CLASS_NAME "hello"
- struct hello_android_dev {
- int val;
- struct semaphore sem;
- struct cdev dev;
- };
- #define init_MUTEX(sem) sema_init(sem, 1)
- #endif
hello.c程式碼檔案
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/fs.h>
- #include <linux/proc_fs.h>
- #include <linux/device.h>
- #include <asm/uaccess.h>
- #include "hello.h"
- static int hello_major = 0;
- static int hello_minor = 0;
- static struct class *hello_class = NULL;
- static struct hello_android_dev *hello_dev = NULL;
- static int hello_open(struct inode *inode, struct file *filp);
- static int hello_release(struct inode *inode, struct file *filp);
- static ssize_t hello_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
- static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
- static struct file_operations hello_fops = {
- .owner = THIS_MODULE,
- .open = hello_open,
- .release = hello_release,
- .read = hello_read,
- .write = hello_write,
- };
- static ssize_t hello_val_show(struct device *dev, struct device_attribute *attr, char *buf);
- static ssize_t hello_val_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
- static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store);
- static int hello_open(struct inode *inode, struct file *filp)
- {
- struct hello_android_dev *dev;
- printk(KERN_ALERT"hello_open 1\n");
- dev = container_of(inode->i_cdev, struct hello_android_dev, dev);
- printk(KERN_ALERT"hello_open 2\n");
- dev = container_of(inode->i_cdev, struct hello_android_dev, dev);
- printk(KERN_ALERT"hello_open 3\n");
- filp->private_data = dev;
- printk(KERN_ALERT"hello_open 4\n");
- return 0;
- }
- static int hello_release(struct inode *inode, struct file *filp)
- {
- return 0;
- }
- static ssize_t hello_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
- {
- ssize_t err = 0;
- struct hello_android_dev *dev = filp->private_data;
- if (down_interruptible(&(dev->sem))) {
- return -ERESTARTSYS;
- }
- if (count < sizeof(dev->val)) {
- goto out;
- }
- printk(KERN_ALERT"hello_read\n");
- if (copy_to_user(buf, &(dev->val), sizeof(dev->val))) {
- err = -EFAULT;
- goto out;
- }
- err = sizeof(dev->val);
- out:
- up(&(dev->sem));
- return err;
- }
- static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
- {
- struct hello_android_dev *dev = filp->private_data;
- ssize_t err = 0;
- if (down_interruptible(&(dev->sem))) {
- return -ERESTARTSYS;
- }
- if (count != sizeof(dev->val)) {
- goto out;
- }
- if (copy_from_user(&(dev->val), buf, count)) {
- err = -EFAULT;
- goto out;
- }
- err = sizeof(dev->val);
- out:
- up(&(dev->sem));
- return err;
- }
- /*
- * dev fs operations
- */
- static ssize_t __hello_get_val(struct hello_android_dev *dev, char *buf)
- {
- int val = 0;
- if (down_interruptible(&(dev->sem))) {
- return -ERESTARTSYS;
- }
- val = dev->val;
- up(&(dev->sem));
- return snprintf(buf, PAGE_SIZE, "%d\n", val);
- }
- static ssize_t __hello_set_val(struct hello_android_dev *dev, const char *buf, size_t count)
- {
- int val = 0;
- val = simple_strtol(buf, NULL, 10);
- if (down_interruptible(&(dev->sem))) {
- return -ERESTARTSYS;
- }
- dev->val = val;
- up(&(dev->sem));
- return count;
- }
- static ssize_t hello_val_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct hello_android_dev *hdev = (struct hello_android_dev *)dev_get_drvdata(dev);
- return __hello_get_val(hdev, buf);
- }
- static ssize_t hello_val_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
- {
- struct hello_android_dev *hdev = (struct hello_android_dev *)dev_get_drvdata(dev);
- return __hello_set_val(hdev, buf, count);
- }
- /*
- * proc fs operations
- */
- static ssize_t hello_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
- {
- if (off > 0) {
- *eof = 1;
- return 0;
- }
- return __hello_get_val(hello_dev, page);
- }
- static ssize_t hello_proc_write(struct file *filp, const char __user *buf, unsigned long len, void *data)
- {
- int err = 0;
- char *page = NULL;
- if (len > PAGE_SIZE) {
- printk(KERN_ALERT"The buff is too large: %lu.\n", len);
- return -EFAULT;
- }
- page = (char *)__get_free_page(GFP_KERNEL);
- if (!page) {
- printk(KERN_ALERT"Failed to alloc page.\n");
- return -ENOMEM;
- }
- if (copy_from_user(page, buf, len)) {
- printk(KERN_ALERT"Failed to copy buff from user.\n");
- err = -EFAULT;
- goto out;
- }
- err = __hello_set_val(hello_dev, page, len);
- out:
- free_page((unsigned long)page);
- return err;
- }
- /*
- * /proc/hello
- */
- static void hello_create_proc(void)
- {
- struct proc_dir_entry *entry;
- entry = create_proc_entry(HELLO_DEVICE_PROC_NAME, 0, NULL);
- if (entry) {
- /* entry->owner = THIS_MODULE;*/
- entry->read_proc = hello_proc_read;
- entry->write_proc = hello_proc_write;
- }
- }
- static void hello_remove_proc(void)
- {
- remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL);
- }
- static int __hello_setup_dev(struct hello_android_dev *dev)
- {
- int err;
- dev_t devno = MKDEV(hello_major, hello_major);
- memset(dev, 0, sizeof(struct hello_android_dev));
- cdev_init(&(dev->dev), &hello_fops);
- dev->dev.owner = THIS_MODULE;
- dev->dev.ops = &hello_fops;
- err = cdev_add(&(dev->dev), devno, 1);
- if (err) {
- return err;
- }
- init_MUTEX(&(dev->sem));
- dev->val = 0;
- return 0;
- }
- static int __init hello_init(void)
- {
- int err = -1;
- dev_t dev = 0;
- struct device *temp = NULL;
- printk(KERN_ALERT"Initializing hello device.\n");
- err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME);
- if (err < 0) {
- printk(KERN_ALERT"Failed to alloc char dev region.\n");
- goto fail;
- }
- hello_major = MAJOR(dev);
- hello_minor = MINOR(dev);
- hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL);
- if (!hello_dev) {
- err = -ENOMEM;
- printk(KERN_ALERT"Failed to alloc hello_dev.\n");
- goto unregister;
- }
- err = __hello_setup_dev(hello_dev);
- if (err) {
- printk(KERN_ALERT"Failed to setup dev: %d.\n", err);
- goto cleanup;
- }
- hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME);
- if (IS_ERR(hello_class)) {
- err = PTR_ERR(hello_class);
- printk(KERN_ALERT"Failed to create hello class.\n");
- goto destroy_cdev;
- }
- /*
- * create /dev/hello
- * create /sys/class/hello/hello
- */
- temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);
- if (IS_ERR(hello_class)) {
- err = PTR_ERR(hello_class);
- printk(KERN_ALERT"Failed to create hello device.\n");
- goto destroy_class;
- }
- /*
- * create /sys/class/hello/hello/val
- */
- err = device_create_file(temp, &dev_attr_val);
- if (err < 0) {
- printk(KERN_ALERT"Failed to create attribute val.\n");
- goto destroy_device;
- }
- dev_set_drvdata(temp, hello_dev);
- /*
- * create /proc/hello
- */
- hello_create_proc();
- printk(KERN_ALERT"Succedded to initialize hello device.\n");
- return 0;
- destroy_device:
- device_destroy(hello_class, dev);
- destroy_class:
- class_destroy(hello_class);
- destroy_cdev:
- cdev_del(&(hello_dev->dev));
- cleanup:
- kfree(hello_dev);
- unregister:
- unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);
- fail:
- return err;
- }
- static void __exit hello_exit(void)
- {
- dev_t devno = MKDEV(hello_major, hello_minor);
- printk(KERN_ALERT"Remove hello device.\n");
- /*
- * remove /proc/hello
- */
- hello_remove_proc();
- /*
- * destroy device and class
- */
- if (hello_class) {
- device_destroy(hello_class, MKDEV(hello_major, hello_minor));
- class_destroy(hello_class);
- }
- /*
- * delete cdev and free malloced mem
- */
- if (hello_dev) {
- cdev_del(&(hello_dev->dev));
- kfree(hello_dev);
- }
- /*
- * free device ID
- */
- unregister_chrdev_region(devno, 1);
- }
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("First Android Driver /dev/hello");
- module_init(hello_init);
- module_exit(hello_exit);
[問題] Can't find default configuration "arch/x86/configs/
[解答] 預設編譯 x86 配置的config
diff --git a/Makefile b/Makefile
index 39af85f..f7bcdad 100644
--- a/Makefile
+++ b/Makefile
@@ -192,7 +192,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
# "make" in the configured kernel build directory always uses that.
# Default value for CROSS_COMPILE is not to prefix executables
# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
-ARCH ?= i$(SUBARCH)
+ARCH ?= mips
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
致謝