蛮荆

为什么 Linux “一切皆文件”

2023-05-19

概述

在 Linux 中,包括硬件设备、目录、文本文件、二进制文件、输入输出流、套接字、管道、TTY 终端等各种资源都被抽象成文件,并且它们之间没有太大的区别。 每个文件都有一个唯一的路径进行标识,因此可以通过文件系统中的路径来访问和管理这些资源。

一切皆为文件的理念提供了最高级的抽象,开发者只需要使用一套 API 就可以操作 Linux 系统中的绝大部分资源。

一个典型的 Linux 系统的文件结构如下所示:

图片来源: https://tldp.org/LDP/intro-linux/html/sect_03_01.html

常见文件类型

1. 普通文件

普通文件并不单单指文本文件,还包括可执行二进制程序、程序的输入输出等。

2. 目录文件

目录文件包含一组文件的所有者、权限和大小等信息。

3. 设备文件

所有物理设备都是通过设备文件进行访问的,设备文件保存有关特定设备的位置、类型和访问模式等信息,主要分为两种类型: 块设备文件和字符设备文件

块设备文件 用于访问块设备 I/O, 块设备使用的是缓冲 I/O,这意味着数据收集在缓冲区中,直到可以传输整个块为止。

字符设备文件 与字符或原始设备进行无缓冲数据传输,数据传输按照字符逐个进行,单次可以传输多个字符。

此外,某些设备(例如磁盘分区)可以以块设备或字符设备模式访问,每个设备文件对应一种访问模式,因此这种具有多种访问模式的物理设备将具有多个设备文件

设备文件全部位于 dev 目录,执行下面的命令:

$ ls -l /dev

输出结果中会逐行输出单个设备文件,第一个字符 b 表示该设备为块设备。

4. 特殊文件

特殊文件用于输入和输出的机制,本质上也是一种 “设备文件”,典型的特殊文件如:

  • /dev/null : 用于丢弃进程的输出流
  • /dev/full : 对该文件进行写入时始终返回相同的错误代码:ENOSPC (表示设备上没有可用空间)
  • /dev/zero : 用于一些需要初始化变量、清空内存或写入特定位模式的应用程序
  • /dev/random : 利用物理过程(例如键盘敲击、鼠标移动、磁盘I/O等)的噪声来收集随机性,并将其整合以产生高质量的随机数。由于这些物理过程本身具有随机性,因此可以确保生成的随机数是真正的随机数,而不是伪随机数
# 查看设备文件
$ ls -l /dev/ | grep -e  "null\|zero"

crw-rw-rw- 1 root root   1,   3 Jun 21 20:39 null
crw-rw-rw- 1 root root   1,   5 Jun 21 20:39 zero
# /dev/null 示例

$ echo "Hello World" > /dev/null
# /dev/full 示例

$ echo: write error: no space left on device
# /dev/zero 示例
# 将 /dev/zero 的输出写入到 text.txt 文件中,并且限制文件的大小为 10MB。
# 由于从 /dev/zero 中读取的所有数据都是零值,因此最终 test.txt 文件中将包含 10MB 的零值字节

$ dd if=/dev/zero of=test.txt bs=1M count=10
# /dev/random 示例
# 从 /dev/random 中读取 16 个字节的随机数据,并转换为一个无符号整数 (十进制表示形式)
# -t u4 参数指定以无符号 4 字节整数形式输出数据
# -An 参数指定不要输出地址信息

$ head -c 16 /dev/random | od -t u4 -An

5. 链接文件

使用 ln 命令创建硬链接和软链接。

# 创建硬链接
$ ln /tmp/abc.txt /tmp/def.txt

# 创建软链接
$ ln -s /tmp/abc.txt /tmp/def.txt

5. 套接字文件

套接字文件是一种特殊类型的文件,用于实现进程间通信 (IPC), 可以看作是网络套接字与本地套接字的混合体。

对于进程间通信,套接字提供了一种简单而强大的方式来传递数据,它们可以在同一台计算机上的进程之间进行通信,也可以在不同计算机上的进程之间进行通信。 套接字文件通常存储在文件系统的特定目录中,例如 /var/run 或 /tmp,通常分为由操作系统系统软件创建的和由应用程序创建的。

6. 命名管道文件

命名管道允许多个进程之间通过一个共享的命名管道文件进行通信,进程可以向管道写入数据,并从管道读取数据,就好像在普通文件中读写数据一样。

查看文件类型

调用 ls 命令时,输出结果中会逐行输出单个文件,第一个字符 表示该文件的类型。

字母 文件类型
- 普通文件
d 目录文件
l 链接文件
c 特殊文件
s 套接字文件
p 命名管道文件
b 块设备文件
# 例如,我们可以查看 /var/run 目录下的文件列表
$ ls -l /var/run

total 60
drwxr-xr-x 2 dnsmasq  nogroup  4096 Jun 20 21:51 dnsmasq
drwx------ 6 root     root     4096 Jun 24 08:09 docker
-rw-r--r-- 1 root     root        3 Jun 24 08:09 docker.pid
srw-rw---- 1 root     docker      0 Jun 24 08:09 docker.sock
-rw-r--r-- 1 root     root        4 Jun 24 08:09 docker-ssd.pid
drwxr-xr-x 3 root     root     4096 Jun 20 22:34 google
drwxrwxrwt 2 root     root     4096 Jan  1  1970 lock
drwxr-xr-x 2 root     root     4096 Jun 24 08:09 metrics
drwxr-xr-x 2 root     root     4096 Jan  1  1970 mount
drwxrwsr-x 2 postgres postgres 4096 Jun 20 21:54 postgresql
-rw-r--r-- 1 root     root        1 Jun 24 08:09 rsyslogd.pid
drwxr-xr-x 3 root     root     4096 Jun 20 21:52 runit
drwxr-xr-x 2 root     root     4096 Jun 20 21:55 sshd
-rw-r--r-- 1 root     root        4 Jun 24 08:09 sshd.pid
-rw-r--r-- 1 root     root        3 Jun 24 08:09 supervisor.pid
-rw-rw-r-- 1 root     utmp      768 Jun 24 08:09 utmp
-rw------- 1 root     root        0 Jun 24 08:09 xtables.lock

常见文件操作

API 语义
open 打开或创建一个文件
read 从一个已打开的文件中读取数据
write 向一个已打开的文件中写入数据
close 关闭一个已经打开的文件句柄

开发者可以直接调用 API 对文件进行操作,而无需关心文件的具体类型,这种 “多态性” 是直接建立在操作系统上面的。

小结

Linux 中 一切皆为文件 的设计理念带来了许多好处:

  • 统一接口: 使用相同的 API 可以对所有类型的文件进行操作,例如读取、写入、移动、删除、修改权限等
  • 简化管理: 使用相同的工具和方式来管理文件和设备的操作,例如备份、复制、移动、删除、链接等
  • 编程性 : 一个程序可以通过文件与其他程序通信,使得程序之间的交互、测试、集成等行为变得更容易,并且可以针对不同的应用场景,使用适用的编程语言来编写代码,提升灵活性和开发效率
  • 扩展性 : 可以创建自定义的文件系统以支持特定的文件或设备类型,最终将新的硬件或软件集成到已有系统中

Reference

转载申请

本作品采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,商业转载请联系作者获得授权。