内核模块与用户空间通信

忽然发现网上很多实例都跑不了,实际上应该是因为他们都是类似参考文献那种,应该是3.x的系统接口。我这里测试是在4.11上。

方法

/proc

这个算是比较简单

大致上是在内核模块先申请一段内存,然后重写一些读写操作(在这里可以加入自己的逻辑,收到信息要怎么做之类的)进去,套用接口挂接上去就可以了。这个时候会建立出一个文件如/proc/testfile之类的

用户空间只需要像读写正常文件一样操作这个文件就可以完成交互

 

 

 

mmap共享内存

应该是最快的通信方法。

先建立一个设备文件(用debugfs也可),然后重写mmap,在用户程序用mmap映射出来就能用了

 

这里一个比较简单的实现就是从/proc里面改了。只要重写操作的时候把mmap也重写了就可以完成。

 

实例

概要

首先是要重写以下方法,把函数指针传递进去来注册文件设备。

 

static int mmap(struct file *filp, struct vm_area_struct *vma);
static int open(struct inode *inode, struct file *filp);
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off);
// copy_to_user 把数据从内核空间拷贝到用户态buf
static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off);
// copy_from_user 把数据从用户态buf拷贝到内核空间
static int release(struct inode *inode, struct file *filp);

static const struct file_operations fops = {
    .mmap = mmap,
    .open = open,
    .release = release,
    .read = read,
    .write = write,
};

// 模块初始化的时候,创建/proc/filename
proc_create(filename, 0600, NULL, &fops)
    
// 模块注销的时候删除掉
remove_proc_entry(filename, NULL);

 

 

下面是一个具体的实现

内核模块

static const char *filename = "ltproc";


enum { BUFFER_SIZE = 64 };

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_fault *vmf)
{
    struct page *page;
    struct mmap_info *info;

    pr_info("vm_fault\n");
    info = (struct mmap_info *)vmf->vma->vm_private_data;
    if (info->data) {
        page = virt_to_page(info->data);
        get_page(page);
        vmf->page = page;
    }
    return 0;
}

/* After 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); //test
    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;
    ssize_t ret;

    pr_info("read\n");
    if ((size_t)BUFFER_SIZE <= *off) {
        ret = 0;
    } else {
        info = filp->private_data;
        ret = min(len, (size_t)BUFFER_SIZE - (size_t)*off);
        if (copy_to_user(buf, info->data + *off, ret)) {
            ret = -EFAULT;
        } else {
            *off += ret;
        }
    }
    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;

  recv_buf = NULL;

    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,
};

// init 

// exit

用户空间程序

这个比较自由

  1. 可以通过正常的读写文件接口来读写这个/proc的文件,如open()之类的
    
    FILE *p = fopen("/proc/ltproc", "r+");
    if(p==NULL){
        return -1;
    }
// ····
    fgets(buf,100,p);

// ····
    fprintf(p, "%llu+%d\n",val, pid);
  1. 使用mmap把这个文件映射出来一个地址,然后直接读写这个地址就可以了,内核可以收到。
    fd = open(pid_dir_name, O_RDWR | O_SYNC);
    page_size = sysconf(_SC_PAGE_SIZE);
    if (fd < 0) {
        perror("open");
        exit(-1);
    }
    printf("fd = %d\n", fd);

    /* mmap twice for double fun. */
    puts("mmap 1");
    address1 = (char*)mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (address1 == MAP_FAILED) {
        perror("mmap");
        exit(-1);
    }

参考

https://wiki.tldp.org/kernel_user_space_howto

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值