Linux内核和用户空间通信的方式(一)— proc文件和mmap共享内存

news/2024/7/10 18:19:46 标签: linux内核, debian, module, file, 测试, user

之所以想写这篇文章,是有两个方面原因。其一是内核版有一个关于《内核可以从线性地址直接计算物理地址,用来做什么呢?》的讨论,偶说计算出物理地址可以共享给用户空间读写。dreamice兄说能否说一下详细的应用。其二是alb*版主提到wheelz曾经写过这样一个例程,拜读了一把,发现在传递物理地址和内存大小上,wheelz的例程还有些不够灵活。alb*版主提到可以通过文件的方式实现动态的传递。

因此,本人也写了这样一个例程,可以动态的将内核空间的物理地址和大小传给用户空间。本文也演示了内核空间和用户空间进行通信可以使用的两种常用方法:proc文件系统和mmap共享内存。

本文欢迎自由转载,但请标明作者,并保证本文的完整性。

整个内核模块,在模块插入时建立proc文件,分配内存。卸载模块的时候将用户空间写入的内容打印出来。
以下是内核模块的代码和用户空间的测试代码。

/*This program is used to allocate memory in kernel
and pass the physical address to userspace through proc file.*/


#include <linux/version.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>

#define PROC_MEMSHARE_DIR "memshare"
#define PROC_MEMSHARE_PHYADDR "phymem_addr"
#define PROC_MEMSHARE_SIZE "phymem_size"

/*alloc one page. 4096 bytes*/
#define PAGE_ORDER 0
/*this value can get from PAGE_ORDER*/
#define PAGES_NUMBER 1

struct proc_dir_entry *proc_memshare_dir ;
unsigned long kernel_memaddr = 0;
unsigned long kernel_memsize= 0;

static int proc_read_phymem_addr(char *page, char **start, off_t off, int count)
{
        return sprintf(page, "%08lx\n", __pa(kernel_memaddr));
}
static int proc_read_phymem_size(char *page, char **start, off_t off, int count)
{
        return sprintf(page, "%lu\n", kernel_memsize);
}

static int __init init(void)
{
        /*build proc dir "memshare"and two proc files: phymem_addr, phymem_size in the dir*/
        proc_memshare_dir = proc_mkdir(PROC_MEMSHARE_DIR, NULL);
        create_proc_info_entry(PROC_MEMSHARE_PHYADDR, 0, proc_memshare_dir, proc_read_phymem_addr);
        create_proc_info_entry(PROC_MEMSHARE_SIZE, 0, proc_memshare_dir, proc_read_phymem_size);

        /*alloc one page*/
        kernel_memaddr =__get_free_pages(GFP_KERNEL, PAGE_ORDER);
        if(!kernel_memaddr)
        {
                printk("Allocate memory failure!\n");
        }
        else
        {
                SetPageReserved(virt_to_page(kernel_memaddr));
                kernel_memsize = PAGES_NUMBER * PAGE_SIZE;
                printk("Allocate memory success!. The phy mem addr=%08lx, size=%lu\n", __pa(kernel_memaddr), kernel_memsize);
        }
        return 0;
}

