【Linux】Linux 操作系统 - 18 , 重谈文件(二) ~ 文件描述符和重定向原理 , 手把手带你彻底理解 !!!

news2025/6/4 23:39:33

文章目录

  • 文件描述符
    • 一 、Linux 系统对文件的管理(要知道)
    • 二 、什么是文件描述符 fd ?
    • 三 、再探文件被管理过程(重要)
    • 四 、文件描述符 0 、1、2
      • 1. 文件描述符的分配原则
      • 2. 提前认识三个默认打开的文件
  • 重定向原理(重要)
    • 一 、重定向现象
    • 二 、深入剖析重定向现象(重要)
      • 1 . 重定向的本质是什么 ???
      • 2 . 各种重定向实现原理
        • 2.1 dup2 系统调用(理解 , 掌握)
        • 2.2 输出重定向
        • 2.3 输入重定向
        • 2.4 追加重定向
      • 3 . 重定向的原理(面试考)
  • 正式谈三个默认打开的文件
    • 一 、stderr
    • 二 、重定向的使用(重要)(重点讲解标准错误)
      • 1 . 重定向的完整写法
      • 2 . 将错误信息和正常信息隔离(重要)
      • 3 . 将错误信息和正常信息打印在一起
  • 总结


文件描述符


  篇章一中笔者提到了文件描述符的概念 , 本节就详细对其进行介绍 !

  对以下系统调用中会涉及文件描述符的概念 :

write(int fd, ....) - write to a file descriptor - 文件描述符

read( int fd, .... ) - read from a file descriptor - 文件描述符 

close(int fd) - close a file descriptor  - 文件描述符 

一 、Linux 系统对文件的管理(要知道)


  之前第一篇章提到操作文件 , 就是进程操作文件 ! 那么考虑一个问题 ???
  在系统中一切皆文件 , 那么文件多了 , 要不要被管理呢 ???

答 : 文件要被管理 ! 那怎么管理呢 ??? 先描述 , 再组织 !
  既然要描述 , 那么就会有数据结构吧 !

以下便是具体描述 :

在这里插入图片描述


二 、什么是文件描述符 fd ?


  上面也提到了文件被管理是通过文件描述符表管理的 , 那这个文件描述符表是个什么呢 ???

  • 文件描述符表是一个数组 !
  • 文件描述符的本质就是数组的下标 ! , 即 : fd 的本质就是数组的下标 !
  • flies_struct 这个数据结构中包含一个结构体指针数组 , 这个数组就是文件描述符表 !
  • 内核源代码中的 files_struct
struct files_struct {
....
....
....
....
....
....

//其中包含一个结构体指针数组
struct file __rcu * fd_array[NR_OPEN_DEFAULT];  // 文件描述符表 
};

在这里插入图片描述

  • 每打开一个文件 , 就会有一个文件描述符来描述该文件 , 即 : 就会有一个指针指向该文件 !

在这里插入图片描述


三 、再探文件被管理过程(重要)

在这里插入图片描述


  总结文件被管理的过程(面试可能考) :

在这里插入图片描述
记住一个图 :
在这里插入图片描述


  所以 , 还可以得出一个结论 :

对文件内容的任何操作 , 必须先把文件的内容加载到内核对应的文件缓冲区内 , 避免频繁 I/O , 提高效率 !


四 、文件描述符 0 、1、2


  有了以上的理解 , 现在便好介绍 0 , 1 , 2 了 .

  笔者之前提过 , 系统会为每个文件分配一个文件描述符 , 每一个描述符对应一个文件 !

1. 文件描述符的分配原则


  因为文件描述符是数组的下标 , 所以会有以下原则 :

  • 找到 files_struct 数组中没有被使用的最小的下标 , 作为当前文件的描述符 ! 注意 : 是最小的下标 !!!
/ fd 分配原则

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
	//close(0);
	//int fd = open("log.txt" , O_CREAT | O_WRONLY | O_TRUNC , 0666);	
	//printf("fd : %d\n",fd);   **********  打印 0
	
	//close(2);
	//int fd = open("log.txt" , O_CREAT | O_WRONLY | O_TRUNC , 0666);	
	//printf("fd : %d\n",fd);   **********  打印 2

		
	int fd = open("log.txt" , O_CREAT | O_WRONLY | O_TRUNC , 0666);	
	printf("fd : %d\n",fd);    **********  打印 3
	
	return 0;
}


