关于EOF和读文件的一些事

到底什么是EOF?这是一个问题。一般而言,EOF字面含义就是文件的结尾-end of file,可是如何去检查是否已经到了结尾呢?文件类型各种各样,不可能在文件的结尾处真实的写一个EOF标志,如果真的这么做,操作系统内核的文件系统设计将会非常复杂,于是这个“检测”文件结尾的工作就交给了用户态,通常来讲就是C库来做这件事。内核仅仅按照顺序的方式向前推进对文件的访问即可,因此对于内核态文件数据结构而言,维护一个pos是重要的,正是这个pos不断向前推进,最终到达文件的尾部,每次读取文件是从pos处开始读取,到了最后pos已经到达了文件尾部,那么再读的话就会返回“读到的长度为0”这个结果,而正是这个结果标示了EOF。因此,用户态的程序在读文件时大体都遵循以下逻辑:

open
while true
    read=>length
    if length=0
        EOF
        break
done

也就是说通过检测read到0字节长度的数据来定义EOF。背后的逻辑就是文件的pos在不断向前推进,一直到底,而这是内核维护的,最终内核默默地维护pos,给与用户态的表象就是read到0字节的数据。

        如果内核不更新文件的pos,那么理论上这个文件将会一直可以读取,没完没了地被读取,这种机制可以实现一些“其它的文件”。之所以要有其它的文件,是因为Unix文件囊括的东西真是太多了,范围太广大了。有些文件真的不适合有一个结尾,也就是不适合维护一个pos,比如
/dev/urandom(这个文件的内核file_operations接口根本不维护pos)这种产生随机数设备文件以及那些根本没有“文件逻辑”然而为了接口方便而必须使用文件接口的设备或者别的。

        在Linux中,procfs是一种拥有文件接口的内核信息导出逻辑,该文件系统提供了几乎所有的“文件”访问方式,这里的文件是广义意义上的,也就是说包括维护pos的文件和不维护pos的文件,对于一般的文件,procfs文件系统访问框架自动维护了pos,因此你使用creare-read/write-entry的接口创建的文件,即使在你提供的回调函数中再希望实现“不间断读取”这种不维护pos的逻辑,也是不可能的,因为这种接口的框架层已经帮你维护了一个pos,最终一定会达到文件尾部的。然而有一种方式可以做到不维护pos状态或者说将pos的语义进行了转义或者说误导。以下就是一个代码实例,如果你用cat来读取,将会不间断的输出:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/proc_fs.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/seq_file.h>

//定义一个变量,每次自增1,你可以将它定义成你的随机数
int i = 0;

struct proc_dirandom_entry *test;

static void *random_start(struct seq_file *m, loff_t *pos)
{
    return &i;
}
static void *random_next(struct seq_file *m, void *v, loff_t *pos)
{
    //这里产生递增的i
    i++;
    return &i;
}

static void random_stop(struct seq_file *m, void *v)
{
    //例行公事
    return;
}

static int random_show(struct seq_file *m, void *v)
{
    //这里是显示逻辑
    int *a = (int*)v;
    seq_printf(m, "%d\n", *a);
    return 0;
}

static const struct seq_operations random_ops = {
    .start = random_start,
    .next = random_next,
    .stop = random_stop,
    .show = random_show,
};

static int ran_open(struct inode *inode, struct file *file)
{
    int ret = seq_open(file, &random_ops);
    return ret;
}



static const struct file_operations random_file = {
    .open = ran_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release,
    .owner = THIS_MODULE,
};



static int __init
ran_test_init(void)
{
    int ret = 0;
    //初始化,名字随便起的
    test = proc_mkdir("simple", NULL);
    proc_create_data("abcd", 0, test,
             &random_file, NULL);
    return ret;
}

static void __exit
ran_test_exit(void)
{
    remove_proc_entry("driver", test);
    remove_proc_entry("simple", NULL);
}
module_init(ran_test_init);
module_exit(ran_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhaoya <marywangran@126.com>");
MODULE_DESCRIPTION("Just a testing");

这仅仅是一个使用seqfs实现永久输出的例子,如果你非要使用“一般的文件”来实现之,你就必须在每次读取之后自行lseek,虽然内核自动推进了pos,然而你可以在用户态将pos再退回来:

open
while true
    read
    lseek to 0
done

关键之处就是读到的东西是什么,如是随机数,那么每次的都不一样,赚到了。

原文链接: https://blog.csdn.net/dog250/article/details/7772909

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    关于EOF和读文件的一些事

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/409062

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 上午11:11
下一篇 2023年4月26日 上午11:11

相关推荐