文件系统

文件索引节点:inode

两部分 纯数据区

  • 文件真正的数据存储区、基本存储单位为block

元数据区

  • 文件属性:磁盘中的存储位置、文件长度等信息
  • 时间戳:创建时间、修改时间
  • 读写权限:使用read/write系统调用时,要首先要进行权限检
  • 所属组、所有者
  • 链接数

挂载

挂载是一个非常重要的功能,使用非常频繁。它指将一个设备(通常是存储设备,可以挂载光盘、硬盘、磁带、光盘镜像文件等)挂接到一个已存在的目录上,那么,你访问这个目录就是访问这个设备(这个目录可以不为空,但挂载后这个目录下以前的内容将不可用) ,一个目录一旦成为"挂载点",该目录下看到的内容,来自"被挂载的文件系统", 与父目录的存储介质完全无关。

挂载需要有 挂载源(设备) 和 挂载点(目录)

/var             → /dev/mtdblock5  (JFFS2 flash)
/var/run         → tmpfs           (RAM)
/var/run/hdd0    → /dev/sda2       (HDD / ext2)

进程间通信

image-20250622223836271

  • 通过文件

  • 通过内核

  • 共享内存

匿名管道

image-20250622230944399

匿名管道是一种只用于亲缘关系进程间、基于内核缓冲、半双工、顺序可靠的字节流通信方式,读写都会阻塞,必须注意正确关闭读写端才能避免死锁。

匿名管道的基本用法

  1. 创建匿名管道
int pipe_fd[2];
pipe(pipe_fd);   // pipe_fd[0] = 读端, pipe_fd[1] = 写端
  1. fork 之后由父子进程各关闭不需要的端口
  • 写数据的进程:关闭读端
  • 读数据的进程:关闭写端

否则 read/write 行为会受到影响(例如 EOF 不会出现)

  1. 父子通过 read / write 进行通信
  • write(pipefd[1], buf, n)
  • read(pipefd[0], buf, n)

匿名管道的阻塞行为(重点)

Write 可能阻塞的情况:

  • 管道缓冲区已满时,write 阻塞,只有当读进程读走数据后,write 才能继续

Read 可能阻塞的情况:

  • 管道缓冲区为空 → read 阻塞,直到写进程写入更多数据
  • 如果写端全部关闭 → read 返回 0(代表 EOF)

匿名管道的注意事项

  1. 必须正确关闭端口,否则会出现以下情况:
    1. 读端不关闭写端 ⇢ 读不到 EOF,一直阻塞
    2. 写端不关闭读端 ⇢ 异常行为,甚至 SIGPIPE
  2. 管道大小有限 ,一般为64KB
  3. 匿名管道不能用于非亲缘进程
  4. 多个写端写入时是原子化的?
    1. 小于 PIPE_BUF(通常 4096 字节)的写入是原子的
    2. 大写可能导致数据交错
  5. 管道是流式无格式通信,没有消息边界,需要自己定义协议格式(分包、长度)

问:如果读端进程已经关闭(close(pipefd[0])),写进程继续 write 会发生什么?

答 write 失败,收到 SIGPIPE

因为匿名管道有一个特殊规则:

  • 规则:当所有读端被关闭时:
    • write 不再阻塞
    • write 立即返回错误:EPIPE
    • 并向进程发送 SIGPIPE 信号
      • → 如果进程没有处理该信号,会被终止!
    • 这就是“Broken Pipe”错误的来源。

写端写管道时,如果所有读端都关闭,write 不会阻塞,而是立即失败,并收到 SIGPIPE。

例如:

每个进程各关闭一端,我们强制建立了一个明确的单向通信流:子进程 -> 父进程

  • 对于读者(父进程): 只有当所有指向管道写端的文件描述符都被关闭时,读者在读取管道时才会接收到 EOF(文件结束符)。如果父进程不关闭自己的 fds[1],那么即使子进程退出了(关闭了它的 fds[1]),管道的写端依然被父进程自己持有着。这样,父进程在读取 fds[0] 时永远不会拿到 EOF,可能会导致程序死锁,一直在等待永远不会到来的数据。
  • 对于写者(子进程): 如果读者(父进程)关闭了读端,子进程再尝试写入数据时,操作系统会向子进程发送 SIGPIPE 信号,表明管道已断开。

命名管道 FIFO

与匿名管道不同,有文件名,FIFO最大的特性就是每个FIFO都有一个路径名与之相关联,从而允许无亲缘关系的任意两个进程间通过FIFO进行通信。

int mkfifo(const char * pathname,mode_t mode);

特点

  1. 使用方式像文件,但不能 seek,你可以通过:
  • open()
  • read()
  • write()
  • close()
  • 但不能lseek,因为 FIFO 是流式文件,没有偏移量。
  1. 进入 FIFO 的数据顺序,就是读出的顺序。
  2. 管道一样,FIFO仅提供半双工的数据通信

open 行为会阻塞

  1. 打开 FIFO 的读端,如果没有写端打开 → 阻塞
int fd = open("fifo", O_RDONLY);  // 没有写端

会阻塞,直到有进程打开写端为止。

  1. 打开 FIFO 的写端,如果没有读端打开 → 阻塞
int fd = open("fifo", O_WRONLY);  // 没有人读

也会阻塞,直到读端出现。

⚠️ 这是命名管道区别于匿名管道的重要特性!!!

匿名管道是在父子进程中自然同时存在读写端,而 FIFO 是“双方需要主动 open”,因此更容易阻塞。

PIPE 与 FIFO 的共同点

  1. 内核实现层面:本质是同一套机制
    • 内核维护的一块 环形缓冲区
    • 基于 page cache
    • 通过 等待队列 + 锁 实现同步与阻塞
  2. 都是“流式文件”(Stream)
    • 不支持 lseek
    • 只能顺序读 / 顺序写
  3. 都是 FIFO(先进先出)
    • 先写入的数据,先被读出
    • 没有消息边界(除非你自己定义协议)

共享内存

D-BUS

D-BUS**(Desktop Bus)是一种“消息总线式”的进程间通信(IPC)机制。**用于让不同进程互相通信

特点:

  • 多个进程共享一个**“软件总线”**
  • 广播消息
  • 点对点 :A 进程直接发给 B 进程

System Bus(系统总线) +