当前位置: 首页 > news >正文

【linux基础I/O(1)】文件描述符的本质重定向的本质

目录

  • 前言
  • 1. 理解C语言的文件接口
  • 2. 操作文件的系统调用接口
    • 2.1 open函数详解
    • 2.2 close函数详解
    • 2.3 write函数详解
    • 2.4 read函数详解
  • 3. 文件描述符fd详解
  • 4. 文件描述符的内核本质
  • 5. 怎样理解Linux下一切皆文件?
  • 6. 理解输出输入重定向
  • 7. 重定向的系统调用
  • 8. 总结

前言

“在Linux系统下,一切皆文件”,相信你也听过这句话,,那么怎样理解这句话呢?学会这篇文字,你就能理解了。

本章重点:

本篇文章着重讲解I/O的四个系统调用接口, 以及文件描述符fd的认识与fd的本质, 最后讲解应该怎样理解Linux下一切皆文件这一说法.在此之前,会先复习一下C语言的文件相关的库函数。

1. 理解C语言的文件接口

首先C\C++程序会默认打开stdin,stdout和stderr三个标准文件方便程序员直接进行读写.

但是显然有点不对劲, 我们平时使用printf和scanf时是从显示器中显示和从键盘输入, 是不是代表显示器和键盘在OS内部其实也可以看作文件?是的,向显示器打印和向磁盘写入无本质区别!

C语言打开文件的方式: fopen
C语言的读取: fread, fscanf, fgets
C语言的写入: fwrite, fprintf, fputs

fopen的返回值和三个标准文件类型都是FILE*

2. 操作文件的系统调用接口

每个语言都有一套自己的文件操作函数,但不管上层语言怎样变化,它都是封装了系统调用,所以文件的系统调用很重要!

一共四个函数:
1.open: 打开文件
2.close: 关闭文件
3.write: 向文件写入
4.read: 从文件中读取

2.1 open函数详解

在这里插入图片描述
open函数的解释如下:
在这里插入图片描述

这个flag比较特殊,虽然它是整型,但是内部却当作了位图在使用,即传递过来的选项会被当作位图中的不同位,通过判断某位是否为1来查看是否有这个选项.

open的选项(实际上是宏定义的整数)
在这里插入图片描述
open的用法: 多个选项用或|分割

int fd = open("/home/cc/test.txt",O_WRONLY | O_CREAT);

2.2 close函数详解

在这里插入图片描述
close函数很简单,意思就是关闭文件描述符fd对应的文件, 调用成功返回0.

2.3 write函数详解

在这里插入图片描述
write是向文件描述符fd对应的文件中写入数据, 数据的来源是buf, 要写入的字节数是count, 调用成功返回写入到文件中的字节数.

write的一般用法:

char* buffer = "abcdef";
int fd = open("/home/cc/text.txt",O_WRONLY);
write(fd,buffer,sizeof(buffer));

2.4 read函数详解

在这里插入图片描述
read是从文件描述符fd对应的文件中读取数据, 将数据读取到buf中,要读取的长度是count, 调用成功返回读取到的字节数.

read的一般用法:

char buffer[1024];
int fd = open("/home/cc/text.txt",O_WRONLY);
ssize_t n = read(fd,buffer,sizeof(buffer));
if(n > 0)buffer[n] = '\0';//将字符串变成C语言风格,以\0结尾

3. 文件描述符fd详解

我们知道文件描述符是一个整数,那么它是否有什么规律呢?请看下面的代码:

int fd1 = open("/home/kwy/text1.txt",O_WRONLY | O_CREAT);
int fd2 = open("/home/kwy/text2.txt",O_WRONLY | O_CREAT);
int fd3 = open("/home/kwy/text3.txt",O_WRONLY | O_CREAT);
int fd4 = open("/home/kwy/text4.txt",O_WRONLY | O_CREAT);
printf("%d, %d, %d, %d",fd1,fd2,fd3,fd4);

