一.llseek函数使用
(1) llseek函数原型与解释
原型: loff_t (*llseek) (struct file* filp,loff_t offset, int whence);
参数 | 功能 |
struct file* | 代表上一级调用函数传入的已打开文件,该结构体与struct inode* 区别在于struct inode*用于代表一个文件,但是该文件可以是未打开的。 |
loff_t | 代表用户空间的文件光标移动数量值,可以是正数,也可以是负数。 |
int whence | 代表移动光标的参考位置,有3种(即当前光标、文件开头、文件结尾) |
返回值:非负数,当前文件的指针位置;负数,函数调用失败。
(2)代码讲解
1.驱动代码
llsee函数在驱动和应用程序都存在(即用户空间和内核空间都存在),应用程序调用llseek函数就会在内核驱动中被调用,实现的功能是修改驱动中write/read函数的文件指针。(直白的说就是:当文件指针offset发生变化,write/read函数的文件指针也跟着变化)
#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;
}
2.应用层代码
lseek(int fd, off_t offset, int whence);
fd:文件句柄,要执行的文件;
offset:文件的偏移量;
whence:文件开始的位置.
二.总结
在 Linux
中,所有设备都以文件的形式存放在
/dev 目录下,都是通过文件的方式进行访问,设备节点是 Linux 内核对设备的抽象,一个设备节点就是一个文件。应用程序主要通过file_operations 结构体的函数的调用执行访问设备,这些调用独立于任何特定的驱动程序。而驱动程序负责将这些标准调用映射到实际硬件的特有操作。
(1)驱动关系调用图解
(2)file_operations 结构体
file_operations 结构体是访问驱动的函数,它的里面的每个结构体成员都对应一个调用,这个结构体里 面有很多的成员变量,并且结构体中的成员函数是字符设备驱动程序设计的主体内容,这些函数实际会在 应用程序进行 Linux
的
open
()、
write
()、
read
()、
close
()等系统调用时最终被内核调用。
file_operations 文件操作集在定义在 include/linux/fs.h
下面。
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);
};
(3)file_operations 结构体的函数描述
对 file_operations 结构体中的主要成员进行分析表:
函数
|
功能
|
llseek
()函数
|
修改一个文件的当前读写位置,并将新位置返回,在出错时,这个函数返回一个负值。
|
read
()函数
|
用来从设备中读取数据,成功时函数返回读取的字节数,出错时返回一个负值。它与
用户空间应用程序中的
ssize_t read
(
int fd
,
void*buf
,
size_t count
)和
size_t fread
(
void*ptr
,
size_t size
,
size_t nmemb
,
FILE*stream
)对应。
|
write
()函数
|
向设备发送数据,成功时该函数返回写入的字节数。如果此函数未被实现,当用户进
行
write
()系统调用时,将得到
-EINVAL
返回值。它与用户空间应用程序中的
ssize_t
write
(
int fd
,
const void*buf
,
size_t count
)和
size_t fwrite
(
const void*ptr
,
size_t size
,
size_t nmemb
,
FILE*stream
)对应。
|
read
()和
write ()
|
如果返回
0
,则暗示
end-of-file
(
EOF
)。
|
unlocked_ioctl
()
|
提供设备相关控制命令的实现(既不是读操作,也不是写操作),当调用成功时,返
回给调用程序一个非负值。它与用户空间应用程序调用的
int fcntl
(
int fd
,
int
cmd
,
.../*arg*/
)和
int ioctl
(
int d
,
int request
,
...
)对应。
|
mmap
()函数
|
将设备内存映射到进程的虚拟地址空间中,如果设备驱动未实现此函数,用户进行
mmap
()系统调用时将获得
-ENODEV
返回值。这个函数对于帧缓冲等设备特别有意
义,帧缓冲被映射到用户空间后,应用程序可以直接访问它而无须在内核和应用间进
行内存复制。它与用户空间应用程序中的 void*mmap(
void*addr
,
size_t length
,
int prot
,
int flags
,
int fd
,
off_t offset
)函数对 应。
|
poll
()函数
|
一般用于询问设备是否可被非阻塞地立即读写。当询问的条件未触发时,用户空间进
行
select
()和
poll
()系统调用将引起进程的阻塞。
|
aio_read
( ) 和
aio_write
()函数
|
分别对与文件描述符对应的设备进行异步读、写操作。设备实现这
两个函数后,用户空间可以对该设备文件描述符执行
SYS_io_setup
、
SYS_io_submit
、
SYS_io_getevents
、
SYS_io_destroy
等系统调用进行读写。
|
open
()函数
|
当用户空间调用
Linux API
函数
open
()打开设备文件时,设备驱动的
open
()函数
最终被调用。驱动程序可以不实现这个函数,在这种情况下,设备的打开操作永远成
功。与
open
()函数对应的是
release
()函数。
|