2. 提前认识三个默认打开的文件


  之前笔者也讲过 , 系统会自动打开三个默认文件 !

  这三个默认文件 , 分别对应 :

  • 键盘 --- 文件描述符 0
  • 显示器 --- 文件描述符 1
  • 显示器 --- 文件描述符 2

观察一个现象

#include <stdio.h>
#include <unistd.h>

int main()
{	
	printf("进入第一次输入 :\n");
	int a = 0;
	scanf("%d",&a);
	printf("%d\n",a);
	close(0);
	printf("进入第二次输入 :\n");
	int b = 10;
	scanf("%d",&b);
	printf("%d\n",b);
	
	return 0;
}

在这里插入图片描述

所以 , 上面完全可以印证文件描述符 0 对应的就是键盘文件(C语言 — stdin ) !


  其余 , 学者可以自行验证 !


重定向原理(重要)


  我们之前经常用 > , 这个就是重定向 , 那么其原理真的了解吗 ?

  重定向分类 :

  • 输出重定向
  • 输入重定向
  • 追加重定向

一 、重定向现象


  给出以下代码 : 你会发现什么现象 ??

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main()
{	
	// 演示输出重定向现象 , 即 1 -- 对于 stdout
	// 关闭 1 对于的文件 , 即 : 关闭显示器文件
	close(1);
	int fd = open("redir.txt" , O_CREAT|O_WRONLY | O_APPEND , 0666);
	
	//打印 , 默认打印到显示器 , 即 : 只会向 1 设备文件中打印 ; 但当关闭 stdout 文件后 , 会打印到 redir.txt 中 
	printf("I am file , defalut printf to stdout\n");
	
	
	return 0;
}


 *************** 关闭 1 , 不会在显示器上打印了 , 而是打印在了 redir.txt 中 **************

二 、深入剖析重定向现象(重要)


  对以上的现象 , 为什么关闭了 1 对应的文件 , 不会打印在显示器 , 却打印到了其它文件了呢 ??

在这里插入图片描述

在这里插入图片描述

1 . 重定向的本质是什么 ???


  通过以上讲解的 , 现象的剖析 , 便可得到 :

重定向的本质 : 改变文件描述符的指针指向 !!!!


2 . 各种重定向实现原理

2.1 dup2 系统调用(理解 , 掌握)


  这里介绍一个系统调用 :

 dup, dup2, dup3 - duplicate a file descriptor , 复制一个文件描述符

 int dup2(int oldfd, int newfd);
 描述 : 使用新的 newfd 这个文件描述符 , 形成 oldfd 这个文件描述符的拷贝 !


 简单理解 : 它会将 oldfd 所指向的文件复制到 newfd,新的会指向旧的内容 !

在这里插入图片描述


2.2 输出重定向
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

// ls > xxx , 往 xxx 写入
int main()
{
	int fd = open("dir.txt" , O_CREAT | O_WRONLY | O_TRUNC , 0666);
	
	// old , new , 新的指向旧的 
	dup2(fd , 1); // 1 本身指向的是显示器文件 , 在 C语言中为 : stdout , 现在指向 fd 
	
	printf("Hello newfile\n");
	const char* str = "dir success!\n";
	write(1 , str , strlen(str));

	close(fd); 
	
	return 0;
}

在这里插入图片描述

  通过我们手动编写的原理 , 这里就可以知道为什么 > 符号可以用来新建文件了 . 因为 open 文件时会 O_CREAT , 文件不存在 , 就创建 !


2.3 输入重定向
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

// ls < xxx , 从 xxx 读取内容
int main()
{
	int fd = open("dir.txt" , O_RDONLY);
	
	dup2(fd , 0); // 0 原来指向键盘文件 , 默认从键盘上读取内容, 现在指向 dir.txt 
	
	char buff[200];
	memset(buff , 0 , sizeof(buff));
	//读 fd 文件	
	while(1)	
	{	
		// 从 0 指向的设备文件中读 
		ssize_t red = read(0 , buff , sizeof(buff)-1);	// 不读 \0 l 

		if(red > 0)
		{
			buff[red] = 0;
			printf("%s\n",buff);	
		}		
		if(red == 0 )
		{
			break;
		}
	
	}
	
	
	return 0;
}


  以上就做到了 , 本来是从键盘读取 , 现在是在新的文件中读取 !


