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

news2024/10/7 4:29:04

关于用户态的文件操作函数我们知道有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

参考链接:
https://blog.csdn.net/weixin_42343585/article/details/81205246
https://www.freesion.com/article/4416178909/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/73575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业网站怎么建立?【企业网站的建设】

不少的实体企业都会考虑建立一个自己的企业网站&#xff0c;那么在企业网站的建设之前需要做好功课。那么企业网站怎么建立&#xff1f;下面给大家说说大概的流程。 1、申请域名 企业可以申请一个和自己企业名称相关的域名&#xff0c;而且域名尽量不要太长&#xff0c;否则难…

Java学习之多态数组

目录 一、定义 二、举例说明 要求1 父类-Person 子类-Student 子类-Teacher main类 运行结果 要求2 思路分析 main类中的代码 运行结果 一、定义 数组的定义类型为父类类型&#xff0c; 里面保存的实际元素类型为子类类型&#xff08;也可以有父类&#xff09; 二、…

Cat.1无线数据传输终端/Cat.1 DTU/LTE Cat.1 DTU/Cat 1模组功能

LTE Cat.1无线数传终端F2C16将借助成熟的LTE网络以更好的覆盖、更快的速度、更低的延时&#xff0c;完美取代传统2G/3G网络&#xff0c;为中低速率物联网行业提供优质的无线连接服务。 工业级芯片设计&#xff0c;设备稳定联网 ●全工业级芯片设计&#xff0c;宽温宽压&#xf…

「虚拟社交」爆火,资深玩家「当道」

⬆️“政企数智办公行业研究报告及融云新品发布会”明天直播&#xff01; 一切应用都将社交化。关注【融云全球互联网通信云】回复【融云】抽取高颜值大容量高端可乐保温杯哦~ 中国政企数智办公平台行业研究报告 融入社交能力&#xff0c;创造增长奇迹。激活用户在不同场景的社…

6个改善【客户体验】的自动电子邮件营销回复示例

关键词&#xff1a;客户体验、电子邮件营销 电子邮件自动回复器是将跨境电商的客户体验 (CX) 提升到一个新水平的一种方式。为了帮助跨境电商决定应该设置哪种自动电子邮件&#xff0c;我们汇总了对客户体验影响最大的 六个电子邮件自动回复示例。 这里有一些统计数据可以正确看…

国内各行业领域是否能通过与元宇宙和虚拟数字人的结合振兴数藏经济?

在过去几年&#xff0c; NFT和数字藏品已被广泛用于数字经济。 根据中国数字藏品行业协会早在2021年发布的市场发展报告中就指出了当年中国数字藏品市场规模达到2166亿元。 今年&#xff0c;国内元宇宙概念被炒得火热&#xff0c;从故宫博物院联合腾讯、网易等推出「故宫系列」…

关于C++11

文章目录&#x1f60d;C11优势&#x1f60e; 列表初始化&#x1f601;变量类型推导&#x1f44c;为什么需要类型推导&#x1f44d;decltype类型推导&#xff08;了解&#xff09;&#x1f61c;final 与 overridefinal&#x1f91e;override❤️默认成员函数控制&#x1f929;显…

TH10-数据统计与内容审核

TH10-数据统计与内容审核1、用户冻结解冻1.1 用户冻结ManageControllerManageService1.2 用户解冻ManageControllerManageService1.3 查询数据列表UserInfoManageService1.4 探花系统修改UserFreezeService2、数据统计2.1 数据采集2.1.1 部署RabbitMQ2.1.2 消息类型说明2.1.3 实…

使用dd+hexdump命令修改环境变量的值和升级uboot

前言 这篇写的较细&#xff0c;使用dd擦除emmc本来就是比较危险的事情&#xff0c;所以一定要细致。哪里没看明白的&#xff0c;赶紧留言问我&#xff0c;可不能存有侥幸心理。 思路大概就是&#xff1a; 1 先从emmc把数据读出来&#xff0c;放一个镜像文件里&#xff0c;使…

【整理】Python全栈技术学习路线

