zoukankan      html  css  js  c++  java
  • Linux内核和用户空间通信的方式(一)— proc文件和mmap共享内存

    动态的将内核空间的物理地址和大小传给用户空间。本文也演示了内核空间和用户空间进行通信可以使用的两种常用方法: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));

    内核中申请到页面之后,要调用一下SetPageReserved,相当于告诉系统,这个页面我已经占了。对于每一个申请到的页面,应该都要这样做

                     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!

    用你这种方式,想申请4M的内存,

    static int __init init(void)
    {
         char * paddr;
         int order = 0;
         int offset = 0;
         int tmp = 0;
        
             /*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);
       [color=royalblue]        order = get_order(4000000);
             kernel_memaddr =__get_free_pages(GFP_KERNEL, order);
    [/color]
             if(!kernel_memaddr)
             {
                     printk("Allocate memory failure!/n");
             }
             else
             {
                     SetPageReserved(virt_to_page(kernel_memaddr));

                     //kernel_memsize = PAGES_NUMBER * PAGE_SIZE;
                    [color=royalblue] kernel_memsize = ((4000000 - 1) >> PAGE_SHIFT)* PAGE_SIZE;[/color]
    [color=#4169e1][/color]
    [color=black]...[/color]
    [color=black]}[/color]
    [color=black][/color]
    [color=black][/color]
    我在内核中往第一页(4k范围内)写数据,在用户态是能够读出来的,但是如果向以后的其它页写数据时,用户态就读不出了,
    用户态写也这样。

    需要对每个4k的页面执行:SetPageReserved(virt_to_page(kernel_memaddr));

    内核中申请到页面之后,要调用一下SetPageReserved,相当于告诉系统,这个页面我已经占了。对于每一个申请到的页面,应该都要这样做。

  • 相关阅读:
    HttpClient入门三
    Centos常用命令之:压缩与解压缩
    Centos常用命令之:搜索
    Centos常用命令之:ln
    Centos常用命令之:VI
    HttpClient入门二
    NX二次开发-UFUN确定两个向量在指定的公差内是否相等(二维向量)UF_VEC2_is_equal
    NX二次开发-UFUN确定两个向量在指定的公差内是否相等(三维向量)UF_VEC3_is_equal
    NX二次开发-NXOPEN C#UF创建块theUfSession.Modl.CreateBlock1
    NX二次开发-NXOpen C# UF函数例子目录【更新日期2020.7.21】
  • 原文地址:https://www.cnblogs.com/shaoguangleo/p/2805883.html
Copyright © 2011-2022 走看看