Linux“一切皆文件“设计哲学 与 Linux文件抽象层:struct file与file_operations的架构解析

在Linux系统中,"一切皆文件"(Everything is a file)是一个核心设计哲学,它抽象了系统资源的访问方式,使得几乎所有硬件设备、进程、网络连接等都可以通过统一的文件接口(如open()read()write()close()等系统调用)进行操作。

目录

一、在Linux系统中,"文件"的概念比Windows更为广泛

二、Linux中的文件

[1、普通文件(Regular Files)](#1、普通文件(Regular Files))

2、目录(Directories)

[3、设备文件(Device Files)](#3、设备文件(Device Files))

[4、命名管道(Named Pipes, FIFO)](#4、命名管道(Named Pipes, FIFO))

5、套接字(Sockets)

[6、符号链接(Symbolic Links)](#6、符号链接(Symbolic Links))

[7、伪文件系统(ProcFS, SysFS, etc.)](#7、伪文件系统(ProcFS, SysFS, etc.))

[8、标准输入/输出/错误(stdin, stdout, stderr)](#8、标准输入/输出/错误(stdin, stdout, stderr))

9、例外情况

三、这一设计的主要优势在于

为什么这样设计?

四、补充说明


一、在Linux系统中,"文件"的概念比Windows更为广泛

  1. Windows中的文件在Linux中同样被视为文件
  2. Windows中非文件对象(如进程、磁盘、显示器、键盘等硬件设备)在Linux中也被抽象为文件
  3. 管道同样被视为文件
  4. 后续将学习到的网络编程中的套接字(socket)也采用文件接口

二、Linux中的文件

1、普通文件(Regular Files)

  • 包括文本文件、二进制文件等,存储在磁盘或其他存储设备中。

  • 例如:/home/user/document.txt

2、目录(Directories)

  • 目录本质上是包含其他文件列表的特殊文件。

  • 例如:/etc/ 目录中保存了系统配置文件列表。

3、设备文件(Device Files)

Linux将硬件设备抽象为文件,分为两类:

  • 块设备(Block Devices):以固定大小的数据块访问(如磁盘)。

    • 例如:/dev/sda(第一块硬盘)。
  • 字符设备(Character Devices):以字符流形式访问(如键盘、鼠标)。

    • 例如:/dev/tty(终端设备)。

示例操作:

bash 复制代码
# 向磁盘设备写入数据(需谨慎!)
dd if=file.img of=/dev/sdb

# 从鼠标设备读取输入(需权限)
cat /dev/input/mouse0

4、命名管道(Named Pipes, FIFO)

  • 用于进程间通信(IPC)的特殊文件,数据先进先出。

  • 示例:

    bash 复制代码
    mkfifo /tmp/my_pipe
    echo "Hello" > /tmp/my_pipe &  # 写入端
    cat < /tmp/my_pipe            # 读取端

5、套接字(Sockets)

  • 用于网络或本地进程间通信的文件。

  • 例如:/var/run/docker.sock 是Docker守护进程的通信套接字。

6、符号链接(Symbolic Links)

  • 指向其他文件的快捷方式。

  • 例如:/bin/sh 可能是指向 /bin/bash 的符号链接。

7、伪文件系统(ProcFS, SysFS, etc.)

  • /proc :动态反映进程和内核状态的文件(如/proc/cpuinfo/proc/1234/为PID 1234的进程信息)。

  • /sys:暴露内核设备和驱动的配置(如调节CPU频率)。

示例:

bash 复制代码
# 查看CPU信息
cat /proc/cpuinfo

# 修改系统参数(如最大进程数)
echo 10000 > /proc/sys/kernel/pid_max

8、标准输入/输出/错误(stdin, stdout, stderr)

  • 在Linux中,这些标准流也通过文件描述符访问:

    • 0:stdin(如键盘输入)。

    • 1:stdout(如终端输出)。

    • 2:stderr(如错误输出)。

重定向示例:

bash 复制代码
ls /nonexistent 2> /dev/null  # 将错误输出重定向到"黑洞"设备

9、例外情况

并非所有资源都是文件,例如:

  • 线程调度、内存分配等底层操作仍需通过系统调用(如mmap())。

  • 某些现代内核特性(如cgroups)可能不完全遵循此规则。


三**、这一设计的主要优势在于**

  • 开发者仅需掌握一套API和开发工具即可调用系统大部分资源
  • 几乎所有读取操作(读取文件、系统状态、管道等)都可通过read函数实现
  • 几乎所有写入操作(修改文件、系统参数、管道等)都可通过write函数完成

为什么这样设计?

  • 统一性:所有资源通过文件接口操作,简化编程模型。

  • 抽象性:用户无需关心底层细节(如硬件差异)。

  • 灵活性 :文件权限(如chmod)、重定向(如>)等机制可通用。


四、补充说明

当打开文件时,系统会创建对应的file结构体进行管理。该结构体定义于: /usr/src/kernels/3.10.0-1160.71.1.el7.x86_64/include/linux/fs.h 以下展示该结构体的相关部分内容:

c 复制代码
struct file {
    ...
    struct inode *f_inode;     /* Cached inode pointer */
    const struct file_operations *f_op;
    ...
    atomic_long_t f_count;     /* Reference count for open files */
    unsigned int f_flags;      /* File access flags (read/write permissions) */
    fmode_t f_mode;            /* File access mode (defined in headers) */
    loff_t f_pos;              /* Current read/write position */
    ...
} __attribute__((aligned(4))); /* Force 4-byte alignment */

值得注意的是,struct file 中的 f_op 指针指向一个 file_operations 结构体,该结构体除 struct module* owner 成员外,其余均为函数指针。这两个结构体均定义于 fs.h 头文件中:

c 复制代码
struct file_operations {
    struct module *owner;           // 指向所属模块的指针
    loff_t (*llseek)(struct file *, loff_t, int);  // 修改文件当前读写位置,返回新位置
    ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);  // 从设备读取数据,NULL返回-EINVAL
    ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);  // 向设备写入数据,NULL返回-EINVAL
    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);  // 无锁版ioctl
    long (*compat_ioctl)(struct file *, unsigned int, unsigned long);  // 兼容版ioctl
    int (*mmap)(struct file *, struct vm_area_struct *);  // 将设备内存映射到进程地址空间,NULL返回-ENODEV
    int (*open)(struct inode *, struct file *);  // 打开文件
    int (*flush)(struct file *, fl_owner_t id);  // 进程关闭文件描述符时调用
    int (*release)(struct inode *, struct file *);  // 文件结构释放时调用
    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 *);  // 文件锁定(设备驱动很少实现)
    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 **);  // 设置租约
};

file_operation 是连接系统调用与驱动程序的核心数据结构,其每个成员都对应着一个特定的系统调用。当系统调用执行时,会读取 file_operation 中对应的函数指针,并将控制权转交给该函数,从而完成 Linux 设备驱动程序的调用流程。

为帮助理解,我们用一张图来总结上述内容:

图中的外设设备虽然各自拥有独立的读写操作方式,但通过 struct file 结构中 file_operation 的函数回调机制,开发者仅需使用 file 接口就能访问 Linux 系统中的绝大多数资源。这正是"Linux下一切皆文件"理念的核心体现。

相关推荐
Edingbrugh.南空4 分钟前
如何优雅调整Doris key顺序
大数据·运维·数据库
网易独家音乐人Mike Zhou28 分钟前
【Linux应用】在PC的Linux环境下通过chroot运行ARM虚拟机镜像img文件(需要依赖qemu-aarch64、不需要重新安装iso)
linux·c语言·stm32·mcu·物联网·嵌入式·iot
bailang_zhizun30 分钟前
【Docker】在银河麒麟ARM环境下离线安装docker
运维·docker·容器
LZD_jay1 小时前
在服务器(ECS)部署 MySQL 操作流程
服务器·数据库·mysql
Ronin3051 小时前
【Linux系统】进程控制
linux·运维·服务器·ubuntu
渡我白衣1 小时前
Linux操作系统之线程(三)
linux
-曾牛2 小时前
Linux搭建LAMP环境(CentOS 7 与 Ubuntu 双系统教程)
linux·运维·ubuntu·网络安全·渗透测试·centos·云计算运维
小嵌同学2 小时前
Linux 内存管理(2):了解内存回收机制
linux·运维·arm开发·驱动开发·嵌入式
珹洺2 小时前
计算机网络:(十一)多协议标记交换 MPLS
运维·计算机网络
绵绵细雨中的乡音2 小时前
消息队列与信号量:System V 进程间通信的基础
linux