#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>

#defineMY_FILE"/root/LogFile"

charbuf[128];
structfile*file=NULL;

staticint__init init(void)
{
mm_segment_t old_fs;
printk("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);
if(IS_ERR(file)){
printk("error occured while opening file %s, exiting...\n",MY_FILE);
return0;
}

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

old_fs=get_fs();
set_fs(KERNEL_DS);
file->f_op->write(file,(char*)buf,sizeof(buf),&file->f_pos);
set_fs(old_fs);

return0;
}

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

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

其中:
typedefstruct{
unsignedlongseg;
}mm_segment_t;

#defineKERNEL_DSMAKE_MM_SEG(0xFFFFFFFFUL)

#defineMAKE_MM_SEG(s)((mm_segment_t){(s)})

基本思想:
一个是要记得编译的时候加上-D__KERNEL_SYSCALLS__
另外源文件里面要#include <linux/unistd.h>

如果报错,很可能是因为使用的缓冲区超过了用户空间的地址范围。一般系统调用会要求你使用的缓冲区不能在内核区。这个可以用set_fs()、get_fs()来解决。在读写文件前先得到当前fs:
mm_segment_t old_fs=get_fs();
并设置当前fs为内核fs:set_fs(KERNEL_DS);
在读写文件后再恢复原先fs: set_fs(old_fs);
set_fs()、get_fs()等相关宏在文件include/asm/uaccess.h中定义。
个人感觉这个办法比较简单。

另外就是用flip_open函数打开文件,得到struct file
*的指针fp。使用指针fp进行相应操作,如读文件可以用fp->f_ops->read。最后用filp_close()函数关闭文件。
filp_open()、filp_close()函数在fs/open.c定义,在include/linux/fs.h中声明。

解释一点:
系 统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数(比如上面的buf),它默认会认为来自用户空间,在->write()函数中,
为了保护内核空间,一般会用get_fs()得到的值来和USER_DS进行比较,从而防止用户空间程序“蓄意”破坏内核空间;

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

补充:
我看了一下源码,在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())改变了用户空间的限制,即扩大了用户空间范围,因此即可使用在内核中的参数了。

技术
©2019-2020 Toolsou All rights reserved,
PYTHON入门期末复习汇总Python+OpenCV人脸识别技术详解员工网上晒腾讯年终奖:每人100股公司股票 价值超6万元!中国最长高铁正式开通!跑完全程最快30.5小时Unity 场景异步加载(加载界面的实现)NOI2019 游记用C++跟你聊聊“原型模式” (复制/拷贝构造函数)比尔·盖茨:疫情后彻底恢复正常可能要到2022年末这些歌,程序员千万万万万别听!随机森林篇 R语言实现