Linux中file文件的研究??
编者:weirdo
编著时间:2020-6-12
编者QQ:2651293248
编写说明:AT24C256这样的字符设备需要一个指针来指明读写地址,这里主要研究file结构体中的f_ops所代表的地址偏移。
1. 文件结构定义
struct file {
struct list_head f_list;
struct dentry *f_dentry;
struct vfsmount *f_vfsmnt;
struct file_operations *f_op; //设备文件操作结构体
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode; //文件打开模式
loff_t f_pos; //文件对应的读写偏移地址
unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
int f_error;
size_t f_maxcount;
unsigned long f_version;
/* needed for tty driver, and maybe others */
void *private_data; //用于读报临时保存设备结构体指针
/* preallocated helper kiobuf to speedup O_DIRECT */
struct kiobuf *f_iobuf;
long f_iobuf_lock;
};
2. 实验测试
实验结果
实验代码:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#define DEV_NAME "myfile"
#define DEV_SIZE 0x1000
static int file_open(struct inode *inode, struct file *filp)
{
printk("Open: filp->f_pos = %lld \n", filp->f_pos);
return 0;
}
static int file_close(struct inode *inode, struct file *filp)
{
printk("Close: filp->f_pos = %lld \n", filp->f_pos);
return 0;
}
static ssize_t file_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned char dat;
int ret;
printk("Write: The value filp->f_pos = %lld, *ppos = %lld !\n", filp->f_pos, *ppos);
ret = copy_from_user(&dat, buf, 1);
printk("Write: The value = %c \n", dat);
printk("Write: The value filp->f_pos = %lld, *ppos = %lld !\n", filp->f_pos, *ppos);
return ret;
}
static ssize_t file_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned char dat = 0x31;
int ret;
printk("Read: The value filp->f_pos = %lld, *ppos = %lld !\n", filp->f_pos, *ppos);
ret = copy_to_user(buf, &dat, 1);
printk("Read: The value filp->f_pos = %lld, *ppos = %lld !\n", filp->f_pos, *ppos);
return ret;
}
loff_t file_llseek (struct file *filp, loff_t offset, int whence)
{
loff_t new_pos; //新偏移量
loff_t old_pos = filp->f_pos; //旧偏移量
printk("<kernel>: file llseek !\n");
switch(whence){
case SEEK_SET:
new_pos = offset;
break;
case SEEK_CUR:
new_pos = old_pos + offset;
break;
case SEEK_END:
new_pos = DEV_SIZE + offset;
break;
default:
printk("<kernel>: Unknow whence !\n");
return - EINVAL;
}
//偏移量的合法检查
if(new_pos < 0 || new_pos > DEV_SIZE){
printk("Llseek: <kernel>: Set offset error !\n");
return - EINVAL;
}
filp->f_pos = new_pos;
printk("Llseek: <kernel>: The new pos = %lld and offset = %lld!\n", new_pos, offset);
return new_pos; //正确返回新的偏移量
}
static const struct file_operations file_fops= {
.owner = THIS_MODULE,
.open = file_open,
.release = file_close,
.read = file_read,
.write = file_write,
.llseek = file_llseek,
};
static struct miscdevice file_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &file_fops,
};
static int __init file_init(void)
{
int ret_val;
pr_info("File Test Init\n");
/* Register the device with the kernel */
ret_val = misc_register(&file_misc);
if (ret_val != 0) {
pr_err("could not register the misc device mydev");
return ret_val;
}
pr_info("mydev: got minor %i\n",file_misc.minor);
return 0;
}
static void __exit file_exit(void)
{
pr_info("File Test Exit\n");
/* unregister the device with the Kernel */
misc_deregister(&file_misc);
}
module_init(file_init);
module_exit(file_exit);
MODULE_AUTHOR("WEIRDO");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("This driver for test struct file");
测试程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<termios.h>
#include<string.h>
int main()
{
int i,fd, ret;
char dat;
fd = open("/dev/myfile", O_RDONLY);
if (fd == -1)
{
printf("Can not open /dev/myfile!\n");
exit(0);
}
printf("Open file success ! \n");
for(i=0;i<10;i++)
{
read(fd,&dat,1);
ret = lseek(fd, 5, SEEK_CUR);
printf("AD0 = %d\n",ret);
}
close(fd);
return 0;
}
实验注意:filp->f_ops和参数*ppos是一直的,我们需要修改filp->f_ops来使得文件指针做出改变,普通的读写操作不会使得文件指针自动的增加。