22 虚拟文件的实现

在驱动里用数据缓冲区代替文本文件的数据存取.
实现代码test.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#define MYMA  1234
#define MYMI  7788
#define COUNT 1

dev_t devid;
struct cdev mycdev;
u8 *data; //驱动数据缓冲区
int dlen = 26; //驱动数据缓冲区长度

//当用户进程读时,应把驱动里的缓冲区数据复制给用户进程
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    int len_copy, ret;

// fl->f_pos表示当前文件描述符对文件的偏移, len表示用户进程想要读的大小

    if ((fl->f_pos + len) > strlen(data)) //如果剩下没读的数据长度少于len,则只复制出剩下没读部分
            len_copy = strlen(data) - fl->f_pos;
    else
            len_copy = len; //如果剩下的数据长度超出len,则本次复制len字节

    ret = copy_to_user(buf, data+fl->f_pos, len_copy);

    //内容复制后,需要改变文件描述符的位置偏移
    *off += len_copy - ret;  //在read/write函数里必须通过off来改变
    return len_copy - ret;
}

//当用户进程write时,驱动里应把用户进程的数据存放起来
ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{
    int len_copy, ret;

    if ((fl->f_pos + len) > dlen) //如果要复制的内容超出数据缓冲区的大小      
        len_copy = dlen - fl->f_pos;  //只复制剩下空间大小的内容
    else
        len_copy = len;

    ret = copy_from_user(data+ fl->f_pos, buf, len_copy);

    *off += len_copy - ret;

    return len_copy - ret;
}

//当用户进程lseek(fd, 54, SEEK_SET)时触发此函数
loff_t myllseek(struct file *fl, loff_t offset, int whence)
{
    //除了read/write函数,可以通过fl->f_pos来改变文件描述符的偏移
    u8 *tmp;
    int len;

    switch (whence)
    {
    case SEEK_SET:
        if (offset < dlen)
            fl->f_pos = offset;
        break;

    case SEEK_CUR:
        if ((fl->f_pos + offset) < dlen)
            fl->f_pos += offset;
        break;

    case SEEK_END:
        if (0 == offset)
            fl->f_pos = dlen - 1; //数据缓冲区的最后位置 
        else if (offset > 0)
        {
            //表示需要把缓冲区变为(原大小+offset)
            len = dlen;
            dlen += offset;

            tmp = kzalloc(dlen, GFP_KERNEL); //分配出新大小的缓冲区
            memcpy(tmp, data, len); //把原缓冲区上的数据复制到新缓冲区
            kfree(data); //回收原空间    
            data = tmp; //取代原缓冲区

            fl->f_pos = dlen - 1;
        }
        break;
    }
    return fl->f_pos;
}


struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
    .llseek = myllseek, 
};

static int __init test_init(void)
{
    int ret, i;

    devid = MKDEV(MYMA, MYMI);
    ret = register_chrdev_region(devid, COUNT, "mydev");
    if (ret < 0)
        goto err0;

    cdev_init(&mycdev, &fops);
    mycdev.owner = THIS_MODULE;
    ret = cdev_add(&mycdev, devid, COUNT);
    if (ret < 0)
        goto err1;

    data = kzalloc(dlen, GFP_KERNEL); //申请数据缓冲区, 此缓冲区用于与用户进程数据交互.
    if (NULL == data)
        goto err2;

    for (i = 0; i < 26; i++)
        data[i] = 'A' + i;

    return 0;
err2:
    cdev_del(&mycdev);
err1:
    unregister_chrdev_region(devid, COUNT);
err0:
    return ret;
}

