实验目的
- 编写一个内核模块;
- 编译该模块;
- 加载、卸载该模块;(相关命令: lsmod,insmod,rmmod,dmesg)
实验过程
编写内核模块代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert W. Oliver II");
MODULE_DESCRIPTION("A simple example Linux module.");
MODULE_VERSION("0.01");
#define DEVICE_NAME "lkm_example"#define EXAMPLE_MSG "Hello, World!\n"#define MSG_BUFFER_LEN 15
/* Prototypes for device functions */static int device_open(struct inode *, struct file *);static int device_release(struct inode *, struct file *);static ssize_t device_read(struct file *, char *, size_t, loff_t *);static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static int major_num;static int device_open_count = 0;static char msg_buffer[MSG_BUFFER_LEN];static char *msg_ptr;
/* This structure points to all of the device functions */static struct file_operations file_ops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release};
/* When a process reads from our device, this gets called. */static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) {
int bytes_read = 0;
/* If we’re at the end, loop back to the beginning */
if (*msg_ptr == 0) {
msg_ptr = msg_buffer;
}
/* Put data in the buffer */
while (len && *msg_ptr) {
/* Buffer is in user data, not kernel, so you can’t just reference
* with a pointer. The function put_user handles this for us */
put_user(*(msg_ptr++), buffer++);
len--;
bytes_read++;
}
return bytes_read;}
/* Called when a process tries to write to our device */static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) {
/* This is a read-only device */
printk(KERN_ALERT "This operation is not supported.\n");
return -EINVAL;}
/* Called when a process opens our device */static int device_open(struct inode *inode, struct file *file) {
/* If device is open, return busy */
if (device_open_count) {
return -EBUSY;
}
device_open_count++;
try_module_get(THIS_MODULE);
return 0;}
/* Called when a process closes our device */static int device_release(struct inode *inode, struct file *file) {
/* Decrement the open counter and usage count. Without this, the module would not unload. */
device_open_count--;
module_put(THIS_MODULE);
return 0;}
static int __init lkm_example_init(void) {
/* Fill buffer with our message */
strncpy(msg_buffer, EXAMPLE_MSG, MSG_BUFFER_LEN);
/* Set the msg_ptr to the buffer */
msg_ptr = msg_buffer;
/* Try to register character device */
major_num = register_chrdev(0, "lkm_example", &file_ops);
if (major_num < 0) {
printk(KERN_ALERT "Could not register device: %d\n", major_num);
return major_num;
} else {
printk(KERN_INFO "lkm_example module loaded with device major number %d\n", major_num);
return 0;
}}
static void __exit lkm_example_exit(void) {
/* Remember — we have to clean up after ourselves. Unregister the character device. */
unregister_chrdev(major_num, DEVICE_NAME);
printk(KERN_INFO "Goodbye, World!\n");}
/* Register module functions */
module_init(lkm_example_init);
module_exit(lkm_example_exit);
以上代码实现,当模块加载后,显示设备主要编号,卸载后显示“Goodbye, World”
问题与解决办法
Disagrees about version of symbol symbol_name after insmod
此处问题出现原因是linux模块与内核版本不同,通过modinfo和uname -a 比对后选择相同的linux模块,重新编译即可
编译
安装开发工具集合:
sudo apt-get install build-essential linux-headers-`uname -r`
编写Makefile
obj-m += lkm_example.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
test:
# We put a — in front of the rmmod command to tell make to ignore
# an error in case the module isn’t loaded.
sudo rmmod lkm_example
# Clear the kernel log without echo
sudo dmesg -C
# Insert the module
sudo insmod lkm_example.ko
dmesg
编译模块
Sudo make
Sudo make test
执行成功后回显:
成功加载卸载模块
Lsmod | grep “lkm_example”
列出加载模块
实验心得
实验结论
本次实验已获得预期输出结果,但是在实验过程中却花费了不少的时间去学习相关知识与函数用法。
个人收获
通过这次小实验,让我进一步了解了linux如何编写内核模块,以及如何加载卸载编译内核模块,激起了我们对linux学习的兴趣
📮评论