2.4 追加重定向


  这里实现一个追加输出重定向 .

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

/ 追加输出重定向
// ls >> xxx , 往 xxx 写入
int main()
{
	int fd = open("dir.txt" , O_CREAT | O_WRONLY | O_APPEND , 0666);
	
	// old , new , 新的指向旧的 
	dup2(fd , 1); // 1 本身指向的是显示器文件 , 在 C语言中为 : stdout , 现在指向 fd 
	
	printf("Hello newfile\n");
	const char* str = "dir success!\n";
	write(1 , str , strlen(str));

	close(fd); 
	
	return 0;
}


  这里就可以发现 , 可以追加写入到 dir.txt 这个文件中了 !


3 . 重定向的原理(面试考)


  通过以上各个重定向的编写 , 我们可以发现一个共同点 , 那就是每个重定向都是 : 打开方式 + dup2 来进行的 !

面试总结 :
在这里插入图片描述


正式谈三个默认打开的文件


  在之前的篇章笔者一直在铺垫三个默认打开的文件 , 但是没有正式讲解 , 这里便给出 !

在这里插入图片描述

  通过文件描述符的学习 , 我们这里就可以知道了 , 以下 :

  • 键盘文件对应文件描述符 0
  • 显示器文件(stdout) 对应文件描述符 1
  • 显示器文件(stderr) 对应文件描述符 2


  所以 , 默认情况下 , 0 , 1 , 2 是被占用的 , 我们之后打开的文件默认是从文件描述符 3 开始的 !


  那对于 stderr 这个显示器文件一直没有介绍 , 以下对其详细解释 !

一 、stderr


  stderr 是显示器文件 , 那么和 stdout 的显示器文件到底有什么区别呢 ??

  • stdout 是显示器文件 , 一般显示我们正常的程序信息 .
  • stderr 是显示器文件 , 一般显示我们错误的程序信息 .
  • 二者本质都是指向同一个硬件 , 但是二者所对应的文件描述符不同 !

描述符不同 , 目的是让我们用重定向进行正常信息和错误信息的隔离 !!!


二 、重定向的使用(重要)(重点讲解标准错误)

1 . 重定向的完整写法

假如这里需要把可执行程序 a.out 里面的内容重定向到文件 log.txt 中 . 

我们平时写的 x > log.txt 的完整写法是 :

a.out 1 > log.txt 

// 解释
意思就是 : 把 a.out 里面的 1 文件描述符对应的内容重定向到 log.txt 中 .


  这里看一段代码理解 :

#include <iostream>
#include <cstdio>

int main()
{	
	//输出 , 默认是在显示器输出 , 1 对应的文件 ,stdout(C语言) , cout(C++)
	printf("Hello C!\n");
	std::cout << "Hello C++!" << std::endl;
	
	//输出错误信息 , 默认也是在显示器输出 , 1 对应文件 ,stderr , cerr
	const char* str = "Hello C err!\n";
	fprintf(stderr , "%s" , str);
	std::cerr << "Hello C++ Err!" << std::endl;
	
	return 0;
}

  • 编译后执行 , 不进行重定向 !

在这里插入图片描述

  • 编译后执行 , 进行重定向 ! (不用完整写法)
    在这里插入图片描述

  • 编译后执行 , 进行重定向 ! (用完整写法)
    在这里插入图片描述


  所以 , 重定向时 , 可以指定文件描述符 , 这是最完整的写法 !!!


2 . 将错误信息和正常信息隔离(重要)


  重定向完整写法的最大应用就是将二者进行隔离 , 这样方便程序员去查看错误信息 !

./mydir 1>log.normal  2>log.err

在这里插入图片描述

  所以 , 要清楚

  • 文件描述符 1 - > 对应的是显示器文件 , stdout / cout , 即 : 一般打印我们程序程序信息 !
  • 文件描述符 2 - > 对于的是显示器文件 , stderr / cerr , 即 : 一般打印我们程序的错误信息 !
  • 通过 重定向 可以将不同的信息打印到不同文件 ! 这是常做的 !!!!

3 . 将错误信息和正常信息打印在一起


  当我们需要将二者信息同时都重定向到一个文件该怎么做 ???
  可能有的人会这样做 :

