linux用户态通过文件IO或标准IO对文件进行 open()/fopen() , read()/fread() , write()/fwrite() , close()/fclose() 操作。那么linux内核态怎么进行文件操作呢?
linux内核态在 /kernel/include/linux/fs.h 中提供了有相对应的函数供我们对文件进行IO操作。
功能 | 函数原型 |
打开文件 | struct file *filp_open(const char *filename, int flags, int mode) |
读文件 | ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) |
写文件 | ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) |
关闭文件 | int filp_close(struct file *filp, fl_owner_t id) |
一、函数定义
1、filp_open()函数
struct file *filp_open(const char *filename, int flags, umode_t mode)
功能:打开文件
参数:
- filename:要打开的文件的路径。
- flags:文件的读写模式。常用的值有 O_RDONLY , O_WDONLY , O_RDWR , O_CREAT。这个参数的值与文件IO中的一样,它们被定义在 /kernel/include/uapi/asm-generic/fcntl.h 中。
- mode:文件的权限
返回值:指向所打开文件的结构体指针。
2、filp_close()函数
int filp_close(struct file *filp, fl_owner_t id)
功能:关闭文件
参数:
- filp:文件句柄
- id:一般填0即可
返回值:0表示成功关闭。
3、vfs_read()函数
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
功能:读文件
参数:
- file:文件句柄
- buf:存放所读取的内容的数组。
注意:使用在内核态申请的字符数组,则要在调用这个函数之前先执行一下以下代码:
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
/* vfs_read/vfs_write 操作相关代码 */
/* ... ... */
set_fs(old_fs);
- count:期望读取的最大大小
- pos:读取位置
返回值:实际读取到的数据大小
4、vfs_write()函数
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
与 vfs_read() 类似,就不再赘述了。
二、示例
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/syscalls.h>
#include <linux/slab.h>
#include <asm/segment.h>
#include <linux/mm.h>
#define FILE_PATH "/dev/mmcblk0p9" //表示 animation.yuv 文件分区
#define YUV420_SIZE (384 * 288 * 3 / 2) //384 * 288 yuv420图片的大小
static struct task_struct *animation_thread;
/* 内核态将图片文件读出,并写入DDR显存中 */
static int animation_work_thread(void *arg)
{
unsigned int volatile *pVoAddr;
struct file *fp;
char *buff_animation;
loff_t pos;
mm_segment_t fs;
int i;
int flag = 0;
/* 1.打开分区文件 */
fp = filp_open(FILE_PATH, O_RDONLY, 0); //打开emmc分区
if (IS_ERR(fp)){
printk("filp_open error\n");
return -1;
}
/* 一般系统调用会要求你使用的缓冲区不能在内核区。这个可以用set_fs()、get_fs()来解决 */
fs = get_fs();
set_fs(KERNEL_DS);
buff_animation = (char *)kzalloc(YUV420_SIZE, GFP_KERNEL); //分配内存1
if (!buff_animation)
return -ENOMEM;
pVoAddr = (unsigned int volatile *) phys_to_virt(0x22400000); //物理地址映射为虚拟地址
/* 循环显示动画 */
while(1)
{
pos = 0;
for(i = 0; i < 75; i++) //轮询显示75张图
{
pos = i * YUV420_SIZE;
vfs_read(fp, buff_animation, YUV420_SIZE, &pos);
memcpy(pVoAddr, buff_animation, YUV420_SIZE); //数据拷贝到VO 显存
msleep(50);
}
}
exit:
kfree(buff_animation); //释放内存
set_fs(fs);
filp_close(fp, NULL); //关闭文件
}
/* 创建线程 */
static int __init animation_init(void)
{
/* 内核态创建线程 */
animation_thread = kthread_run(animation_work_thread, NULL, "animation_thread");
if(!animation_thread) {
printk("%s, %s, %d\n", __FILE__, __func__, __LINE__);
return -EINVAL;
}
return 0;
}
late_initcall(animation_init); //arch_initcall 内核启动后自动调用 animation_init()函数