内核态的文件操作函数:filp_open、filp_close、vfs_read、vfs_write、set_fs、get_fs

简介: 内核态的文件操作函数:filp_open、filp_close、vfs_read、vfs_write、set_fs、get_fs

关于用户态的文件操作函数我们知道有open、read、write这些。但是这些的实现都是依赖于库的实现,但是在内核态是没有库函数可用的。最近做测试,在内核态中,需要学习一下在内核态里面的文件操作函数。分为三对出现。

感谢前辈的优秀文章,参考链接在文末

这些的函数就在linux/fs.h和asm/uaccess.h中存在。

1、第一对:filp_open、filp_close–文件开关操作

filp_open

struct file* filp_open(const char* filename, int open_mode, int mode);
第一个参数表明要打开或创建文件的名称(包括路径部分)。
第二个参数文件的打开方式,其取值与标准库中的open相应参数类似,可以取O_CREAT,O_RDWR,O_RDONLY等。
第三个参数创建文件时使用,设置创建文件的读写权限,其它情况可以设为0

filp_close

int filp_close(struct file *filp, fl_owner_t id);
第一个参数是filp_open返回的file结构体指针
第二个参数基本上都是NULL

2、第二对:vfs_read、vfs_write–文件读写操作

//读文件
ssize_t vfs_read(struct file *filp, char __user *buffer, size_t len, loff_t *pos);
//写文件
ssize_t vfs_write(struct file *filp, const char __user *buffer, size_t len, loff_t *pos);
filp:文件指针,由filp_open()函数返回。
buffer:缓冲区,从文件中读出的数据放到这个缓冲区,向文件中写入数据也在这个缓冲区。
len:从文件中读出或者写入文件的数据长度。
pos:为文件指针的位置,即从什么地方开始对文件数据进行操作。

3、第三队:set_fs、get_fs

在buffer前面都有一个__user修饰符,这要求buffer指针应该指向用户的空间地址。如果在内核中使用上述的两个函数直接进行文件操作,将内核空间的指针传入的时候,函数会返回失败EFAULT。但在Linux内核中,一般不容易生成用户空间的指针,或者不方便独立使用用户空间内存。为了使这两个函数能够正常工作,必须使得这两个函数能够处理内核空间的地址。

使用set_fs()函数可以指定上述两个函数对缓冲区地址的处理方式,原型如下:

  • 这个函数改变内核对内存检查的处理方式,将内存地址的检查方式设置为用户指定的方式。参数fs取的值有两个:USER_DS和KERNEL_DS。分别代表用户空间和内核空间。
  • 在默认情况下,内核对地址的检查方式为USER_DS,即按照用户空间进行地址检查并进行用户地址空间到内核地址空间的变换。如果函数中要使用内核地址空间,需要使用set_fs(KERNEL_DS)函数进行设置。与set_fs()函数对应,get_fs()函数获得当前的设置,在使用set_fs()之前先调用get_fs()函数获得之前的设置对文件进行操作后,使用set_fs()函数还原之前的设置。

内核空间文件续写的框架为:

mm_segmen_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
...
set_fs(old_fs)

**注意:**使用vfs_read()和vfs_write()的时候,要注意最后的参数loff_t *pos,pos所指向的值必须要进行初始化,表明从文件的什么位置进行读写。使用此参数可以对续写文件的位置进行设定,这可以完成用户空间中lseek()函数的功能

整个栗子

1、简单版

下面是一个使用内核空间的文件读函数从文件中读取数据的例子:

ssize_t ReadFile(struct file *filp, char __user *buffer, size_t len, loff_t *pos)
{
    ssize_t count = 0;
    oldfs = get_fs();
    set_fs(KERNEL_DS);
    count = file->f_op->read(filp, buf, len, &file->f_pos);
    set_fs(oldfs);
    return count;
}

2、进阶版

源码

