您的位置:首页 > 运维架构 > Linux

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

2012-04-13 11:25 375 查看
原文

动态的将内核空间的物理地址和大小传给用户空间。本文也演示了内核空间和用户空间进行通信可以使用的两种常用方法: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,相当于告诉系统,这个页面我已经占了。对于每一个申请到的页面,应该都要这样做。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: