现在的位置: 首页 > 综合 > 正文

内核空间和用户空间之二:在内核空间中系统调用用户空间的内容

2012年06月28日 ⁄ 综合 ⁄ 共 2580字 ⁄ 字号 评论关闭

         在linux内核中操作用户文件的方法:使用get_fs()和set_fs(KERNEL_DS)调用系统调用改变权限,再通过内核文件操作函数,可以访问用户空间。

(1)宏介绍
     源码在include/asm/uaccess.h中,有如下定义:

    #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) 
    #define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) 
    #define USER_DS MAKE_MM_SEG(PAGE_OFFSET) 
    #define get_ds() (KERNEL_DS) 
    #define get_fs() (current->addr_limit) 
    #define set_fs(x) (current->addr_limit = (x)) 

而它的注释也很清楚: /* * The fs value determines whether argument validity checking should be performed or not. If get_fs() == USER_DS, checking is performed, with
get_fs() == KERNEL_DS, checking is bypassed.  For historical reasons, these macros are grossly misnamed. */

因此可以看到,fs的值是作为是否进行参数检查的标志。当系统调用的参数要求来自用户空间,在内核中使用系统调用的时候,set_fs(get_ds())改变了用户空间的限制,即扩大了用户空间范围,因此即可使用在内核中的参数了。

(2)函数实例,在一个模块中新建一个文本,并往文本中写入内容。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/syscalls.h>
#include <asm/unistd.h>
#include <asm/uaccess.h>


#define MY_FILE "LogFile"
//#define MY_FILE "/root/LogFile"

asmlinkage long sys_open(const char __user *filename,int flags, int mode);

char buf[128];
struct file *file = NULL;

 

static int __init init(void)
{
        mm_segment_t old_fs;
        int ret = 0;
        printk("<0>""Hello, I'm the module that intends to write messages to file./n");


        if(file == NULL)
                file = filp_open(MY_FILE, O_RDWR | O_APPEND | O_CREAT, 0644);
        //                 file = sys_open(MY_FILE, O_RDWR | O_APPEND | O_CREAT, 0644);
        if (IS_ERR(file)) {
                printk("<0>""error occured while opening file %s, exiting.../n", MY_FILE);
                return 0;
        }
        memset(buf,0x61,128);

        sprintf(buf,"%s", "The Messages is xjkdljflk.");

        old_fs = get_fs();
        printk("<0>""old_fs is %lx/n",old_fs.seg);
        set_fs(KERNEL_DS);
        old_fs = get_fs();
        printk("<0>""old_fs is %lx/n",old_fs.seg);
        printk("<0>""KERNEL_DS is %lx/n",KERNEL_DS);
        ret = file->f_op->write(file, (char *)buf, sizeof(buf), &file->f_pos);
        if(ret < 0)
                printk("<0>""file write failed/n");
        set_fs(old_fs);


        return 0;
}

static void __exit fini(void)
{
        if(file != NULL)
                filp_close(file, NULL);
}

module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");

     使用的Makefile如下:

ifeq ($(KERNELRELEASE),)

KERNELDIR := /usr/src/linux-headers-2.6.32-28-generic
PWD := $(shell pwd)


modules:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c 
.PHONY:modules modules_install clean
else
obj-m := call.o
endif

       编译完后,执行insmod和rmmod即可看到想要的结果。

(3)过程说明

         用flip_open函数打开文件,得到struct file *的指针fp。使用指针fp进行相应操作,如读文件可以用fp->f_ops->read,最后用filp_close()函数关闭文件。

        总之:要在内核空间使用系统调用,此时传递给->write()的参数地址就是内核空间的地址了,在USER_DS之上(USER_DS ~ KERNEL_DS),如果不做任何其它处理,在write()函数中,会认为该地址超过了USER_DS范围,所以会认为是用户空间的“蓄意破坏”,从而不允许进一步的执行; 为了解决这个问题; set_fs(KERNEL_DS);将其能访问的空间限制扩大到KERNEL_DS,这样就可以在内核顺利使用系统调用了!

抱歉!评论已关闭.