>> >> >> Reference << << << <<<<<<Ref>>>>>>
>> >> >> Indexer << << << <<<<<<Idx>>>>>>
Matched: 0

Tags

    Categories

      Types

        Top Results

          input output
          M: 2026-01-14 -

          Input Output

          Linux的输入输出模型

          Linux一切皆文件(Everything is file),终端、设备、文件、网络连接等都抽象成“文件”。命令的输入和输出最终都是文件I/O

          File Descripter(FD)

          每个进程启动时,都会默认打开三个文件描述符(File Descripter, FD)
          文件描述符是一个非负整数,它是一个索引,进程通过这个索引在内核维护的“打开文件表”中查找对应的文件信息
          简单来说:文件描述符是进程和内核之间交互文件的“句柄”或“令牌”

          FD的本质与内核数据结构

          内核管理打开文件涉及到三个关键的数据结构

          1. 进程级的文件描述符表
            • 每个进程都有一个私有的表
            • 表的索引就是文件描述符(FD)
            • 表中的每一项包含一个指向内核打开文件表中的某个条目的指针
            • 此外,它还包含一些进程级的标志,比如FD_CLOEXEC
          2. 系统级的打开文件表
            • 这是一个全局的表,被所有进程共享
            • 表中的每一项(称为一个file结构体)代表一个被打开的文件,它包含了
              • 文件状态标志:如O_RDONLY(只读)、O_WRONLY(只写)、O_APPEND(追加)等
              • 当前文件偏移量:这是文件读/写操作开始的位置。当使用read()write()时,这个偏移量会自动更新
              • 指向该文件的v-node表的指针
          3. v-node表
            • 这也是一个全局的表
            • v-node(虚拟节点)包含了文件的静态信息,例如
              • 文件类型(普通文件、目录、套接字等)
              • 文件的inode
              • 文件的大小
              • 文件的所有者和权限
              • 指向文件操作函数的指针(如read, write

          多个文件描述符(甚至来自不同进程)可以指向同一个“打开文件表”条目。这意味着它们共享文件状态和文件偏移量。这通常是通过fork()(子进程继承父进程的FD)或dup()系列函数实现的 多个“打开文件表”条目可以指向同一个v-node。这意味着同一个文件被独立打开了多次。每个打开都有自己的文件状态和偏移量,互不干扰

          标准文件描述符

          FD名称常用缩写默认绑定对象说明
          0标准输入stdin键盘程序的输入源
          1标准输出stdout屏幕程序的正常输出
          2标准错误stderr屏幕程序的错误输出

          这就是为什么在终端运行命令时,输入来自键盘,结果和错误都显示在屏幕上
          内核通过文件描述符表来管理进程打开的文件,0、1、2是固定的下标索引
          这就是为什么可以在C语言中用read(0, ...)从标准输入读取,用write(1, ...)向标准输出写入

          输出重定向(Redirection)

          重定向的本质是修改文件描述符的默认绑定目标

          语法作用原理
          command > file将stdout重定向到file(覆盖)内核将FD 1从屏幕改为指向file,进程对此无感知
          command >> file将stdout重定向到file(追加)同上,但以O_APPEND模式打开文件
          command 2> file将stderr重定向到file(覆盖)修改FD 2的指向
          command 2>&1将stderr合并到stdout让FD 2成为 FD 1的一个副本,两者指向同一个地方
          command &> file command >& file将stdout和stderr都重定向到file2>&1> file的语法糖(注意顺序:先重定向1,再让2指向1)
          command < file将stdin重定向为file修改FD 0的指向,使其从file读取数据

          原理:Shell在fork()出子进程后、exec()执行命令前,会根据重定向符号调用open()dup2()等系统调用,修改子进程的文件描述符表。进程本身只管对FD 0,1,2进行读写,完全不知道底层目标已被改变\

          管道

          在Linux中,管道不仅仅是一个|符号——它是一个完整的进程间通信机制。它是Unix哲学的核心:编写只做好一件事的小程序,并将它们链接在一起

          核心概念

          • 管道是一个由内核管理的缓冲区(匿名内存区域),用于将数据从一个进程传递到另一个进程
          • Shell中的|操作符将左侧命令的标准输出连接到右侧命令的标准输入
          ls -l | grep ".txt"
          

          ls -l将文件列表写入管道,grep从管道中读取并过滤出只包含.txt的行

          内核的行为

          当输入一个管道命令时,Shell执行以下步骤:

          1. 使用pipe()系统调用创建一个管道,它返回两个文件描述符
          • fd[0]= read end
          • fd[1]= write end
          1. 为管道中的每个命令创建子进程
          2. 重定向数据流:
          • 在第一个命令中,标准输出被替换为fd[1]
          • 在第二个命令中,标准输入被替换为fd[0]
          1. 关闭为使用的端口以防止死锁
          2. 并发运行命令 内核通过缓冲区移动数据。如果缓冲区(通常约为64KB)填满,写入会阻塞,直到读取消费数据——这称之为反压

          标准流

          默认情况下,只有标准输出会通过管道
          如果也想传递错误信息:

          command 2>&1 | nextcommand
          

          2>&1merges stderr(2) into stdout(1)

          多级管道

          管道可以无限连接

          cat access.log \
          | grep "404" \
          | cut -d' ' -f1 \
          | sort \
          | uniq -c \
          | sort -nr
          

          数据流:cat -> grep -> cut -> sort -> uniq -> sort
          每个命令执行一个小的、单一的任务;组合起来,它们就形成了一条强大的处理流水线

          匿名管道 vs 命名管道

          • 匿名管道:由|创建,仅在关联进程的生命周期内存在
          • 命名管道:使用mkfifo mypipe创建,作为一个特殊文件出现,可以被不相关的进程读写

          重要性

          • 无需临时文件——数据保留在内存中
          • 并行性——所有阶段同时运行
          • 可组合的设计——小程序可以粘合在一起解决复杂问题

          示例:重定向与管道混合使用

          • 管道默认只传输标准输出,除非使用2>&1显式合并标准错误
          • 重定向的顺序很重要。Shell从左到右解析,但像2>&1这样的合并操作,如果想包含标准错误,必须在管道符之前出现
          1. 传输标准输出,将标准错误保存到文件
          command 2>errors.log | next
          
          • stdout -> next
          • stderr -> errors.log
          1. 同时传输标准输出和标准错误
          command 2>&1 | next
          
          • 2>&1 将标准错误合并到标准输出
          • 合并后的流 -> next
          1. 传输数据的同时保存副本到文件
          command | tee output.log | next
          
          • tee复制标准输出:一份到output.log,一份到 next
          • 如果想包含标准错误,添加2>&1
          command 2>&1 | tee output.log | next
          
          1. 将管道的输出重定向到文件
          cat file | grep keyword > result.text
          
          • 管道: cat -> grep
          • final stdout of grep -> result.txt
          1. 多级管道示例
          grep "ERROR" app,log 2>grep_err.log \
          | cut -d' ' -f1 \
          | sort \
          | uniq -c \
          | tee summary.txt \
          | sort -nr > final.txt
          

          Flow:

          • grep stdout -> cut -> sort -> uniq -> tee -> sort
          • grep stderr -> grep_err.log
          • tee write a copy to summary.txt
          • last sort write final output to final.txt

          进程组

          基本概念

          • 进程组:一组共享一个进程组ID的进程
            • 每个进程仍然有自己的PID
            • PGID通常等于该组中的第一个进程(组长)的PID
          • 会话:一个或多个进程组组成一个会话
            • 会话有一个会话首进程
            • 用于管理终端访问和信号分发

          目的

          • 信号控制:终端可以向整个进程组发送信号(例如,Ctrl+C -> SIGINT),而不仅仅是单个进程
          • 作业控制:Shell可以暂停、恢复或将整个进程组置于后台(如管道中的所有命令)
          • 终端管理:进程组和会话隔离了输入/输出的控制

          Shell管道中的进程组

          ls -l | grep ".txt" | sort
          
          • Shell为整个管道创建一个进程组
          • lsgrepsort都属于同一个组
          • 按下Ctrl+C时
            • 终端向整个进程组发送SIGINT信号
            • 所有三个命令都接收到信号并同时终止,而不仅仅是最后一个命令