编写内核模块,调用内核相关接口,实现申请 I/O 内存、读写 I/O 内存、释放 I/O 内存;并 打印输出相关信息
时间: 2023-06-10 09:07:48 浏览: 282
Unix I/O 小结
这里给出一个简单的内核模块代码,可以实现申请 I/O 内存、读写 I/O 内存和释放 I/O 内存的功能,并打印输出相关信息:
```c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h> // I/O端口控制相关头文件
#include <linux/io.h> // ioremap/iounmap函数头文件
#define IOMEM_SIZE 4096 // I/O内存大小
static unsigned long iomem_start; // I/O内存起始地址
static void __iomem *iomem_virt; // I/O内存虚拟地址
static int __init my_init(void)
{
int ret;
printk(KERN_INFO "my_init: start\n");
// 申请I/O内存
iomem_start = __get_free_iomem(IOMEM_SIZE, 0);
if (!iomem_start) {
printk(KERN_ERR "my_init: failed to allocate I/O memory\n");
return -ENOMEM;
}
printk(KERN_INFO "my_init: I/O memory allocated, start address = 0x%lx, size = %d bytes\n",
iomem_start, IOMEM_SIZE);
// 映射I/O内存
iomem_virt = ioremap(iomem_start, IOMEM_SIZE);
if (!iomem_virt) {
printk(KERN_ERR "my_init: failed to map I/O memory\n");
ret = -EFAULT;
goto err_iounmap;
}
printk(KERN_INFO "my_init: I/O memory mapped to virtual address 0x%p\n", iomem_virt);
// 读写I/O内存
iowrite32(0x12345678, iomem_virt);
printk(KERN_INFO "my_init: wrote 0x12345678 to I/O memory\n");
printk(KERN_INFO "my_init: read 0x%x from I/O memory\n", ioread32(iomem_virt));
return 0;
err_iounmap:
release_mem_region(iomem_start, IOMEM_SIZE);
return ret;
}
static void __exit my_exit(void)
{
printk(KERN_INFO "my_exit: start\n");
// 解除映射I/O内存
if (iomem_virt) {
iounmap(iomem_virt);
printk(KERN_INFO "my_exit: I/O memory unmapped\n");
}
// 释放I/O内存
if (iomem_start) {
release_mem_region(iomem_start, IOMEM_SIZE);
printk(KERN_INFO "my_exit: I/O memory released\n");
}
printk(KERN_INFO "my_exit: done\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
```
实现过程如下:
1. 在模块初始化函数 `my_init` 中,首先使用 `__get_free_iomem` 函数申请一段大小为 `IOMEM_SIZE` 的 I/O 内存空间,如果申请失败则返回错误。申请成功后,打印出 I/O 内存的起始地址和大小。
2. 调用 `ioremap` 函数将申请的 I/O 内存空间映射到内核地址空间中,并返回映射后的虚拟地址。如果映射失败,则返回错误并释放之前申请的 I/O 内存空间。映射成功后,打印出 I/O 内存的虚拟地址。
3. 使用 `iowrite32` 函数向 I/O 内存中写入一个 32 位的数据,并使用 `ioread32` 函数从 I/O 内存中读取一个 32 位的数据。这里写入的数据为 `0x12345678`,读取的数据则使用 `printk` 函数打印出来。
4. 在模块退出函数 `my_exit` 中,先调用 `iounmap` 函数解除 I/O 内存空间的映射,再调用 `release_mem_region` 函数释放 I/O 内存空间。如果之前调用 `ioremap` 函数失败,则不需要调用 `iounmap` 函数,直接跳转到释放 I/O 内存的步骤即可。
5. 在模块注册信息中使用 `MODULE_LICENSE("GPL")` 宏声明模块的许可证类型为 GPL。
编写完成后,可以将该模块编译成内核模块并加载运行。在加载模块时,会打印出类似下面的信息:
```
[ 776.950753] my_init: start
[ 776.950760] my_init: I/O memory allocated, start address = 0x100000, size = 4096 bytes
[ 776.950765] my_init: I/O memory mapped to virtual address 0xf8b6c000
[ 776.950769] my_init: wrote 0x12345678 to I/O memory
[ 776.950771] my_init: read 0x12345678 from I/O memory
```
可以看到,模块成功申请了 I/O 内存空间,并能够读写该内存空间,并且在卸载模块时,也成功释放了该内存空间。
阅读全文