linux内核缓冲区设置,如何将Linux内核缓冲区映射到用户空间?

最小运行的例子,用户态测试

#include /* copy_from_user */

#include

#include

#include

#include /* min */

#include

#include

#include

#include

static const char *filename = "lkmc_mmap";

enum { BUFFER_SIZE = 4 };

struct mmap_info {

char *data;

};

/* After unmap. */

static void vm_close(struct vm_area_struct *vma)

{

pr_info("vm_close\n");

}

/* First page access. */

static int vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)

{

struct page *page;

struct mmap_info *info;

pr_info("vm_fault\n");

info = (struct mmap_info *)vma->vm_private_data;

if (info->data) {

page = virt_to_page(info->data);

get_page(page);

vmf->page = page;

}

return 0;

}

/* Aftr mmap. TODO vs mmap, when can this happen at a different time than mmap? */

static void vm_open(struct vm_area_struct *vma)

{

pr_info("vm_open\n");

}

static struct vm_operations_struct vm_ops =

{

.close = vm_close,

.fault = vm_fault,

.open = vm_open,

};

static int mmap(struct file *filp, struct vm_area_struct *vma)

{

pr_info("mmap\n");

vma->vm_ops = &vm_ops;

vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;

vma->vm_private_data = filp->private_data;

vm_open(vma);

return 0;

}

static int open(struct inode *inode, struct file *filp)

{

struct mmap_info *info;

pr_info("open\n");

info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);

pr_info("virt_to_phys = 0x%llx\n", (unsigned long long)virt_to_phys((void *)info));

info->data = (char *)get_zeroed_page(GFP_KERNEL);

memcpy(info->data, "asdf", BUFFER_SIZE);

filp->private_data = info;

return 0;

}

static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)

{

struct mmap_info *info;

int ret;

pr_info("read\n");

info = filp->private_data;

ret = min(len, (size_t)BUFFER_SIZE);

if (copy_to_user(buf, info->data, ret)) {

ret = -EFAULT;

}

return ret;

}

static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off)

{

struct mmap_info *info;

pr_info("write\n");

info = filp->private_data;

if (copy_from_user(info->data, buf, min(len, (size_t)BUFFER_SIZE))) {

return -EFAULT;

} else {

return len;

}

}

static int release(struct inode *inode, struct file *filp)

{

struct mmap_info *info;

pr_info("release\n");

info = filp->private_data;

free_page((unsigned long)info->data);

kfree(info);

filp->private_data = NULL;

return 0;

}

static const struct file_operations fops = {

.mmap = mmap,

.open = open,

.release = release,

.read = read,

.write = write,

};

static int myinit(void)

{

proc_create(filename, 0, NULL, &fops);

return 0;

}

static void myexit(void)

{

remove_proc_entry(filename, NULL);

}

module_init(myinit)

module_exit(myexit)

MODULE_LICENSE("GPL");

#define _XOPEN_SOURCE 700

#include

#include

#include

#include

#include /* uintmax_t */

#include

#include

#include /* sysconf */

#include "common.h" /* virt_to_phys_user */

enum { BUFFER_SIZE = 4 };

int main(int argc, char **argv)

{

int fd;

long page_size;

char *address1, *address2;

char buf[BUFFER_SIZE];

uintptr_t paddr;

if (argc < 2) {

printf("Usage: %s \n", argv[0]);

return EXIT_FAILURE;

}

page_size = sysconf(_SC_PAGE_SIZE);

printf("open pathname = %s\n", argv[1]);

fd = open(argv[1], O_RDWR | O_SYNC);

if (fd < 0) {

perror("open");

assert(0);

}

printf("fd = %d\n", fd);

/* mmap twice for double fun. */

puts("mmap 1");

address1 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

if (address1 == MAP_FAILED) {

perror("mmap");

assert(0);

}

puts("mmap 2");

address2 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

if (address2 == MAP_FAILED) {

perror("mmap");

return EXIT_FAILURE;

}

assert(address1 != address2);

/* Read and modify memory. */

puts("access 1");

assert(!strcmp(address1, "asdf"));

/* vm_fault */

puts("access 2");

assert(!strcmp(address2, "asdf"));

/* vm_fault */

strcpy(address1, "qwer");

/* Also modified. So both virtual addresses point to the same physical address. */

assert(!strcmp(address2, "qwer"));

/* Check that the physical addresses are the same.

* They are, but TODO why virt_to_phys on kernel gives a different value? */

assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address1));

printf("paddr1 = 0x%jx\n", (uintmax_t)paddr);

assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address2));

printf("paddr2 = 0x%jx\n", (uintmax_t)paddr);

/* Check that modifications made from userland are also visible from the kernel. */

read(fd, buf, BUFFER_SIZE);

assert(!memcmp(buf, "qwer", BUFFER_SIZE));

/* Modify the data from the kernel, and check that the change is visible from userland. */

write(fd, "zxcv", 4);

assert(!strcmp(address1, "zxcv"));

assert(!strcmp(address2, "zxcv"));

/* Cleanup. */

puts("munmap 1");

if (munmap(address1, page_size)) {

perror("munmap");

assert(0);

}

puts("munmap 2");

if (munmap(address2, page_size)) {

perror("munmap");

assert(0);

}

puts("close");

close(fd);

return EXIT_SUCCESS;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值