高级I/O操作-定位操作

x33g5p2x  于2022-05-16 转载在 其他  
字(3.3k)|赞(0)|评价(0)|浏览(374)

定位操作

驱动代码:

/* vfb.c */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
 
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
 
#define VFB_MAJOR	256
#define VFB_MINOR	1
#define VFB_DEV_CNT	1
#define VFB_DEV_NAME	"vfbdev"
 
struct vfb_dev {
	unsigned char *buf;
	struct cdev cdev;
};
 
static struct vfb_dev vfbdev;
 
static int vfb_open(struct inode * inode, struct file * filp)
{
	return 0;
}
 
static int vfb_release(struct inode *inode, struct file *filp)
{
	return 0;
}
 
static int vfb_mmap(struct file *filp, struct vm_area_struct *vma)
{
	if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(vfbdev.buf) >> PAGE_SHIFT, \
		vma->vm_end - vma->vm_start, vma->vm_page_prot))
		return -EAGAIN;
	return 0;
}
 
ssize_t vfb_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
	int ret;
	size_t len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    //判断文件访问是否超过了边界
    //超过了调整访问的长度
	if (*pos + len > PAGE_SIZE)
		len = PAGE_SIZE - *pos;
 
	ret = copy_to_user(buf, vfbdev.buf + *pos, len);
	*pos += len - ret;
 
	return len - ret;
}
//文件定位操作
static loff_t vfb_llseek(struct file * filp, loff_t off, int whence)
{
	loff_t newpos;
 
	switch (whence) {
	case SEEK_SET:
		newpos = off;
		break;
	case SEEK_CUR:
		newpos = filp->f_pos + off;
		break;
	case SEEK_END:
		newpos = PAGE_SIZE + off;
		break;
	default:                /* can't happen */
		return -EINVAL;
	}
    //判断新的位置值是否合法
	if (newpos < 0 || newpos > PAGE_SIZE)
		return -EINVAL;
    //将新的文件位置值更新到file结构的f_pos成员中
	filp->f_pos = newpos;
 
	return newpos;
}
 
static struct file_operations vfb_fops = {
	.owner = THIS_MODULE,
	.open = vfb_open,
	.release = vfb_release,
	.mmap = vfb_mmap,
	.read = vfb_read,
	.llseek = vfb_llseek,
};
 
static int __init vfb_init(void)
{
	int ret;
	dev_t dev;
	unsigned long addr;
 
	dev = MKDEV(VFB_MAJOR, VFB_MINOR);
	ret = register_chrdev_region(dev, VFB_DEV_CNT, VFB_DEV_NAME);
	if (ret)
		goto reg_err;
 
	cdev_init(&vfbdev.cdev, &vfb_fops);
	vfbdev.cdev.owner = THIS_MODULE;
	ret = cdev_add(&vfbdev.cdev, dev, VFB_DEV_CNT);
	if (ret)
		goto add_err;
 
	addr = __get_free_page(GFP_KERNEL);
	if (!addr)
		goto get_err;
 
	vfbdev.buf = (unsigned char *)addr;
	memset(vfbdev.buf, 0, PAGE_SIZE);
 
	return 0;
 
get_err:
	cdev_del(&vfbdev.cdev);
add_err:
	unregister_chrdev_region(dev, VFB_DEV_CNT);
reg_err:
	return ret;
}
 
static void __exit vfb_exit(void)
{
	dev_t dev;
 
	dev = MKDEV(VFB_MAJOR, VFB_MINOR);
 
	free_page((unsigned long)vfbdev.buf);
	cdev_del(&vfbdev.cdev);
	unregister_chrdev_region(dev, VFB_DEV_CNT);
}
 
module_init(vfb_init);
module_exit(vfb_exit);
 
MODULE_LICENSE("GPL");

驱动代码第45行和第46行判断文件访问是否超过了边界,如果是则调整访问的长度。

代码第48行在复制时考虑到了偏移所带来的影响

代码第49行则是更新位置值。

代码第54行至第78行是文件定位操作的实现,根据whence的不同,设置了新的文件位置值

代码第72行和第73行则是判断新的位置值是否合法

代码第75行将新的文件位置值更新到file结构的f_pos成员中

测试程序代码:

/* test.c */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
 
int main(int argc, char * argv[])
{
	int fd;
	char *start;
	int i;
	char buf[32];
 
	fd = open("/dev/vfb0", O_RDWR);
	if (fd == -1)
		goto fail;
 
	start = mmap(NULL, 32, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (start == MAP_FAILED)
		goto fail;
	
	for (i = 0; i < 26; i++)
		*(start + i) = 'a' + i;
	*(start + i) = '\0';
 
	if(lseek(fd, 3, SEEK_SET) == -1)
		goto fail;
 
	if (read(fd, buf, 10) == -1)
		goto fail;
 
	buf[10] = '\0';
	puts(buf);
 
	munmap(start, 32);
	return 0;
 
fail:
	perror("mmap test");
	exit(EXIT_FAILURE);
}

测试程序相对于上一篇文章的变化是在读操作时候前面首先使用lseek将文件位置定位为3,那么之后的操作都从文件的第3个字节开始读取。

if(lseek(fd, 3, SEEK_SET) == -1)
        goto fail;
 
    if (read(fd, buf, 10) == -1)
        goto fail;

编译结果如下图:

abcd 对应文件位置 (数组) 0 1 2 3

因为是从第3字节开始读取 即d

读10个

read(fd, buf, 10)、

从buf缓冲区中读取10字节

defghijklm 10个

至此I/O操作结束

相关文章