会发现fa1,2,3,4的整数值分别是:
3,4,5,6,这是为什么?需要回答两个问题:

1. 0号1号和2号描述符去哪儿了?
2. 文件描述符的增长规律是什么?

首先, 在最开始说过C/C++程序会默认打开stdin, stdout, stderr三个标准文件,所以其实0,1,2号文件描述符就是这三个标准文件.

其次, 0,1,2被使用后, 后面的文件描述符会从3开始, 依次+1, 一共创建到6号描述符,若此时将3号文件描述符关闭,下次打开文件对应的描述符就是3,而不是7!

可以使用下面的代码来验证第一个猜想:

//向屏幕打印信息
const char* str = "abcdef";
write(1, str, strlen(str));
//从屏幕读取信息
char buffer[1024];
int n = read(0, buffer, sizeof(buffer));
if(n > 0)buffer[n] = '\0';

4. 文件描述符的内核本质

进程想要访问某个文件的前提是打开文件,在操作系统内可能会有很多个打开的文件,OS为了维护这些资源,需要对它进行管理,会为每个打开的文件创建struct file结构体, 再用链表将这些结构体连接起来.
在这里插入图片描述

这是文件在OS内部的管理体系,而每个进程都要知道自己打开了哪些文件, 所以进程PCB中会保存一张文件描述符表(本质是结构体指针),这个表中存放了这个进程打开的所有文件!
在这里插入图片描述

从这个图中可以看见, 文件描述符的本质其实就是数组的下标,每次打开文件会去数组中扫描,找到最近的没有被使用的下标!

5. 怎样理解Linux下一切皆文件?

首先,底层不同的硬件如磁盘,显卡,键盘等一定对应了不同的操作方法,但这些设备的核心功能就是读写,也就是I/O.

在这里插入图片描述
操作系统会为每一个底层硬件创建struct file结构体,此结构体中一定包含了两个函数指针,分别指向这个硬件对应的读方法和写方法.

所以当我们使用键盘或打开显示器时,就会有对应的指针指向对应的那个方法。
所以当我们使用键盘时,0S就会去找到那个structfile,并且找到里面的方法调用。当我们从 struct file 的角度向上看时,就不用关心底层外设的差异了,操作它们的方法都是:read/write的函数指针,在上层我们看到是所有设备就叫做 一切皆文件!

6. 理解输出输入重定向

根据上面的推论,如果我先把1号描述符关闭了,再打开一个文件,它的描述符就应该是1,此时再进行输出会发现什么?

int main()
{close(1);int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);printf("fd: %d\n",fd);fprintf(stdout,"hello fprintf,我是一号文件描述符\n");return 0;
}

在这里插入图片描述

两个现象,第一个确实如刚刚所说的新打开的文件的描述符就是1,并且此时使用printf输出也不会输出到屏幕使用fprintf向stdout也不会输出到屏幕而是输出到文件log.txt中.说明stdout只认文件描述符1,不管1此时还是不是标准输出,printf函数也是如此.

结论:重定向本质就是在OS内部修改fd对应的内容指向.

在这里插入图片描述

7. 重定向的系统调用

如果每次写重定向都要先关闭一个文件,再来操作未免有些麻烦了,可以直接使用系统调用dup或dup2函数.
在这里插入图片描述

我们一般都使用dup2,它的意思是把原本写入到newfile 文件的内容,重定向到 oldfile 文件中!最终和oldfd描述符是一样的。比如现在想把本来应该打印在显示器(1号描述符)的信息打印在log.txt中(3号描述符),应该这样使用:

open("log.txt",O_WRONLY | O_CREAT);
dup2(3,1);

8. 总结

文件描述符是学习Linux下I/O的关键,而基础IO的知识将会一直陪伴我们到学习Linux网络和高级IO,掌握文件描述符fd的本质对后续的学习至关重要!

相关文章:

【linux基础I/O(1)】文件描述符的本质重定向的本质