static void __exit fini(void)
{
        printk("The content written by user is: %s\n", (unsigned char *) kernel_memaddr);
        ClearPageReserved(virt_to_page(kernel_memaddr));
        free_pages(kernel_memaddr, PAGE_ORDER);
        remove_proc_entry(PROC_MEMSHARE_PHYADDR, proc_memshare_dir);
        remove_proc_entry(PROC_MEMSHARE_SIZE, proc_memshare_dir);
        remove_proc_entry(PROC_MEMSHARE_DIR, NULL);

        return;
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Godbach ([email]nylzhaowei@163.com[/email])");
MODULE_DESCRIPTION("Kernel memory share module.");


用户空间的测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>

int main(int argc, char* argv[])
{
        if(argc != 2)
        {
                printf("Usage: %s string\n", argv[0]);
                return 0;
        }
        
        unsigned long phymem_addr, phymem_size;
        char *map_addr;
        char s[256];
        int fd;
        
        /*get the physical address of allocated memory in kernel*/
        fd = open("/proc/memshare/phymem_addr", O_RDONLY);
        if(fd < 0)
        {
                printf("cannot open file /proc/memshare/phymem_addr\n");
                return 0;
        }
        read(fd, s, sizeof(s));
        sscanf(s, "%lx", &phymem_addr);
        close(fd);

        /*get the size of allocated memory in kernel*/
        fd = open("/proc/memshare/phymem_size", O_RDONLY);
        if(fd < 0)
        {
                printf("cannot open file /proc/memshare/phymem_size\n");
                return 0;
        }
        read(fd, s, sizeof(s));
        sscanf(s, "%lu", &phymem_size);
        close(fd);
        
        printf("phymem_addr=%lx, phymem_size=%lu\n", phymem_addr, phymem_size);
        /*memory map*/
        int map_fd = open("/dev/mem", O_RDWR);
        if(map_fd < 0)
        {
                printf("cannot open file /dev/mem\n");
                return 0;
        }
        
        map_addr = mmap(0, phymem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, phymem_addr);
        strcpy(map_addr, argv[1]);
        munmap(map_addr, phymem_size);
        close(map_fd);
        return 0;
        
}

测试的内核是2.6.25.以下是执行结果。

debian:/home/km/memshare# insmod memshare_kernel.ko
debian:/home/km/memshare# ./memshare_user 'hello,world!'
phymem_addr=e64e000, phymem_size=4096
debian:/home/km/memshare# cat /proc/memshare/phymem_addr
0e64e000
debian:/home/km/memshare# cat /proc/memshare/phymem_size
4096
debian:/home/km/memshare# rmmod memshare_kernel
debian:/home/km/memshare# tail /var/log/messages
Sep 27 18:14:24 debian kernel: [50527.567931] Allocate memory success!. The phy mem addr=0e64e000, size=4096
Sep 27 18:15:31 debian kernel: [50592.570986] The content written by user is: hello,world!

仓促之间,有些地方处理的还是比较简单。希望高手多多指正。需要了解这方面实现的可以参考一下,共同学习。


http://www.niftyadmin.cn/n/1402573.html

相关文章

Linux内核和用户空间通信的方法(二)— 使用netlink

理论篇在 Linux 2.4 版以后版本的内核中&#xff0c;几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的&#xff0c;例如iprote2网络管理工具&#xff0c;它与内核的交互就全部使用了netlink&#xff0c;著名的内核包过滤框架Netfilter在与用户空间的通读&am…

mini2440网卡驱动DM9000之dm9000_start_xmit

/* 调用时机&#xff1a;当网卡有数据需要发送的时候&#xff0c;该函数被调用 */ static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long flags; board_info_t *db netdev_priv(dev); dm9000_dbg(db, 3, "%s:\n", __func…

Kdump之makedumpfile源码分析

出自:http://blog.chinaunix.net/uid-10328574-id-2951118.html kexec是一个快速启动机制&#xff0c;允许在已经运行的内核的上下文启动另一个Linux内核&#xff0c;不需要经过BIOS。BIOS可能会消耗很多时间&#xff0c;特别是带有众多数量的外设的大型服务器。这种办法可以为…

Kdump之kexec源码分析

知道kexec还是在linuxsir上看到一篇介绍其应用的帖子, 经常用kexec快速启动内核的步骤如下:(1).kexec -l <kernel-image> --append"<command-line-options>" [--initrdxxxxxxxxxxx一般是要的,不过某些情况下可选]例如: kexec -l /boot/vmlinuz-2.6.31 -…

Kdump之kdump分析

说Kexec是基于kexec机制工作的,但关于Kdump到底是怎么实现的&#xff0c;比如将第二个内核怎么加载到具体的保留位置&#xff0c;第一个内核crash后怎么传需要的elfcorehdr和memmap参数给第二个内核&#xff0c;另外第二个内核是怎么调用makdedumpfile来过滤压缩页的,网上一些资…

Kdump之kdump分析(续)

在第二个内核以类似ro rootLABEL/ rhgb quiet irqpoll maxcpus1 reset_devices memmapexactmap memmap640K0K memmap5264K16384K memmap125152K22288K elfcorehdr147440K memmap56K#1834688K memmap136K#1834744K memmap128K#1834880K memmap1024K$4193280K启动时&…

kdump转储的内核实现

前面一篇文章介绍了kexec和kdump的思想&#xff0c;本文着重讲它们的另一个方面&#xff0c;就是kdump到底是如何转储垮掉内核的内存映像的。首先定义一个链表&#xff0c;它很重要。 static LIST_HEAD(vmcore_list); unsigned long long elfcorehdr_addr ELFCORE_ADDR_MAX; /…

基于kexec的崩溃转储机制

设计 当一个内核转储发生的时候kdump使用kexec启动一个备份 的内核 。这个备份启动的内核只是使用少量的内存&#xff0c;并且这些内存由第一个内核提供 。这样设计保证了第一个内核启动且正在运行中的DMA不会破坏第二个内核的运行 。 在内核崩溃之前所有关于核心映…