.mydir  1>log.normal  2>log.normal  // 这个有问题吗 ????????

  上面的做法是有问题的 , 因为 > 底层是用的系统调用 open 呀 , open 的打开文件方式是用了 O_TRUNC 的 , 也就意味着会先清空在写入 , 所以 , 非常错误 !!!

  • 正确做法
./mydir 1>log.normal 2>&1 


2 > &1 的意思就是 : 再把 2 里面的内容添加到 1 里面 , 不清空 !

在这里插入图片描述

总结

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2386790.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第五十三节:综合项目实践-车牌识别系统

一、项目背景与意义 车牌识别系统(LPR)是智能交通领域的核心技术之一,广泛应用于停车场管理、违章抓拍、高速公路收费等场景。本文将通过Python+OpenCV实现一个完整的车牌识别系统,涵盖图像预处理→车牌定位→字符分割→字符识别四大核心环节。 二、系统架构设计 技术栈组…

Git Push 失败:HTTP 413 Request Entity Too Large

Git Push 失败&#xff1a;HTTP 413 Request Entity Too Large 问题排查 在使用 Git 推送包含较大编译产物的项目时&#xff0c;你是否遇到过 HTTP 413 Request Entity Too Large 错误&#xff1f;这通常并不是 Git 的问题&#xff0c;而是 Web 服务器&#xff08;如 Nginx&am…

第10章 网络与信息安全基础知识

网络概述 多模光纤的特点&#xff1a;成本低&#xff0c;宽芯线&#xff0c;聚光好&#xff0c;耗散大&#xff0c;低效&#xff0c;用于低速度、短距离的通信。 单模光纤的特点&#xff1a;成本高&#xff0c;窄芯线&#xff0c;需要激光源&#xff0c;耗散小&#xff0c;高效…

go 访问 sftp 服务 github.com/pkg/sftp 的使用踩坑,连接未关闭(含 sftp 服务测试环境搭建)

前言 最近在使用 sftp 服务时&#xff0c;被告知发起了海量的连接&#xff0c;直接把服务器搞崩&#xff0c;ip 被封了。 这是啥情况&#xff1f; golang 写的代码&#xff0c;我就正常的访问 sftp 服务&#xff0c;连接使用过后也都关闭了&#xff0c;咋会出现连接一直连着…

Linux多线程(二)之进程vs线程

文章目录 Linux进程VS线程进程和线程进程的多个线程共享关于进程线程的问题 重谈地址空间Linux线程周边的概念 Linux进程VS线程 进程和线程 进程是资源分配的基本单位&#xff08;进程是承担分配系统资源的基本实体&#xff09; 执行流也是资源&#xff01;线程是进程内部的执…

【HW系列】—web常规漏洞(文件上传漏洞)

文章目录 一、简介二、危害三、文件检测方式分类四、判断文件检测方式五、文件上传绕过技术六、漏洞防御措施 一、简介 文件上传漏洞是指Web应用程序在处理用户上传文件时&#xff0c;未对文件类型、内容、路径等进行严格校验和限制&#xff0c;导致攻击者可上传恶意文件&…

如何实现 C/C++ 与 Python 的通信

C/C 与 Python 的通信可以通过多种方式实现&#xff0c;如使用 C API、Ctypes、Cython、SWIG、Python.h 或基于共享库的调用等。其中&#xff0c;使用 Ctypes 方式最为简便&#xff0c;适合快速调用已有的 C 函数库。例如&#xff0c;通过将 C 代码编译为动态链接库&#xff08…

好用但不常用的Git配置

参考文章 文章目录 tag标签分支新仓库默认分支推送 代码合并冲突处理默认diff算法 tag标签 默认是以字母顺序排序&#xff0c;这会导致一些问题&#xff0c;比如0.5.101排在0.5.1000之后。为了解决这个问题&#xff0c;我们可以把默认排序改为数值排序 git config --global t…

ULVAC VWR-400M/ERH 真空蒸发器 Compact Vacuum Evaporator DEPOX (VWR-400M/ERH)

ULVAC VWR-400M/ERH 真空蒸发器 Compact Vacuum Evaporator DEPOX (VWR-400M/ERH)

PPT连同备注页(演讲者模式)一块转为PDF

