当前位置: 首页 > news >正文

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

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

目录

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

二、Linux中的文件

1、普通文件(Regular Files)

2、目录(Directories)

3、设备文件(Device Files)

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

5、套接字(Sockets)

6、符号链接(Symbolic Links)

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

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(终端设备)。

示例操作:

# 向磁盘设备写入数据(需谨慎!)
dd if=file.img of=/dev/sdb# 从鼠标设备读取输入(需权限)
cat /dev/input/mouse0

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

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

  • 示例:

    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频率)。

示例:

# 查看CPU信息
cat /proc/cpuinfo# 修改系统参数(如最大进程数)
echo 10000 > /proc/sys/kernel/pid_max

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

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

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

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

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

重定向示例:

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 以下展示该结构体的相关部分内容:

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 头文件中:

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返回-EINVALssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);  // 向设备写入数据,NULL返回-EINVALssize_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);  // 仅对文件系统有用,设备文件应为NULLunsigned 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);  // 无锁版ioctllong (*compat_ioctl)(struct file *, unsigned int, unsigned long);  // 兼容版ioctlint (*mmap)(struct file *, struct vm_area_struct *);  // 将设备内存映射到进程地址空间,NULL返回-ENODEVint (*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下一切皆文件"理念的核心体现。

http://www.lryc.cn/news/593700.html

相关文章:

  • 使用 validation 框架生成一个校验参数是否在枚举内的校验器
  • 环形区域拉普拉斯方程傅里叶级数解
  • DC-DC降压转换5.5V/3A高效率低静态同步降压转换具有自适应关断功能
  • 基于 Google Earth Engine 的 DEM 鞍部自动提取
  • 动态规划——状压DP经典题目
  • 鸿蒙蓝牙通信
  • 【Java源码阅读系列56】深度解读Java Constructor 类源码
  • GitLab 社区版 10.8.4 安装、汉化与使用教程
  • AI编程工具对比:Cursor、GitHub Copilot与Claude Code
  • 【SVM smote】MAP - Charting Student Math Misunderstandings
  • sqli-labs靶场通关笔记:第32-33关 宽字节注入
  • Kotlin方差
  • 1 渗透基础
  • ros2高级篇之高可用启动文件及配置编写
  • Spring AI 1.0版本 + 千问大模型之文本对话
  • node.js学习笔记1
  • 【数据类型与变量】
  • MySQL——约束类型
  • Springboot项目的搭建方式5种
  • 使用DataGrip连接安装在Linux上的Redis
  • Python+大模型 day02
  • 辛普森悖论
  • 使用看门狗实现复位
  • 1.初始化
  • Web开发 03
  • 双目摄像头品牌
  • 板子 5.29--7.19
  • 【科研绘图系列】R语言绘制显著性标记的热图
  • 【黄山派-SF32LB52】—硬件原理图学习笔记
  • 商业秘密视域下计算机软件的多重保护困境