内核和应用层内存映射 I/O的完整源码
时间: 2024-01-17 18:04:55 浏览: 136
这里是一个简单的内核和应用层内存映射 I/O 的完整源码,包含了内核模块和用户程序部分:
##### 内核模块部分
```c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#define DEVICE_NAME "my_mmap"
MODULE_LICENSE("GPL");
static int major;
static char* buffer;
static int mmap_fault(struct vm_area_struct* vma, struct vm_fault* vmf)
{
unsigned long offset = vmf->pgoff << PAGE_SHIFT;
struct page* page = virt_to_page(buffer + offset);
get_page(page);
vmf->page = page;
return 0;
}
static const struct vm_operations_struct mmap_vm_ops = {
.fault = mmap_fault
};
static int mmap_mmap(struct file* filp, struct vm_area_struct* vma)
{
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long pfn = virt_to_phys(buffer) >> PAGE_SHIFT;
int ret = io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
if (ret) {
printk(KERN_ERR "io_remap_pfn_range failed\n");
return ret;
}
vma->vm_ops = &mmap_vm_ops;
mmap_fault(vma, NULL);
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.mmap = mmap_mmap
};
static int __init mmap_init(void)
{
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major < 0) {
printk(KERN_ERR "Failed to register_chrdev\n");
return major;
}
buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buffer) {
printk(KERN_ERR "Failed to allocate buffer\n");
unregister_chrdev(major, DEVICE_NAME);
return -ENOMEM;
}
memset(buffer, 'A', PAGE_SIZE);
return 0;
}
static void __exit mmap_exit(void)
{
kfree(buffer);
unregister_chrdev(major, DEVICE_NAME);
}
module_init(mmap_init);
module_exit(mmap_exit);
```
这份代码实现了一个简单的内核模块,它注册了一个名为 "my_mmap" 的字符设备,提供了一个 mmap 方法用于内存映射操作。mmap 方法的实现过程中,使用了 io_remap_pfn_range 函数将内核缓冲区的物理地址映射到用户进程的虚拟地址空间中。具体实现细节可以参考代码注释。
##### 用户程序部分
```c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#define DEVICE_NAME "/dev/my_mmap"
int main(int argc, char** argv)
{
int fd;
char* buffer;
size_t offset, length;
if (argc < 3) {
printf("Usage: %s <offset> <length>\n", argv[0]);
return EXIT_FAILURE;
}
offset = atol(argv[1]);
length = atol(argv[2]);
fd = open(DEVICE_NAME, O_RDONLY);
if (fd == -1) {
perror("open");
return EXIT_FAILURE;
}
buffer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, offset);
if (buffer == MAP_FAILED) {
perror("mmap");
close(fd);
return EXIT_FAILURE;
}
for (size_t i = 0; i < length; ++i) {
putchar(buffer[i]);
}
if (munmap(buffer, length) == -1) {
perror("munmap");
close(fd);
return EXIT_FAILURE;
}
close(fd);
return EXIT_SUCCESS;
}
```
这份代码实现了一个用户程序,它打开了内核模块注册的字符设备 "/dev/my_mmap",使用 mmap 函数将设备文件的一部分映射到进程的虚拟地址空间中,并输出映射区域的内容。具体实现细节可以参考代码注释。
需要注意的是,用户程序和内核模块需要分别编译并加载到内核和用户空间中。可以使用 Makefile 管理编译过程。
阅读全文