llseek接口函数属于驱动file_operation结构体内部函数的一部分。
llseek函数实现的目的:为了使用用户空间的llseek()系统调用这个函数功能来移动设备的文件指针,然后读/写接口就可以对移动后的位置进行读取功能,而不是每次读/写都只能从0开始一次读取全部数据。
综上所述:llsee函数在驱动和应用程序都存在(即用户空间和内核空间都存在),应用程序调用llseek函数就会在内核驱动中被调用,实现的功能是修改驱动中write/read函数的文件指针。
被修改的对象:对应打开文件的位置指针。可以看成是在一块内存里面(因为驱动中调用write/read函数时,也是要申请或者开辟一块内核空间进行读写),调用write/read函数来操作,其传入的参数有上一级的调用者传入给它们,其中就包括了当前的文件指针。
file_operation结构体的定义如下
/*
* NOTE:
* all file operations except setlease can be called without
* the big kernel lock held in all filesystems.
*/
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
};
llseek函数原型为:loff_t (*llseek) (struct file* filp,loff_t offset, int whence);
- struct file* 代表上一级调用函数传入的已打开文件,该结构体与struct inode* 区别在于struct inode*用于代表一个文件,但是该文件可以是未打开的。
- loff_t代表用户空间的文件光标移动数量值,可以是正数,也可以是负数。
- int whence代表移动光标的参考位置,有3种(即当前光标、文件开头、文件结尾)。
- 返回值:非负数,当前文件的指针位置;负数,函数调用失败。
实现llseek函数框架:
#define DEV_SIZE (4)
loff_t first_chardev_llseek(struct file* filp,loff_t off,int whence){
loff_t NewPos=0;
int offset=off;
switch(whence){
case SEEK_SET: //SEEK_SET代表以文件头为偏移起始值
NewPos=offset;
break;
case SEEK_CUP: //SEEK_CUP代表以当前位置为偏移起始值
NewPos=filp->f_pos+offset;
break;
case SEEK_END: //SEEK_END代表以文件结尾为偏移起始值
NewPos=DEV_SIZE+offset;
break;
default:
return -1;
}
if(NewPos<0)
return -1;
filp->f_pos=NewPos;
return NewPos;
}
>>>>>完成上面的llseek函数并且告诉给file_operation结构体后,就可以被应用程序的系统调用所使用,从而在内核中实现文件位置的改变。
>>>>>llseek函数改变了filp->f_pos,返回新的位置。
(接下来是完成符合llseek的write/read函数,主要内容是也是使用函数参数传递进来的文件指针作为依据和位置的定位来实现数据在指定位置的读写)
重写能与llseek配套的write函数框架:
#define DEV_SIZE (4)
ssize_t first_chrdev_write(struct file* file,const char _user* buf,size_t count,loff_t* f_pos){
char led_buffer[DEV_SIZE]={0};
int retval=0;
int i=0;
//判断位置指针大小是否操作内存大小
if(*f_pos>=DEV_SIZE){
goto out;
}
//判断写是否会超出界限
if(*f_pos+count>DEV_SIZE)
count=DEV_SIZE-*f_pos;
//copy_from_user((void*)(led_buffer[*f_pos]),(const void _user*)buf,count)这个函数用到
//了count,表示拷贝的大小。而获得count大小的就是通过文件指针f_pos表示的位置与当前可用内存的大小
//比较,得出还有多少的内存可用。
//可以所llseek修改的就是f_pos,调用write函数后,传入的就是修改过后的值。
if(copy_from_user((void*)(led_buffer[*f_pos]),(const void _user*)buf,count)){
retval=-EFAULT;
goto out;
}
/*对从用户空间拷贝过来的数据进行处理的代码段*/
/*.................................*/
*f_pos+=count;
retval=count;
out:
return retval;
}
重写能与llseek配套的read函数框架:
#define DEV_SIZE (4)
ssize_t first_chrdev_read(struct filr* file,char _user* buf,size_t count,loff_t* f_pos){
int retval=0;
char led_buffer[DEV_SIZE]={2,2,2,2};
if(*f_pos>=DEV_SIZE){
goto out;
}
if(*f_pos+count>DEV_SIZE){
count=DEV_SIZE-*f_pos;
}
if(copy_to_user(buf,&led_buffer[0+*f_pos],count)){
retval=-EFAULT;
goto out;
}
*f_pos += count;
retval=count;
return retval;
out:
return 0;
}
应用程序调用llseek函数,通过系统调用实现内核驱动对应的llseek函数的调用。其原型如下:
off_t lseek(int fd, off_t offset, int whence);