为什么 Linux “一切皆文件”
概述
在 Linux 中,包括硬件设备、目录、文本文件、二进制文件、输入输出流、套接字、管道、TTY 终端等各种资源都被抽象成文件,并且它们之间没有太大的区别。 每个文件都有一个唯一的路径进行标识,因此可以通过文件系统中的路径来访问和管理这些资源。
一切皆为文件的理念提供了最高级的抽象,开发者只需要使用一套 API 就可以操作 Linux 系统中的绝大部分资源。
一个典型的 Linux 系统的文件结构如下所示:
常见文件类型
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 可以对所有类型的文件进行操作,例如读取、写入、移动、删除、修改权限等
- 简化管理: 使用相同的工具和方式来管理文件和设备的操作,例如备份、复制、移动、删除、链接等
- 编程性 : 一个程序可以通过文件与其他程序通信,使得程序之间的交互、测试、集成等行为变得更容易,并且可以针对不同的应用场景,使用适用的编程语言来编写代码,提升灵活性和开发效率
- 扩展性 : 可以创建自定义的文件系统以支持特定的文件或设备类型,最终将新的硬件或软件集成到已有系统中