static void __exit test_exit(void)
{
    unregister_chrdev_region(devid, COUNT);
    cdev_del(&mycdev);
    kfree(data);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

app.c:


#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>


int main(void)
{
    int fd = open("/dev/mydev", O_RDWR);
    int ret;
    char ch;

    if (fd < 0)
        return 1;

    lseek(fd, 2, SEEK_SET);
    while (1)   
    {
        ret = read(fd, &ch, 1);
        if (ret <= 0)
            break;
        printf("%c\n", ch);
    }

    lseek(fd, 100, SEEK_END);
    lseek(fd, 26, SEEK_SET);

    write(fd, "what what what", 15);
    lseek(fd, 26, SEEK_SET);
    printf("################################\n");
    while (1)   
    {
        ret = read(fd, &ch, 1);
        if (ret <= 0)
        {
            perror("read");
            break;
        }
        printf("%c\n", ch);
    }


    close(fd);
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
一个驱动支持多个虚拟文件的实现
test.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#define MYMA  1234
#define MYMI  7788
#define COUNT 3 //三个设备号,每个设备号对应一个设备文件和一个数据缓冲区

dev_t devid;
struct cdev mycdev;
u8 *data[COUNT]; //驱动数据缓冲区
int dlen = 1024; //驱动数据缓冲区长度

ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    int mi = MINOR(fl->f_path.dentry->d_inode->i_rdev);
    int len_copy, ret, n;

    n = mi - MYMI;
    if (n >= COUNT)
        return -ENODEV;

    if ((fl->f_pos + len) > strlen(data[n])) 
            len_copy = strlen(data[n]) - fl->f_pos;
    else
            len_copy = len; 

    ret = copy_to_user(buf, data[n]+fl->f_pos, len_copy);

    *off += len_copy - ret; 
    return len_copy - ret;
}

ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{
    int mi = MINOR(fl->f_path.dentry->d_inode->i_rdev);
    int len_copy, ret, n;

    n = mi - MYMI;
    if (n >= COUNT)
        return -ENODEV;
    if ((fl->f_pos + len) > dlen) 
        len_copy = dlen - fl->f_pos; 
    else
        len_copy = len;

    ret = copy_from_user(data[n] + fl->f_pos, buf, len_copy);

    *off += len_copy - ret;

    return len_copy - ret;
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
};

static int __init test_init(void)
{
    int ret, i, j;

    devid = MKDEV(MYMA, MYMI);
    ret = register_chrdev_region(devid, COUNT, "mydev");
    if (ret < 0)
        goto err0;

    cdev_init(&mycdev, &fops);
    mycdev.owner = THIS_MODULE;
    ret = cdev_add(&mycdev, devid, COUNT);
    if (ret < 0)
        goto err1;

    for (i = 0; i < COUNT; i++)
    {
        data[i] = kzalloc(dlen, GFP_KERNEL); 
        for (j = 0; j < 26; j++)
            data[i][j] = 'A' + j;
    }

    return 0;
err1:
    unregister_chrdev_region(devid, COUNT);
err0:
    return ret;
}

static void __exit test_exit(void)
{
    int i;

    unregister_chrdev_region(devid, COUNT);
    cdev_del(&mycdev);

    for (i = 0; i < COUNT; i++)
        kfree(data[i]);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
目录 1 虚拟文件系统概述 5 1.1 通用文件模型 7 1.2 VFS所处理的系统调用 9 2 虚拟文件系统架构 11 2.1 VFS对象数据结构 11 2.1.1 超级块对象 11 2.1.2 索引节点对象 15 2.1.3 文件对象 18 2.1.4 目录项对象 22 2.2 把Linux中的VFS对象串联起来 24 2.2.1 与进程相关的文件 25 2.2.2 索引节点高速缓存 29 2.2.3 目录项高速缓存 30 2.2.4 VFS对象的具体实现 32 2.3 文件系统的注册与安装 38 2.3.1 文件系统类型注册 38 2.3.2 文件系统安装数据结构 41 2.3.3 安装普通文件系统 52 2.3.4 分配超级块对象 58 2.3.5 安装根文件系统 60 2.3.6 卸载文件系统 65 2.4 路径名的查找 66 2.4.1 查找路径名的一般流程 67 2.4.2 父路径名查找 82 2.4.3 符号链接的查找 84 2.5 VFS系统调用的实现 88 2.5.1 open()系统调用 88 2.5.2 read()和write()系统调用 96 2.5.3 close()系统调用 97 3 第二扩展文件系统 99 3.1 Ext2磁盘数据结构 101 3.1.1 磁盘超级块 102 3.1.2 组描述符和位图 105 3.1.3 磁盘索引节点表 105 3.2 VFS接口数据结构 110 3.2.1 Ext2 超级块对象 110 3.2.2 Ext2 的索引节点对象 121 3.2.3 创建Ext2文件系统 124 3.2.4 Ext2的方法总结 126 3.3 Ext2索引节点分配 129 3.3.1 创建索引节点 130 3.3.2 删除索引节点 143 3.4 Ext2数据块分配 144 3.4.1 数据块寻址 145 3.4.2 文件的洞 147 3.4.3 分配数据块 148 4 页面高速缓存 160 4.1 页高速缓存数据结构 160 4.1.1 address_space对象 161 4.1.2 基树 164 4.2 高速缓存底层处理函数 166 4.2.1 查找页 166 4.2.2 增加页 168 4.2.3 删除页 173 4.3 文件系统与高速缓存 175 4.3.1 缓冲头数据结构 175 4.3.2 分配块设备缓冲区页 178 4.3.3 释放块设备缓冲区页 184 4.4 在页高速缓存中搜索块 185 4.4.1 __find_get_block()函数 185 4.4.2 __getblk()函数 188 4.4.3 __bread()函数 190 4.5 把脏页写入磁盘 191 4.5.1 pdflush内核线程 192 4.5.2 搜索要刷新的脏页 193 4.5.3 回写陈旧的脏页 196 5 文件读写 199 5.1 系统调用VFS层的处理 200 5.2 第二扩展文件系统Ext2层的处理 201 5.2.1 Ext2的磁盘布局 202 5.2.2 Ext2的超级块对象 206 5.2.3 Ext2索引节点对象的创建 210 5.2.4 Ext2索引节点对象的读取 218 5.2.5 Ext2层读文件入口函数 225 5.3 页高速缓存层的处理 237 5.3.1 创建一个bio请求 238 5.3.2 得到文件的逻辑块号 244 5.3.3 普通文件的readpage方法 251 5.3.4 块设备文件的readpage方法 252 5.3.5 文件的预读 260 5.4 通用块层的处理 264 5.4.1 块设备的基础知识 265 5.4.2 通用块层相关数据结构 269 5.4.3 提交I/O传输请求 271 5.4.4 请求队列描述符 273 5.5 块设备I/O调度层的处理 281 5.5.1 块设备的初始化 284 5.5.2 建立块设备驱动环境 288 5.5.3 关联block_device结构 295 5.5.4 为设备建立请求队列 306 5.5.5 块设备I/O调度程序 311 5.5.6 真实的I/O调度层处理 321 5.6 块设备驱动层的处理 330 5.6.1 scsi总线驱动的初始化 330 5.6.2 scsi设备驱动体系架构 342 5.6.3 scsi块设备驱动层处理 347 5.6.4 scsi命令的执行 369 5.6.5 scsi命令的第一次转变 372 5.6.6 scsi命令的第二次转变 380 5.7 写文件 384 5.7.1 generic file_write函数 384 5.7.2 普通文件的prepare_write方法 386 5.7.3 块设备文件的prepare_write方法 387 5.7.4 将脏页写到磁盘 388 6 直接I/O与异步I/O 391 6.1 直接I/O 391 6.2 异步I/O 393 6.2.1 Linux 2.6中的异步I/O 394 6.2.2 异步I/O环境 394 6.2.3 提交异步I/O操作 395
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值