小弟正在写一个测试程序,程序的目的是用户空间的虚拟地址映射为内核空间的逻辑地址,然后内核空间的驱动程序对该内存空间直接操作,写入相应的字符串,最后再由用户空间读出该字符串以验证程序的正确性。
程序的流程大概是这样的。 -----------------内核空间------------------------ 驱动程序注册一个字符设备 实现iotctl调用。 ioctl将用户空间传递的虚拟地址通过进行页表查询转换为物理地址,然后再将物理地址转化为逻辑地址,最后向该逻辑地址中写入“hello kmap”字符串 返回
-----------------用户空间------------------------ 使用valloc分配一页内存 调用memset清空该内存 然后打开驱动程序注册的字符设备接口 调用字符设备的iotcl,将上述分配的一页内存的虚拟地址传递给内核驱动程序 此时内存里面应该已有驱动程序写入的字符串信息,调用printf打印该内存。
小弟按照上述思路实现了程序,但是运行以后,发生了oops。不知道问题具体出在哪里,下面贴下代码,请各位指点迷津。谢谢
---------------------------------kernel.h-------------------------------------- /* * kernel.h 公用头文件 */ #ifndef KMAP_KERNEL_H #define KMAP_KERNEL_H
#ifdef KMAP_KERNEL #define __DEBUG_MSG(a,x...) do{printk(KERN_ALERT"%s %s %d:"a,__FILE__,__FUNCTION__,__LINE__,##x);}while(0) #else #define __DEBUG_MSG(a,x...) do{fprintf(stderr, "%s %s %d:"a, __FILE__, __FUNCTION__, __LINE__, ##x);}while(0) #endif
#define DEVICE_NAME "kmap"
#define KMAP_IOC_MAGIC 'q' #define KMAP_IOC_SET_PID_AND_VADDR _IOW(KMAP_IOC_MAGIC, 1, char *)
#ifdef KMAP_KERNEL int init_module(void); void cleanup_module(void);
static int kmap_open(struct inode *, struct file *); static int kmap_ioctl(struct inode *, struct file *, unsigned int, unsigned long); #endif
struct ioctl_arg { pid_t pid; unsigned long vaddr; }; #endif
------------------------------------kernel.c------------------------------------- /* *kernel.c 内核实现部分 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include
#include #include #include #include #include #include #include #include #include #include #include #include
#include #include #include #include #include #include #include #include
#define KMAP_KERNEL
#include "kernel.h"
static int major = 0; static struct file_operations fops = { .open = kmap_open, .ioctl = kmap_ioctl };
int init_module(void) { major = register_chrdev(0, DEVICE_NAME, &fops); if ( major < 0 ) { printk(KERN_ALERT"register chrdev failed:%d\n", major); return major; }
printk(KERN_ALERT"mknod /dev/%s c %d 0\n", DEVICE_NAME, major); return 0; }
void cleanup_module(void) { /*unregister the device*/ int ret = unregister_chrdev(major, DEVICE_NAME); if ( ret < 0 ) { __DEBUG_MSG("Error in unregister_chrdev:%d\n", ret); } }
static int kmap_open(struct inode *inode, struct file *filp) { __DEBUG_MSG("enter kmap_open\n"); return 0; } static int kmap_ioctl(struct inode *inode, struct file *filp, unsigned int cmd , unsigned long arg) { pid_t pid = 0; unsigned long vaddr = 0; unsigned long phy = 0; unsigned long laddr = 0;
int err = 0, retval = 0;
pgd_t *pgd = NULL; pud_t *pud = NULL; pmd_t *pmd = NULL; pte_t *pte = NULL;
struct page *mypage;
struct ioctl_arg k_arg;
__memset_generic(&k_arg, 0, sizeof(struct ioctl_arg));
/*get arg*/ err = !access_ok(VERIFY_READ, arg, sizeof(struct ioctl_arg)); if ( err ) { __DEBUG_MSG("access vaild failed\n"); return -EFAULT; }
err = copy_from_user((void *)&k_arg, (const void *)arg, sizeof(struct ioctl_arg)); pid = k_arg.pid; vaddr = k_arg.vaddr;
__DEBUG_MSG("pid:%d vaddr:%lu\n", pid, vaddr); /* *convert vaddr to kernel logic addr *1,get the current task by pid *2,get the phyaddr *3,convert phyaddr to kernel logic addr *4,write sth. in the buffer
*NOTE:step 1 is not necessay,because now current is pointed to task */
__DEBUG_MSG("The pid which user passed in is:%d\n" " The current pid is:%d\n", pid, current->pid); /*get phyaddr*/ pgd = pgd_offset(current->mm, vaddr); if ( pgd_none(*pgd) || pgd_bad(*pgd) ) { __DEBUG_MSG("invalid pgd\n"); retval = -1; goto error; }
pud = pud_offset(pgd, vaddr); if ( pud_none(*pud) || pud_bad(*pud) ) { __DEBUG_MSG("invalid pud\n"); retval = -1; goto error; }
pmd = pmd_offset(pud, vaddr); if ( pmd_none(*pmd) || pmd_bad(*pmd) ) { __DEBUG_MSG("invalid pmd\n"); retval = -1; goto error; } __DEBUG_MSG("before pte_offset_map\n"); pte = pte_offset_map(pmd, vaddr); __DEBUG_MSG("after pte_offset_map\n"); pte_unmap(pte); if ( pte_none(*pte) ) { __DEBUG_MSG("bad pte va: %X pte: %p pteval: %lX\n", vaddr, pte, pte_val(*pte)); retval = -1; goto error; }
phy = (pte_val(*pte)) & PAGE_MASK;
__DEBUG_MSG("the phy addr is %lu\n", phy);
mypage = pte_page(*pte); set_bit(PG_locked, &mypage->flags); atomic_inc(&mypage->_count);
/*convert phy to kernel logic addr*/ laddr = __pa(phy); strcpy((char *)laddr, "hello kmap");
error: return retval; } ------------------------------------user.c------------------------------------- /* *user.c 用户空间部分 */ #include #include #include #include #include #include #include /*getpagesize*/ #include
#include "kernel.h"
#define CHAR_DEV_PATH "/dev/"DEVICE_NAME
int main(int argc, char **argv) { int fd = -1; pid_t pid = 0; unsigned int vaddr = 0; int result = 0; int page_size =0; struct ioctl_arg arg; int retval = 0;
memset(&arg, 0, sizeof(struct ioctl_arg));
/*get page size*/ page_size = getpagesize();
/*get pid*/ pid = getpid();
vaddr = (unsigned long)valloc(page_size); memset((char *)vaddr, 0, page_size); strcpy((char *)vaddr, "hello kmap");
/*assemble arg*/ __DEBUG_MSG("The pid and the vaddr is:%d, %lu\n", pid, vaddr); arg.pid = pid; arg.vaddr = vaddr;
/*open the device*/ fd = open(CHAR_DEV_PATH, O_RDWR);
if ( fd < 0 ) { __DEBUG_MSG("error when open device file:%s\n", strerror(errno)); retval = -1; goto error; }
/*pass the arg to kernel*/ result = ioctl(fd, KMAP_IOC_SET_PID_AND_VADDR, &arg); if ( result ) { __DEBUG_MSG("error when ioctl:%s\n", strerror(errno)); }
printf("after ioctl we get the memory:%s\n", (char *)vaddr);
error: if ( fd != -1 ) { close(fd); } return retval; }
回复于:2007-01-16 20:10:46
请大家重点看一下下面的代码片断,问题应该是出在这里的 /* *convert vaddr to kernel logic addr *1,get the current task by pid *2,get the phyaddr *3,convert phyaddr to kernel logic addr *4,write sth. in the buffer
*NOTE:step 1 is not necessay,because now current is pointed to task */
__DEBUG_MSG("The pid which user passed in is:%d\n" " The current pid is:%d\n", pid, current->pid); /*get phyaddr*/ pgd = pgd_offset(current->mm, vaddr); if ( pgd_none(*pgd) || pgd_bad(*pgd) ) { __DEBUG_MSG("invalid pgd\n"); retval = -1; goto error; }
pud = pud_offset(pgd, vaddr); if ( pud_none(*pud) || pud_bad(*pud) ) { __DEBUG_MSG("invalid pud\n"); retval = -1; goto error; }
pmd = pmd_offset(pud, vaddr); if ( pmd_none(*pmd) || pmd_bad(*pmd) ) { __DEBUG_MSG("invalid pmd\n"); retval = -1; goto error; } __DEBUG_MSG("before pte_offset_map\n"); pte = pte_offset_map(pmd, vaddr); __DEBUG_MSG("after pte_offset_map\n"); pte_unmap(pte); if ( pte_none(*pte) ) { __DEBUG_MSG("bad pte va: %X pte: %p pteval: %lX\n", vaddr, pte, pte_val(*pte)); retval = -1; goto error; }
phy = (pte_val(*pte)) & PAGE_MASK;
__DEBUG_MSG("the phy addr is %lu\n", phy);
mypage = pte_page(*pte); set_bit(PG_locked, &mypage->flags); atomic_inc(&mypage->_count);
/*convert phy to kernel logic addr*/ laddr = __pa(phy); strcpy((char *)laddr, "hello kmap");
回复于:2007-01-16 22:31:23
这个问题,难道比较大,你还是多打印点调试信息,慢慢调试.
回复于:2007-01-16 22:34:21
我找到问题所在了。 用__va代替__pa 将物理地址转化为内核的逻辑地址是用__va而不是__pa,改动以后程序工作正常。
回复于:2007-01-20 14:17:15
虽然你这个方法是可行的,却是不提倡的方法, 应该用mmap()的方法。 为你的设备file_operations定义一个mmap函数, 然后用户态调用系统调用mmap()把设备分配的内存映射到用户态, 这是标准的做法。
回复于:2007-01-20 18:33:41
谢谢轮子大侠,mmap的方法我也尝试过了,我也想尝试一下这种方法? 不知道为什么这种方法是不提倡的?能否再深入的指点一下小弟?
回复于:2007-01-21 13:46:39
我觉得是移植性不好,很多操作和体系结构,内存布局有关系。 需要处理很多特殊的情况。
比如用户态virtual地址和内核可以访问的virtual地址之间的转换 你用__va()来转换,但如果你的机器内存>896M, 这个页面是highmem的话,就是不对的,因为内核没有这种映射。 需要另外建立内核映射。
另外,你的程序还有很多操作也不规范, 比如你设置PG_lock,这个标志通常在改变页面状态时才设置, 并且是短时的,比如要进行磁盘I/O时。 你lock住page后,可能会影响到内核的操作。
还有访问pte应该加锁(pgtable lock)。
mmap()的方法是传统的unix的标准方法,内核有标准的支持。 换句话说,是unix的传统,所以我觉得mmap()的方法是对的。
回复于:2007-01-22 15:35:36
r u wheelz from CLF?
how about ur health?
俺喜欢忽悠,哈哈。
[ 本帖最后由 sisi8408 于 2007-1-22 15:39 编辑 ]
|
|
原文链接: 转载请注明作者名及原文出处
| |