欢迎光临
我们一直在努力

【Linux 篇】数字世界的通用法则 —— 一切皆文件底层原理深度解析

于此止?:个人主页

本文选自专栏: 《Linux篇》

❄️个人专栏传送门: 《QT百日筑基篇》 《算法篇》 

《C++与STL剖析篇》 《数据结构篇》 《Python基础篇》《嵌入式砺剑录》

⭐️纵有狂风拔地起,我亦乘风破万里⭐️

Linux下一切皆文件


前言:本文是一篇短文,主要是介绍我们对应的怎么去理解Linux一切皆文件。

我们在之前学Linux中听说过一切皆文件,但是我们还是直局限于我们的概念,当时我们只是记住即可并不理解它们的意思,下面我们就来详细的理解一切接文件把。

理解Linux一切皆文件

首先在我们经常使用的Windows下我们是文件的在我们的Linux下还是软件,但是有些在windows不是文件的东西比如(磁盘、网卡、键盘……)他在Linux下也抽象成立软件,我们可以通过访问文件的信息来获取他们的信息。

这样做的好处是我们设计一台API和开发工具就能控制我们这些文件,即可调取Linux下的大部分资源。在Linux中几乎很多的事情都需要读(读文件…)的操作我们都是使用read来进行读取。

我们之前在讲解进程的时候看过下面的OS操作系统的图片。我们在操作对应的硬件的时候也是将我们的硬件看作软件, 那么该怎么进行查看呢?我们下面来进行解说。

首先我们底层会有一个个硬件,我们在硬件之上会通过一些驱动进行控制。我们这里将我们对应的驱动就是对于底层进行IO。将对应的IO进行对我们硬件进行输出。

  • 应用层无法直接访问硬件与内核驱动,我们需要编写用户态程序;程序启动运行时,Linux 会创建对应的进程,所有 IO 交互都依托进程上下文完成。
  • 内核通过 VFS 虚拟文件系统做统一封装,在硬件和应用层之间搭建软件抽象层,把键盘、磁盘、显示器、网卡等硬件都封装成设备文件。
  • 应用层只需调用 open、read、write、close 标准文件系统调用,就能无差别操控各类硬件,无需区分底层硬件差异,这就是 “一切皆文件” 的设计意义。
  • 我们这里的文件是使用了虚拟文件系统(简称VFS)进行的, 我们在这个虚拟文件系统之上称为我们的一切接文件,是我们使用了对应的软件层来对我们的硬件进行一些的管理操作。
  • 我们的下面采用函数指针的方法, 因为结构体中不能使用我们对应的定义函数,我们能使用函数指针的方法,这就相当于我们实现了对应的C语言的多态。
  • 后续我们对与底层的硬件的访问就是我们对于结构体的访问,我们要对硬件进行访问时,我们可以进行我们read和write的编写,通过OS传给我们对应的驱动控制当中进而控制我们对应的硬件。
  • 我们在控制这些我们不确定的请况下我们都是使用镀一层软件层的方法进行对于硬件的操作。所以Linux下一切皆文件。
  • 其中我们还有对应的inode信息(储存我们的属性),和缓冲区(储存我们的内容)后续将刷新到我们对应的驱动中。
  • 我们可以查看一下我们对应的内核源代码中的struct_file

    我们有对应的inode信息, 也就是我们文件的属性,还有我们对应的文件方法(struct file_operation)。还有我们对应的引用计数和权限。

    struct file {

    struct inode *f_inode; /* cached value */
    const struct file_operations *f_op;

    atomic_long_t f_count; // 表示打开文件的引用计数,如果有多个文件指针指向它,就会增加f_count的值。
    unsigned int f_flags; // 表示打开文件的权限
    fmode_t f_mode; // 设置对文件的访问模式,例如:只读,只写等。所有的标志在头文件<fcntl.h> 中定义
    loff_t f_pos; // 表示当前读写文件的位置

    } __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */

    现在我们看一下我们的struct operation的内核代码

    file_operation存在我们的一些程序的方法,我们我面可能通过这些方法将我们的代码进行刷新到 我们的驱动程序中。

    struct file_operations {
    struct module *owner;
    //指向拥有该模块的指针;

    loff_t (*llseek) (struct file *, loff_t, int);
    //llseek 方法用作改变文件中的当前读/写位置,并且新位置作为(正的)返回值.

    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    //用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型,常常是目标平台本地的整数类型).

    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    //发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数.

    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);
    //对于设备文件这个成员应当为 NULL; 它用来读取目录,并且仅对**文件系统**有用.

    unsigned int (*poll) (struct file *, struct poll_table_struct *);

    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

    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 *);
    //mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系统调用返回 -ENODEV.

    int (*open) (struct inode *, struct file *);
    //打开一个文件

    int (*flush) (struct file *, fl_owner_t id);
    //flush 操作在进程关闭它的设备文件描述符的拷贝时调用;

    int (*release) (struct inode *, struct file *);
    //在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL.

    int (*fsync) (struct file *, struct dentry *, int datasync);
    //用户调用来刷新任何挂着的数据.

    int (*aio_fsync) (struct kiocb *, int datasync);

    int (*fasync) (int, struct file *, int);

    int (*lock) (struct file *, int, struct file_lock *);
    //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 **);
    };

    上图中的外设,每个设备都可以有自己的 read、write,但一定是对应着不同的操作方法!!但通过 struct file 下 file_operation 中的各种函数回调,让我们开发者只用 file 便可调取 Linux 系统中绝大部分的资源!!这便是 “linux 下一切皆文件” 的核心理解。

    赞(0)
    未经允许不得转载:171主机测评 » 【Linux 篇】数字世界的通用法则 —— 一切皆文件底层原理深度解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址