首先&#xff0c;进入创建PDF/XPS&#xff1a; 然后进入选项&#xff1a; 发布选项-发布内容里选备注页&#xff1a; 导出的原始结果是这样的&#xff1a; 这个时候裁剪一下&#xff0c;范围为所有页面&#xff1a; 最终结果&#xff1a; 如果导出不选“备注页”而是只勾选“包…

项目三 - 任务8:实现词频统计功能

本项目旨在实现一个词频统计功能&#xff0c;通过读取文本文件并利用Java编程技巧处理和分析文本数据。首先&#xff0c;使用BufferedReader逐行读取文件内容&#xff0c;然后通过String.split(" ")方法将每行文本分割成单词数组。接下来&#xff0c;采用HashMap来存…

ollama list模型列表获取 接口代码

ollama list模型列表获取 接口代码 curl http://localhost:11434/v1/modelscoding package hcx.ollama;/*** ClassName DockerOllamaList* Description TODO* Author dell* Date 2025/5/26 11:31* Version 1.0**/import java.io.BufferedReader; import java.io.InputStreamR…

OPC Client第5讲(wxwidgets):初始界面的事件处理;按照配置文件初始化界面的内容

接上一讲&#xff0c;即实现下述界面的事件处理代码&#xff1b;并且按照配置文件初始化界面的内容&#xff08;三、&#xff09; 事件处理的基础知识&#xff0c;见下述链接五、 OPC Client第3讲&#xff08;wxwidgets&#xff09;&#xff1a;wxFormBuilder&#xff1b;基础…

【C++进阶篇】初识哈希

哈希表深度剖析&#xff1a;原理、冲突解决与C容器实战 一. 哈希1.1 哈希概念1.2 哈希思想1.3 常见的哈希函数1.3.1 直接定址法1.3.2 除留余数法1.3.3 乘法散列法&#xff08;了解&#xff09;1.3.4 平方取中法&#xff08;了解&#xff09; 1.4 哈希冲突1.4.1 冲突原因1.4.2 解…

Spring Boot——自动配置

目录 1.bean加载方式 1.1XML方式声明bean 1.2 xml 注解方式声明bean 1.3通过Configuration和Bean 1.4使用Import注解 1.5使用上下文对象在容器初始化完毕后注入bean 1.6使用ImportSelector接口 1.7实现ImportBeanDefinitionRegistrar接口 1.8bean加载方式&#xff08;…

使用 Vuex 实现用户注册与登录功能

引言 在构建具有用户认证功能的应用时&#xff0c;Vuex 可以用来管理用户的登录状态和相关信息。以下是如何使用 Vuex 来实现用户注册与登录功能的概述。 &#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端…

进程通信(管道,共享内存实现)

01. 进程通信简介 进程通信工具分为数据传输工具和共享内存两类。这里我们讨论进程通信工具(IPC)里面的管道、system V和共享内存。在理解阶层通信之间&#xff0c;我们先了解用户空间缓冲区和内核空间缓冲区两个概念。 1.1 用户空间缓冲区 存在于用户态的进程用户空间&#…

电池预测 | 第28讲 基于CNN-GRU的锂电池剩余寿命预测

电池预测 | 第28讲 基于CNN-GRU的锂电池剩余寿命预测 目录 电池预测 | 第28讲 基于CNN-GRU的锂电池剩余寿命预测预测效果基本描述程序设计参考资料 预测效果 基本描述 电池预测 | 第28讲 基于CNN-GRU的锂电池剩余寿命预测 运行环境Matlab2023b及以上&#xff0c;锂电池剩余寿…

快速上手SHELL脚本常用命令

一、设置主机名称 1.修改文件方式 重启后生效 2.命令修改 重启shell后生效 二、网卡管理nmcli 1.查看网卡 2.设置网卡 详细配置&#xff1a;快速上手Linux联网管理-CSDN博客 三、简单处理字符 1.打印连续数字 2.设置字体颜色 \033[颜色代号m 3.反向打印文件内容 tac&a…

【自然语言处理与大模型】大模型Agent四大的组件

大模型Agent是基于大型语言模型构建的智能体&#xff0c;它们能够模拟独立思考过程&#xff0c;灵活调用各类工具&#xff0c;逐步达成预设目标。这类智能体的设计旨在通过感知、思考与行动三者的紧密结合来完成复杂任务。下面将从大模型大脑&#xff08;LLM&#xff09;、规划…