Linux(5)(上)

一.文件管理共识

  1. 文件 = 内核 + 属性

  2. 文件分为打开文件和没打开文件

  3. 被打开的文件本质是被进程打开的(进程:打开的文件=1 :n)

  4. 没打开的文件放在磁盘上,我们关心的是这些文件是然后存储的,因为我们要快速找到文件才能进行赠删查改

  5. 操作系统内部存在大量打开的文件,那么怎么管理呢(先描述,在组织)在内核中,一个打开的文件一定存在其打开对象,包含文件的很多属性

二.了解文件

  1. 以C为主了解

    • 当前路径是什么,当前路径其实就是进程的当前路径(cwd),如果更改了进程的当前路径那么当前路径就被修改了。

    • C在默认启动时自动打开三个标准输出输入流:stdin(键盘输入流)stdout(显示屏输出流)stderr(显示屏错误流)

  2. 通过系统了解

    • 文件其实是磁盘上的,而磁盘是外部设备,所以访问磁盘文件就是访问硬件

    • 几乎所有的库只要是访问硬件设备的必定要封装系统调用(因为自定向下一层一层访问)

  3. 访问文件的本质

  • 左侧的和进程有关,右侧和文件管理有关,产生关联是蓝色这个表的下标

  • fd从3开始,因012分别对应C语言默认启动的输入输出流(文件)其实这个C语言的默认操作本质是操作系统的特性,操作系统本身就会打开3个文件,因此C语言默认同样也要打开3个文件(输入输出流)

  • FILE是C库自己封装的结构体,这里面必须封装文件的描述符

三. 知识补充

3.1 C对文件的操作

1.fopen

cpp 复制代码
// 打开或创建一个文件,并返回对应的 FILE 流指针,用于后续 I/O 操作
#include <stdio.h>

FILE *fopen(const char *pathname, const char *mode);

// 参数:
//   pathname - 文件路径(可以是相对路径或绝对路径)
//   mode     - 打开模式字符串,决定文件的访问方式和初始位置,常见值包括:
//              "r"  : 只读,文件必须存在
//              "w"  : 只写,若文件存在则清空,不存在则创建
//              "a"  : 追加写,文件指针定位到末尾,不存在则创建
//              "r+" : 读写,文件必须存在
//              "w+" : 读写,若文件存在则清空,不存在则创建
//              "a+" : 读写追加,读从开头,写始终在末尾,不存在则创建
//              (可附加 "b" 表示二进制模式
//                如 "rb"、"wb+",在 Linux 中效果与文本模式相同)

// 返回值:
//   成功:返回指向 FILE 结构的指针(用于后续 fread/fwrite/fclose 等)
//   失败:返回 NULL,并设置 errno

2.fread

cpp 复制代码
// 从 FILE 流中读取指定数量的数据项
#include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

// 参数:
//   ptr    - 指向用户提供的缓冲区,用于存放读取的数据
//   size   - 每个数据项的字节大小(如 sizeof(int))
//   nmemb  - 要读取的数据项个数
//   stream - 已打开的输入 FILE 流(如 stdin 或 fopen 返回的指针)

// 返回值:
//   成功:返回实际读取到的'完整数据项个数'(<= nmemb)
//         若返回值小于 nmemb,可能是到达文件末尾或发生错误
//   失败或 EOF:返回 0(若一开始就遇到 EOF)或部分值;
//                需配合 feof() 或 ferror() 判断具体原因

3.fwrite

cpp 复制代码
// 将指定数量的数据项从缓冲区写入 FILE 流
#include <stdio.h>

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

// 参数:
//   ptr    - 指向要写入的数据缓冲区
//   size   - 每个数据项的字节大小
//   nmemb  - 要写入的数据项个数
//   stream - 已打开的输出流(如文件或 stdout)

// 返回值:
//   返回实际成功写入的完整数据项个数(<= nmemb)
//   若返回值小于 nmemb,表示发生错误(需用 ferror() 检查)

4.fclose

cpp 复制代码
// 关闭一个已打开的 FILE 流,并刷新其缓冲区(若为输出流)
#include <stdio.h>

int fclose(FILE *stream);

// 参数:
//   stream - 要关闭的 FILE 流指针(必须是由 fopen、fdopen 等返回的有效指针)

// 返回值:
//   成功:返回 0
//   失败:返回 EOF(通常因刷新输出缓冲区时出错,如磁盘满、I/O 错误等)

3.2 系统接口

1.open

cpp 复制代码
// 打开或创建一个文件,并返回其文件描述符(非缓冲,底层 I/O)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags, ... /* mode_t mode */);

// 参数:
//   pathname - 文件路径(字符串)
//   flags    - 打开标志,控制访问模式和行为,常用组合如:
//              O_RDONLY, O_WRONLY, O_RDWR(必须三选一)
//              O_CREAT  :若文件不存在则创建
//              O_TRUNC  :若文件存在则清空内容
//              O_APPEND :写入时始终追加到文件末尾
//              O_EXCL   :与 O_CREAT 同用,确保文件不存在(用于原子创建)
//   mode     - 可选参数,仅当使用 O_CREAT 时需要,指定新文件的权限(如 0644)