目录 前言1. 理解C语言的文件接口2. 操作文件的系统调用接口2.1 open函数详解2.2 close函数详解2.3 write函数详解2.4 read函数详解 3. 文件描述符fd详解4. 文件描述符的内核本质5. 怎样理解Linux下一切皆文件?6. 理解输出输入重定向7. 重定向的系统调用8. 总结 前言 “在Lin…...

微服务架构下的慢请求排查与优化策略

目录 一、分析请求路径 二、检查日志 三、进行时序分析 四、检查资源消耗 五、检查并发处理能力 六、检查网络连接 七、从根本上使用服务治理的方式解决问题 八、结语 在当今的数字化时代,企业为了应对快速变化的市场需求和日益增长的用户基数,纷…...

C++ 中 Unicode 字符串的宽度

首先,什么是 Unicode? Unicode 实际上是一个统一的文字编码标准,它出现目的是为了解决不同计算机之间字符编码不同而导致的灾难性不兼容问题。 Unicode 字符集与 Unicode 编码是两种不同的概念。Unicode 字符集实际是对进入标准的所有文字用…...

人工智能在SEO中的应用与关键词优化策略

内容概要 随着科技的迅猛发展,人工智能在搜索引擎优化(SEO)中的应用逐渐成为业界关注的热点。AI技术不仅可以有效提高关键词的优化策略,还能在提升内容效率、增强用户体验方面发挥重要作用。通过对相关技术的深入探讨&#xff0c…...

spring mvc源码学习笔记之四

pom.xml 内容如下 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/P…...

ruckus R510升级到Unleashe后不能访问

ruckus R510 是IPQ4019&#xff0c;升级到Unleashe&#xff0c;它弹窗提示 但是这个IP没办法用&#xff0c;访问不了AP。 必应了一下&#xff0c;官方提示用advance ip scanner扫描。 扫描持续好久&#xff0c;发现IP竟然是从主路由获得。 9090的端口不用填&#xff0c;甚至不…...

【游戏设计原理】47 - 超游戏思维

对于这条原理&#xff0c;我首先想到的是开放世界&#xff0c;或者探索性游戏&#xff0c;这是最能包容各类玩家的游戏类型。这类游戏定义了基本规则&#xff0c;玩家的可操作性很强。就像上图里的沙池一样&#xff0c;里面有滑梯&#xff0c;是规则性比较明确的&#xff0c;而…...

FastAPI vs Flask 专业对比与选择

FastAPI与Flask是两个流行的Python Web框架&#xff0c;它们在构建Web应用程序和API方面各有特点。以下是对这两个框架的详细比较&#xff1a; 一、设计理念与用途 Flask&#xff1a; 是一个轻量级的Python Web框架&#xff0c;基于Werkzeug WSGI工具箱和Jinja2模板引擎。设计…...

【信息系统项目管理师】【综合知识】【备考知识点】【思维导图】第十一章 项目成本管理

word版☞【信息系统项目管理师】【综合知识】【备考知识点】第十一章 项目成本管理 移动端【思维导图】☞【信息系统项目管理师】【思维导图】第十一章 项目成本管理...

xdoj-字符串-556,为什么字符不能被正常读入

目录 题目 代码 测试用例 the input the correct output 问题发现过程阐述 如果把line16中的数组大小11换成line17中的10 case 1 case 2 case 3 如果数组开成11 case4 代码分析 问题描述 Question1 Question2 题目 题目&#xff1a;连续数字字符串提取 问题描述…...

计算机网络——期末复习(5)期末考试样例1(含答案)

考试题型&#xff1b; 概念辨析&#xff15;个、计算与分析&#xff13;个、综合题&#xff13;&#xff0d;&#xff14;个 必考知识点&#xff1a; 概述&#xff1a;协议 体系结构 物理层&#xff1b;本次考核较少 链路层&#xff1a;CSMA/CD 退避二进制算法 &#xff0…...

Docker安装oracle数据库【最新版】

