llseek方法实现了lseek和llseek系统调用,如果设备操作未定义llseek方法,内核默认通过修改filp->f_pos执行定位,filp->f_pos是文件的当前读写位置。为了使lseek系统调用正常工作,read和write方法需要更新它们收到的偏移量参数。
然而大多数设备定位是没有意义的,这些设备只提供数据流,而不是数据区。如果不需要llseek方法,不能简单地不声明llseek,因为默认是允许定位的。应该在open方法中调用nonseekable_open通知内核设备不支持llseek,filp将被标记为不可定位;为了完整,还应该将file_operations中的llseek设置为no_llseek(定义在<linux/fs.h>)。
int nonseekable_open(struct inode *inode, struct file *filp);
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define MAX_BUF_LEN 1024
static int major = 277;
static int minor = 0;
static dev_t helloNum;
struct class *helloClass = NULL;
struct device *helloDev = NULL;
static char helloBuf[MAX_BUF_LEN] = {0};
static loff_t helloBufPos = 0;
static int helloBufSize = 0;
int hello_open(struct inode *pinode, struct file *pfile)
{
printk("hello_open, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
return 0;
}
int hello_release(struct inode *pinode, struct file *pfile)
{
printk("hello_release, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
return 0;
}
ssize_t hello_read(struct file *filep, char __user *buf, size_t size, loff_t *pos)
{
int copySize = size;
if (helloBufSize <= helloBufPos)
{
return 0;
}
if (copySize > helloBufSize - helloBufPos)
{
copySize = helloBufSize - helloBufPos;
}
if(copy_to_user(buf, helloBuf + helloBufPos, copySize))
{
return -EFAULT;
}
*pos += copySize;
helloBufPos = *pos;
return copySize;
}
ssize_t hello_write(struct file *filep, const char __user *buf, size_t size, loff_t *pos)
{
if(copy_from_user(helloBuf + helloBufPos, buf, size))
{
return -EFAULT;
}
helloBufSize = *pos + size;
*pos += size;
helloBufPos = *pos;
printk("write:%s\n", helloBuf);
return size;
}
loff_t hello_llseek(struct file *filep, loff_t off, int whence)
{
loff_t newPos = 0;
switch(whence)
{
case SEEK_SET:
newPos = off;
break;
case SEEK_CUR:
newPos = helloBufPos + off;
break;
case SEEK_END:
newPos = helloBufSize + off;
break;
default:
return -EINVAL;
}
if (newPos < 0)
{
return -EINVAL;
}
helloBufPos = newPos;
return newPos;
}
static struct file_operations hello_ops = {
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
.llseek = hello_llseek,
};
static int hello_init(void)
{
int ret = 0;
printk("hello_init\n");
ret = register_chrdev( major, "hello", &hello_ops);
if(ret < 0)
{
printk("register_chrdev failed.\n");
return ret;
}
helloClass = class_create(THIS_MODULE, "hellocls");
if (IS_ERR(helloClass))
{
printk("class_create failed.\n");
ret = PTR_ERR(helloClass);
goto error_exit1;
}
helloNum = MKDEV(major,minor);
printk("major:%d, minor:%d\n", MAJOR(helloNum), MINOR(helloNum));
helloDev = device_create(helloClass, NULL, helloNum, NULL, "hello0");
if (IS_ERR(helloDev))
{
printk("device_create failed.\n");
ret = PTR_ERR(helloDev);
goto error_exit2;
}
return 0;
error_exit2:
class_destroy(helloClass);
error_exit1:
unregister_chrdev(major,"hello");
return ret;
}
static void hello_exit(void)
{
printk("hello_exit\n");
device_destroy(helloClass, helloNum);
class_destroy(helloClass);
unregister_chrdev(major,"hello");
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char * argv [ ])
{
int fd = open("/dev/hello0", O_RDWR, 666);
if (fd < 0)
{
perror("open faied");
return -1;
}
printf("open successed: %d\n", fd);
write(fd, "hello1", 6);
lseek(fd, 0, SEEK_SET);
char readBuf[32] = {0};
read(fd, readBuf, sizeof(readBuf));
printf("read:%s\n", readBuf);
lseek(fd, 0, SEEK_END);
write(fd, "hello2", 6);
lseek(fd, 0, SEEK_SET);
read(fd, readBuf, sizeof(readBuf));
printf("read:%s\n", readBuf);
close(fd);
return 0;
}