// 返回值:
//   成功:返回非负整数(文件描述符,如 3、4...)
//   失败:返回 -1,并设置 errno

2. read

cpp 复制代码
// 从文件描述符中读取数据到缓冲区
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

// 参数:
//   fd    - 文件描述符(由 open、pipe、socket 等返回的非负整数)
//   buf   - 指向用户提供的缓冲区,用于存放读取的数据
//   count - 最多读取的字节数

// 返回值:
//   成功:返回实际读取的字节数(可能小于 count);
//         返回 0 表示已到达文件末尾(EOF)
//   失败:返回 -1,并设置 errno

3. write

cpp 复制代码
// 将数据从缓冲区写入到文件描述符
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

// 参数:
//   fd    - 文件描述符(由 open、pipe、socket 等返回的非负整数)
//   buf   - 指向要写入的数据缓冲区
//   count - 要写入的字节数

// 返回值:
//   成功:返回实际写入的字节数(可能小于 count,尤其在管道、套接字或信号中断时)
//   失败:返回 -1,并设置 errno

4.close

cpp 复制代码
// 关闭一个文件描述符
#include <unistd.h>

int close(int fd);

// 参数:
//   fd - 要关闭的文件描述符

// 返回值:
//   成功:返回 0
//   失败:返回 -1,并设置 errno

3.3 文件描述符

通过对open函数的学习,我们知道了文件描述符就是一个小整数

0 1 2

  • Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.

  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器

文件描述符的分配规则

  • 在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

四.重定向

概念: 在 Linux 中,重定向 是指改变命令的默认输入/输出方向

1. 通过文件描述符的分配规则实现

当标准输出被关闭时,我myfile就会使用1这个下标,作为其文件描述符(注意要先关闭标准输出,然后再创建myfile)

2. 通过函数dup2

cpp 复制代码
// 将文件描述符 oldfd 复制到 newfd,若 newfd 已打开则先关闭它
#include <unistd.h>

// 该函数实现的是newfd拷贝oldfd从而实现指向的对象
// 此时可以把oldfd关闭,因为newfd同样指向oldfd指向的对象
int dup2(int oldfd, int newfd);

// 参数:
//   oldfd - 源文件描述符
//   newfd - 目标文件描述符(会被强制设为 oldfd 的副本)

// 返回值:
//   成功:返回 newfd
//   失败:返回 -1,并设置 errno

例子
int fd = open("output.txt", O_WRONLY | O_CREAT, 0644);
dup2(fd, 1);  // 现在 printf 会写入 output.txt,而不是屏幕

这个dup2很奇怪不符合人类直觉,要多看几遍

3. >>和>的

前者是追加重定向,后者是覆盖重定向,二者为什么是这样的呢?

O_TRUNC :若文件存在则清空内容;O_APPEND :写入时始终追加到文件末尾

4.总结和补充

  • 重定向的核心机制是:将一个已打开文件的 struct file* 指针复制到标准文件描述符 (如 1)在进程文件描述符表中的槽位,从而改变 I/O 的目标。
  • 进程替换不会干扰文件访问操作

五.理解一切皆文件的概念

在 Linux 中,为了统一管理硬件设备,内核通过 VFS(虚拟文件系统) 将设备抽象为"文件"。用户通过 open() 获得文件描述符,内核创建 struct file 对象,其中包含指向设备专属操作函数(file_operations)的指针。当调用 read()/write() 时,内核通过该指针调用对应驱动函数,从而访问设备。这种机制利用函数指针实现了类似"多态"的效果,而设备本身的属性由驱动私有结构体管理,与 struct file 分离。

关于这部分内容就先讲到这里,本章的重点还是非常多的,比如系统接口有那些这么实现的,文件描述符是什么东西,以及重定向概念、实现方法。都是重中之重,希望大家重点记忆相关知识!

相关推荐
2401_890443022 小时前
Linux EXT系列文件系统
linux
菜鸡00012 小时前
安装glog
linux
点云SLAM2 小时前
Scenarios 英文单词学习
学习·英文单词学习·雅思备考·情景 / 情节·剧情 / 故事情景·scenarios
lbb 小魔仙2 小时前
eBPF+Linux 6.18:云原生环境下的安全监控与故障排查实战
linux·运维·云原生
包小黑2 小时前
【Linux】bug登记好习惯:发现bug,用命令行截取对应日志
linux·bug
KingRumn8 小时前
Linux信号之标准信号与实时信号
linux·算法
QT 小鲜肉10 小时前
【Linux命令大全】001.文件管理之git命令(实操篇)
linux·服务器·笔记·git·elasticsearch
半夏知半秋10 小时前
docker常用指令整理
运维·笔记·后端·学习·docker·容器
sishen419910 小时前
嵌入式Linux没有学习方向怎么办,嵌入式Linux怎么学
linux