文章目录 1. 安装 Docker 环境2. 拉取 Oracle 镜像3. 查看镜像4. 创建容器5. 进入容器进行配置6. 进行软连接7. 配置 Oracle 环境变量8. 创建软连接9. 切换到 Oracle 用户10. 登录 SQL*Plus 并修改 sys、system 用户密码11. 重新启动数据库12. 解决 "Database Not Open&qu…...

基于STM32的智能门锁系统设计

目录 引言系统设计 硬件设计软件设计系统功能模块 用户身份验证模块开锁控制模块状态监控与报警模块数据存储与管理模块控制算法 用户身份验证算法开锁控制算法状态监控与报警算法代码实现 用户身份验证模块实现开锁控制模块实现状态监控模块实现系统调试与优化结论与展望 1. …...

【踩坑指南:2025年最新】如何在Linux(Ubuntu)启动第一个Scala Hello World程序(Scala3)

如何正确地写出Scala的第一个程序&#xff0c;并且利用Scala3的简洁特性&#xff1f; 在解释器中直接输出Hello world非常简单&#xff0c;只需要直接执行即可&#xff1a; scala> println("Hello World") Hello World 但如果我们希望编写一个脚本文件&#xf…...

SAP系统中的标准价、移动平均价是什么?有何区别?物料分类账的优点

文章目录 前言一、SAP系统中的价格控制二、移动平均价、标准价是什么&#xff1f;三、S价&#xff08;标准价&#xff09;的优势四、S价&#xff08;标准价&#xff09;的劣势五、V价&#xff08;移动平均价&#xff09;的优势六、V价&#xff08;移动平均价&#xff09;的劣势…...

9.类的定义与使用

类的定义构造函数(__init__)实例变量类变量方法(实例方法)类方法(classmethod)静态方法(staticmethod)属性装饰器(property)私有属性与方法继承多态方法重写super()函数类的文档字符串类的属性和方法访问控制 1.类的定义: 如int,list,tuple等等都是类,还可以通过class方法自己…...

【网络安全 | 漏洞挖掘】JS Review + GraphQL滥用实现管理面板访问

未经许可,不得转载。 正文 在映射目标范围后,我发现了一个用于管理的控制台界面,但没有注册功能。 于是我开始尝试: 1、模糊测试注册端点 -> 失败 2、在请求中将登录替换为注册 -> 再次失败 尝试均未奏效后,我决定冷静下来,重新思考方法并利用技术手段。 我观察…...

前端如何判断多个请求完毕

在前端开发中&#xff0c;经常会遇到需要同时发起多个异步请求&#xff0c;并在所有请求都完成后再进行下一步操作的情况。 这里有几个常用的方法来实现这一需求&#xff1a; 使用 Promise.all() Promise.all() 方法接收一个 Promise 对象的数组作为参数&#xff0c;当所有的…...

atrust异常导致ERR_NETWORK_CHANGED

首先因为工作需要不断安装卸载不同版本深信服的atrust。那么可能遇到和我一样的问题。 深信服的这种东西有点毛病&#xff0c;以前只是偶尔导致我局域网无法访问&#xff0c;我停止atrust后&#xff0c;他还有后台程序在后台不断更改我的适配器&#xff0c;在我局域网需要固定…...

【网络安全 | 漏洞挖掘】绕过电子邮件确认实现预账户接管

未经许可,不得转载。 文章目录 正文漏洞步骤赏金正文 我测试的应用程序有多个子域名: 1、account.example.com:处理用户账户管理。 2、project.example.com:管理用户拥有或被邀请的项目。 3、org.example.com:一个新的子域,用于管理多个项目的组织。 4、collaborator.ex…...

python3GUI--智慧交通监控与管理系统 By:PyQt5