//kernel
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/netlink.h>
#include <linux/spinlock.h>
#include <net/sock.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
#include <linux/igmp.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
//user
#define DBGPRINT printk
//内核程序使用file_open来打开文件
struct file *SIPFW_OpenFile(const char *filename, int flags, int mode)
{
  struct file *f = NULL;
  DBGPRINT("==>SIPFW_OpenFile\n");
  f = filp_open(filename, flags, 0);
  if (!f || IS_ERR(f))
  {
    f = NULL;
  }
  DBGPRINT("<==SIPFW_OpenFile\n");
  return f;
}
ssize_t SIPFW_ReadLine(struct file *f, char *buf, size_t len)
{
#define EOF (-1)
  ssize_t count = -1;
  mm_segment_t oldfs;
  struct inode *inode;
  //DBGPRINT("==>SIPFW_ReadLine\n");
  if (!f || IS_ERR(f) || !buf || len <= 0) 
  {
    goto out_error;
  }
  if (!f || !f->f_inode)
  {
    goto out_error;
  }
  inode = f->f_inode;
  if (!(f->f_mode & FMODE_READ))
  {
    goto out_error;
  }
  if (f->f_op /*&& f->f_op->read*/) 
  {
    oldfs = get_fs();
    set_fs(KERNEL_DS);
    count = 0;
    if (vfs_read(f, buf, 1, &f->f_pos) <= 0)
    {
      DBGPRINT("file read failure\n");
      goto out;
    }
    if (*buf == EOF)
    {
      DBGPRINT("file EOF\n");
      goto out;
    }
    count = 1;
    while (*buf != EOF && *buf != '\0' && *buf != '\n' && *buf != '\r' 
        && count < len  && f->f_pos <= inode->i_size)
    {
      buf   += 1;
      count   += 1;
      if (vfs_read(f, buf, 1, &f->f_pos) <= 0) 
      {
        count -= 1;
        break;
      }
    }
  } 
  else
  {
    DBGPRINT("goto out_error\n");
    goto out_error;
  }
  if (*buf == '\r'|| *buf =='\n' ||*buf == EOF ) 
  {
    *buf = '\0';
    count -= 1;
  } 
  else
  {
    buf += 1;
    *buf = '\0';
  }
out:
  set_fs(oldfs);
out_error:
  //DBGPRINT("<==SIPFW_ReadLine %d\n", count);
  return count;
}
// ssize_t SIPFW_WriteLine(struct file *f, char *buf, size_t len)
// {
//  ssize_t count = -1;
//  mm_segment_t oldfs;
//  struct inode *inode;
//  DBGPRINT("==>SIPFW_WriteLine\n");
//  if (!f || IS_ERR(f) || !buf || len <= 0) 
//  {
//    goto out_error;
//  }
//  if (!f || !f->f_dentry || !f->f_dentry->d_inode)
//  {
//    goto out_error;
//  }
//  inode = f->f_dentry->d_inode;
//  if (!(f->f_mode & FMODE_WRITE) || !(f->f_mode & FMODE_READ) )
//  {
//    goto out_error;
//  }
//  if (f->f_op && f->f_op->read && f->f_op->write) 
//  {
//    //f->f_pos = f->f_count;
//    oldfs = get_fs();
//    set_fs(KERNEL_DS);
//    count = 0;
//    count = f->f_op->write(f, buf, len, &f->f_pos) ;
//    if (count == -1)
//    {
//      goto out;
//    }   
//  } 
//  else  
//  {
//    goto out_error;
//  }
// out:
//  set_fs(oldfs);
// out_error:
//  DBGPRINT("<==SIPFW_WriteLine\n");
//  return count;
// }
void SIPFW_CloseFile(struct file *f)
{
  DBGPRINT("==>SIPFW_CloseFile\n");
  if(!f)
    return;
  filp_close(f, current->files);
  DBGPRINT("<==SIPFW_CloseFile\n");
}
int SIPFW_HandleConf(void)
{
  int retval = 0,count;
  int l = 0;
  char *pos = NULL;
  struct file *f = NULL;
  char line[256] = { 0 };
  DBGPRINT("==>SIPFW_HandleConf\n");
  //  提前建一个/etc/sipfw.conf文件吧
  f = SIPFW_OpenFile("/etc/sipfw.conf", O_RDWR, 0);
  if(f == NULL)
  {
    retval = -1;
    DBGPRINT("SIPFW_OpenFile called failure\n");
    goto EXITSIPFW_HandleConf;
  }
  while((count = SIPFW_ReadLine(f, line, 256)) > 0)
  {
    pos = line;
    DBGPRINT("line = %d, data: %s\n", l, line);\
    l++;
    memset(line, 0, sizeof(line));
  }
  SIPFW_CloseFile(f); 
EXITSIPFW_HandleConf:
  DBGPRINT("<==SIPFW_HandleConf\n");
  return retval;
}
static int __init SIPFW_Init(void)
{
  int ret = -1;
  DBGPRINT("==>SIPFW_Init\n");
  ret = SIPFW_HandleConf();
  DBGPRINT("<==SIPFW_Init\n");
  return ret;
}
static void __exit SIPFW_Exit(void)
{
  DBGPRINT("==>SIPFW_Exit\n");
  DBGPRINT("<==SIPFW_Exit\n");
}
module_init(SIPFW_Init);
module_exit(SIPFW_Exit);
MODULE_LICENSE("GPL/BSD");