【整理】Python全栈技术学习路线【阶段一】Python基础Linux【阶段二】多任务编程服务器前端基础【阶段三】数据库mini Web框架【阶段四】Dhango框架美多商城项目【阶段五】DRF框架美多商城后台【阶段六】项目部署Flask框架Hm头条【阶段七】人工智能基础推荐系统基础Hm头条推荐系…

带你了解extern “C“

1.extern “C” 这个语法是c的语法。我们知道在一个.c文件中调用另一个.c中实现的函数是没有任何问题的&#xff0c;一个.cpp文件调用另一个.cpp文件中实现的函数也是没有问题的。但是我们如果想要在一个.cpp文件调用另一个.c文件中实现的函数&#xff0c;或者在一个.c文件中调…

双调序列

目录 双调序列 思路: 代码: 时间复杂度: 总结: 题目链接: 双调序列 题目描述&#xff1a; XJ编程小组的童鞋们经常玩一些智力小游戏&#xff0c;某月某日&#xff0c;小朋友们又发明了一种新的序列&#xff1a;双调序列&#xff0c;所谓的双调呢主要是满足如下条件描述…

TensorFlow之分类模型-2

1 基本概念 2 文本分类与情感分析 获取数据集 加载数据集 训练数据集 性能设置 为了提升训练过程中数据处理的性能&#xff0c;keras技术框架提供数据集缓存的功能&#xff0c;使用缓存可以避免读取磁盘数据集时由于IO消耗太多而出现性能瓶颈的问题&#xff0c;如果数据集…

操作系统的主要功能是什么

操作系统的主要功能是进程管理、存储管理、设备管理、文件管理、作业管理。 计算机系统的资源可分为设备资源和信息资源两大类。 操作系统位于底层硬件与用户之间&#xff0c;是两者沟通的桥梁。 1、进程管理&#xff0c;其工作主要是进程调度&#xff0c;在单用户单任务的情…

opcj2-盘点几个常见的Java开源脚手架

很多人抱怨自己是CURDer&#xff0c;很多时候就是在简单的修修改改。如果不书序SSM&#xff08;Spring、SpringMVC和Mybatis&#xff09;套路的人可能开始的时候会感觉非常吃力。但是熟悉之后发现其实就这么回事。SpringMVC负责响应对外接口&#xff0c;Mybatis负责数据库的访问…

TF4-圈子功能

TF4-圈子功能1、首页推荐1.1、接口分析1.2、功能实现1.2.1 controller1.2.2 service1.2.3 API接口1.2.4 请求dto对象2、MongoDB集群3、圈子功能2.1、功能说明1.2、实现方案分析1.3、技术方案(重点)1.4、表结构设计4、圈子实现3.1、环境搭建3.1.1、mongo主键自增3.1.2、实体类Mo…

基于matlab的SVM支持向量机分类仿真,核函数采用RBF函数

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 支持向量机&#xff08;support vector machines, SVM&#xff09;是二分类算法&#xff0c;所谓二分类即把具有多个特性&#xff08;属性&#xff09;的数据分为两类&#xff0c;目前主流机器学…

如何清理 docker 磁盘空间 附讲解(全)

目录前言1. Docker System 命令1.1 docker system df1.2 docker system prune2. 冗余容器或镜像3. 限制容器日志前言 补充docker知识点&#xff0c;可看我之前的文章&#xff1a;Docker零基础从入门到精通&#xff08;全&#xff09; docker 镜像特别容易占空间&#xff0c;稍…

基于Redis实现高性能延时消息队列

最近在倒腾自建博客后端系统&#xff0c;需要用到延时任务的功能&#xff0c;但手头只有一套MySQL和Redis&#xff0c;如果搞一套MQ成本有点大&#xff0c;于是想着用redis实现延时消息队列。有些场景用数据库的定时扫表也能简单实现延时消息的功能&#xff0c;不过对于我这边的…

Bitmap,布隆过滤器初步了解

布隆过滤器使用教程 文章目录布隆过滤器使用教程1.背景2.什么是Bitmap3.布隆过滤器3.1 什么是布隆过滤器3.2 布隆过滤器的作用3.3 布隆过滤器的基本原理4.布隆过滤器的实现Guava和Redisson4.1 实现思路4.2 SpringBoot实现这些操作Bitmap,guava,redisson布隆过滤器1.背景 最近公…