文章目录 一&#xff0e;前言二&#xff0e;预览三&#xff0e;软件组成&技术难点1.软件组成结构2.技术难点3.项目结构 四&#xff0e;总结 大小&#xff1a;35.5 M&#xff0c;软件安装包放在了这里! 一&#xff0e;前言 博主高产&#xff0c;本次给大家带来一款我自己使…...

Java重要面试名词整理(十八):Sentinel

文章目录 服务雪崩及其解决方案技术选型: Sentinel or Hystrix 流量治理组件Sentinel**基于SentinelResource注解埋点实现资源保护** Sentinel控制台**限流阈值类型****流控模式****直接****关联** **流控效果**系统规则——系统自适应保护 限流算法**计数器法****滑动时间窗口…...

一文大白话讲清楚TCP连接的三次握手和断开连接的四次挥手的原理

文章目录 一文大白话讲清楚TCP连接的三次握手和断开连接的四次挥手的原理1.TCP建立连接需要3次握手1.1 先讲个你兄弟的故事1.2 TCP 3次握手1.2 TCP 3次握手8件事1.3 TCP握手能不能是两次 2. TCP 断开连接要4次挥手2.1 还回到你兄弟的故事上2.2 TCP 4次挥手2.2 TCP4次挥手4件事2…...

CSS——1.优缺点

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><link rel"stylesheet" type"text/css" href"1-02.css"/></head><body><!--css&#xff1a;层叠样式表…...

TIM——编码器测速

Encoder Interface 编码器接口 编码器接口可接收增量&#xff08;正交&#xff09;编码器的信号&#xff0c;根据编码器旋转产生的正交信号脉冲&#xff0c;自动控制CNT自增或自减&#xff0c;从而指示编码器的位置、旋转方向和旋转速度 每个高级定时器和通用定时器都拥有1个编…...

抢先体验:人大金仓数据库管理系统KingbaseES V9 最新版本 CentOS 7.9 部署体验

一、简介 KingbaseES 是中国人大金仓信息技术股份有限公司自主研发的一款通用关系型数据库管理系统&#xff08;RDBMS&#xff09;。 作为国产数据库的杰出代表&#xff0c;它专为中国市场设计&#xff0c;广泛应用于政府、金融、能源、电信等关键行业&#xff0c;以高安全性…...

供应链系统设计-供应链中台系统设计(七)- 商品中心设计篇

概述 上篇文章我们大致讲了一些商品中心相关的概念&#xff0c;例如&#xff1a;SPU、SKU、Item等等&#xff0c;在这里我们来简单的回顾一下&#xff1a; 商品概念的分层与定义&#xff1a; SPU&#xff08;Standard Product Unit&#xff09;&#xff1a;代表产品系列或产品…...

Power BI如何连接Azure Databricks数据源?

故事背景: 近期有朋友询问&#xff0c;自己公司有一些项目使用了Azure Databricks用于数据存储。如何使用Power BI Desktop桌面开发软件连接Azure Databricks的数据源呢&#xff1f; 解决方案: 其实Power BI是提供了连接Azure Databricks数据源的选项的&#xff0c;只是配置…...

【HarmonyOS】鸿蒙应用如何进行页面横竖屏切换以及注意事项,自动切换横竖屏,监听横竖屏

【HarmonyOS】鸿蒙应用如何进行页面横竖屏切换以及注意事项&#xff0c;自动切换横竖屏&#xff0c;监听横竖屏 一、鸿蒙应用如何进行页面横竖屏调用API手动切换 1.首先要在EntryAbility 中获取主窗口对象 EntryAbility.ets import { AbilityConstant, UIAbility, Want } fr…...

编译 C++ 程序:分离与保留调试信息以支持 GDB 对 Core 文件的调试

在 C 程序开发过程中&#xff0c;调试是一个非常重要的环节。当程序出现问题&#xff0c;尤其是在生产环境中出现崩溃并生成 Core 文件时&#xff0c;我们需要使用调试工具&#xff08;如 GDB&#xff09;对程序进行深入分析&#xff0c;找出问题的根源。为了在需要时能够有效地…...