Makefile:

MODULE_NAME :=main_file
obj-m :=$(MODULE_NAME).o
KERNELDIR = /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
  $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
  $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

测试中读取的文件:(/etc/sipfw.conf)

运行效果:

https://www.freesion.com/images/776/0b01da97ef33a4353bbbd347575fd938.png


目录
相关文章
|
6月前
|
SQL HIVE
数仓学习-----named_struct和collect_set函数
数仓学习-----named_struct和collect_set函数
116 5
|
14天前
|
Java Python
gc模块的set_threshold函数
gc模块的set_threshold函数
|
4月前
|
存储 JSON 关系型数据库
mysql中find_in_set()函数用法详解及增强函数
总结而言,`FIND_IN_SET()`是MySQL中处理由逗号分隔的字符串列表的一种便捷方法,尤其适用于列表相对较短且不经常更改的场景。然而,对于更为复杂的需要高性能和可扩展性的数据库设计,它可能不是最优选择,应考虑使用更加正规化的数据库结构。
542 2
mysql中find_in_set()函数用法详解及增强函数
CF443A Anton and Letters(去重set函数)
CF443A Anton and Letters(去重set函数)
44 0
|
4月前
|
存储 语音技术 Python
语音识别,函数综合案例,黑马ATM,/t/t一个对不齐,用两个/t,数据容器入门,数据容器可以分为列表(list)、元组(tuple)、字符串(str)、集合(set)、字典(dict)
语音识别,函数综合案例,黑马ATM,/t/t一个对不齐,用两个/t,数据容器入门,数据容器可以分为列表(list)、元组(tuple)、字符串(str)、集合(set)、字典(dict)
|
6月前
|
数据采集 Python
10个Python set 常用操作函数!,bilibili面试题
10个Python set 常用操作函数!,bilibili面试题
10个Python set 常用操作函数!,bilibili面试题
|
SQL 分布式计算 大数据
`collect_set`函数用于将一组数据收集到一个集合中
`collect_set`函数用于将一组数据收集到一个集合中
241 1
|
分布式计算 MaxCompute
MaxCompute中,collect_set函数是一个聚合函数
MaxCompute中,collect_set函数是一个聚合函数
214 1
|
前端开发 JavaScript API
ES6-ES11-第一部分-let、const、解构赋值、模板字符串、简化对象写法、箭头函数、函数参数默认值、rest 参数、扩展运算符、Symbol、迭代器、生成器、Promise、Set、Map(五)
ES6-ES11-第一部分-let、const、解构赋值、模板字符串、简化对象写法、箭头函数、函数参数默认值、rest 参数、扩展运算符、Symbol、迭代器、生成器、Promise、Set、Map(五)
|
6月前
|
JSON 前端开发 JavaScript
【面试题】面试官:请你实现一个深拷贝,那如果是正则/set/函数怎么拷贝?
【面试题】面试官:请你实现一个深拷贝,那如果是正则/set/函数怎么拷贝?