文件描述符fd

news2024/10/5 21:23:19

目录

文件描述符fd

操作系统是文件的管理者,所有语言上的对“文件”的操作,都必须贯穿OS,又因为操作系统不相信任何人,访问操作系统需要通过系统调用接口,所以几乎所有的语言fopen,fclose,fwrite,fgets,fputs,fgetc,fputc等底层一定需要使用OS提供的系统调用,为了能够更深入的了解文件操作,就需要去学习文件的系统调用接口。

系统文件IO接口介绍

open

在这里插入图片描述

int open(char* pathname,int flags,mode_t mode);
//flags-->打开方式,通过传bit位的方式,传多组标记
//O_RDONLY: 只读打开
//O_WRONLY: 只写打开
//O_RDWR : 读,写打开
//这三个常量,必须指定一个且只能指定一个
//O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
//O_APPEND: 追加写
//mode-->最常用的是权限
//返回的是一个文件描述符

  • flags的运行原理可以理解为flags是一个设定了的值,通过给定的O_WRONLY,O_RDONLY…来与flags按位与

    if(O_WRONLY & flags)
    {
        //....
    }
    //O_WRONLY可以理解为
    #define O_WRONLY 0x1 0000 0001
    //O_RDONLY可以理解为
    #define O_RDONLY 0x2 0000 0010
    //O_CREAT可以理解为
    #define O_CREAT 0x4  0000 0100
    //所以如果需要传多组标志就可以运用或,也就是上图的O_WRONLY | O_CREAT
    

close

用法其实就是关闭文件,里面的参数是open的返回值,也就文件描述符fd

write

open的函数返回值

在认识返回值之前,我们先来了解一下系统调用和库函数

  • 像fopen,fclose,fread,fwrite都是C标准库中的函数,也就是库函数(libc)
  • 上面的open,close,write都属于系统接口
  • 从这个图中看系统调用接口和库函数的关系就清晰了,可以认为f#系列的库函数,都是对系统调用的封装,方便二次开发
    文件描述符fd通过对open的学习,可知文件描述符就是一个整数

0 & 1 & 2

Linux进程默认情况下,OS会帮助我们进程打开三个标准输入输出!

0:标准输入,键盘

1:标准输入,显示器

2:标准错误,显示器

所以输入输出还有这种方式:

在这里插入图片描述

#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
  const char str[]="hello word\n";
  write(1,str,strlen(str));
  write(2,str,strlen(str));
}


现在就知道了,文件描述符就是从0开始的小整数,当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,这就有了file结构体。进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针,所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

struct file{
 //包含了打开文件的相关信息
 //链接属性
	file* fd_array[];
}

文件描述符的分配规则

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	int fd = open("myfile", O_RDONLY);
	if(fd < 0)
    {
		perror("open");
		return 1;
	}
	printf("fd: %d\n", fd);
	close(fd);
	return 0;
}
//结果fd:3
//关闭0,或者2
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	close(0);
	//close(2);
	int fd = open("myfile", O_RDONLY);
	if(fd < 0)
    {
		perror("open");
		return 1;
	}
	printf("fd: %d\n", fd);
	close(fd);
	return 0;
}
//结果fd:0,fd:2

由此可见文件描述符分配规则:files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新文件描述符

重定向


接下来就开始完善我们的另外一个代码

int main()
{
  //实现重定向
  //我们知道了文件描述符分配规则
  close(1);//关闭标准输出
  int fd = open("./myfile.txt",O_WRONLY | O_CREAT,0644);
  if(fd<0)
    return -1;
  printf("hello word\n");
  fprintf(stdout,"hello stdout\n");
  fprintf(stderr,"hello stderr\n");
}

printf是C语言库当中的IO函数,一般往 stdout 中输出,但是stdout底层访问文件的时候,找的还是fd:1, 但此时,fd:1下标所表示内容,已经变成了myfile的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向

还有一个细节,这里并没有在最后close(fd),否则就是另外一个现象了,这个现象后面会说,解决方法也很简单在关闭fd之前先刷新一下缓冲区就好了在这里插入图片描述
到这里重定向的本质到底是什么呢?


就是将本应该写到标准输出里面的东西写到标准输入中去

  • 小知识:2&>1 就是将标准错误重定向到标准输出在这里插入图片描述

dup2系统调用

//头文件
#include<unistd.h>
//我们一般使用dup2比较的多
int dup2(int oldfd,int newfd);
//可以思考一下含义
//就是让newfd成为oldfd的副本
//以下是个例子
----------------------------------------------------
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
 // 系统调用还提供了dup*类型的函数,这里我们主要了解dup2
 // int dup2(int oldfd,int newfd);
  int fd = open("./myfile.txt",O_WRONLY | O_CREAT,0644);
  if(fd<0)
  {
    perror("open:");
    return -1;
  }
  dup2(fd,1);
  printf("hello word\n");
  fprintf(stdout,"hello stdout\n");
  fprintf(stderr,"hello stderr\n");
  fflush(stdout);//如果不加上刷新缓冲区会导致myfile.txt没有数据
  close(fd);
}

注:

  • 如果newfd被打开了,首先会关闭newfd,如果oldfd不是有效的文件描述符则调用失败,并且newfd没有关闭
  • 如果oldfd是有效的文件描述符并且newfd的值与oldfd相同dup2()什么都不做并返回newfd
  • dup2所复制的文件描述符与原来的文件描述符共享各种文件状态。共享所有的锁定,读写位置和各项权限或flags等.

FILE

  • 因为IO相关的函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问
  • 所以C库当中的FILE结构体内部也一定封装了fd

在这里解释一下之前为什么不加fflush直接close文件里面没有数据

  • 首先让我们先了解:用户—>OS的刷新策略

    1. 立即刷新(不缓冲)
    2. 行刷新(行缓冲也就是遇到\n刷新)—>显示器打印就是行刷新
    3. 缓冲区满了才会刷新(全缓冲),比如往磁盘文件中写入
  • 刷新策略OS—>硬件同样适用

  • 看完上面是不是就清晰了许多,为什么没有fflush就close掉fd文件里面没有数据呢?----->首先因为我们进行了重定向,本质上就是将行刷新策略改为了全刷新策略,我们写的hello printf不会被立刻刷新到内核缓冲区,还存在c缓冲区中,其次就是我们在没有刷新缓冲区的情况下把文件给关闭了,这就导致没法刷新到内核缓冲区中,所以会出现文件没有数据的情况;加上fflush之所以可以是因为fflush是强制刷新缓冲区。

  • 像write这样的系统调用是直接写到内核缓冲区当中的

还有这么一个现象

#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
  //系统调用接口
  const char* str1 = "hello stderr\n";
  write(2,str1,strlen(str1));

  const char* str2 = "hello stdout\n";
  write(1,str2,strlen(str2));

  printf("hello printf\n");
  fprintf(stdout,"hello fprintf\n");

  fork();
 // close(1);
}
//如果将其重定向到myfile.txt文件中时会出现这种现象

  • 原因:首先当往显示器上面打印的时候刷新策略是行刷新,所以fork()没有影响,但是当重定向的时候,刷新策略变成了全缓冲,这个缓冲区是c缓冲区,同时也是父进程的缓冲区,父子进程共享代码和数据,当父进程结束或者子进程结束就会刷新缓冲区,谁先刷新缓冲区,谁就发生了写时拷贝,也就是为什么数据出现了两次;write是系统调用不经过用户级缓冲区,(同时也能说明之前的printf和fprintf的缓冲区一定不在OS内部),所以不会重复打印
  • 解决方法就是在fork()之前,使用fflush(stdout)强制刷新即可,立即刷新完之后缓冲区里面就不会存在数据了,这样也就不发生写时拷贝

stdout,cin/cout,iostream,fstream:类会包含缓冲区,这也就是为什么会有std::endl;(作用也就是刷新c++缓冲区中的数据到显示器当中)

总结:

  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲
  • printf,fprintf 库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲,有时甚至在fork后面刷新
  • 而我们放在缓冲区中的数据,就不会被立即刷新但是进程退出之后,会统一刷新,写入文件当中。
  • 但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。
  • write 没有变化,说明没有所谓的缓冲

printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区。那这个用户级缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供

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

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

相关文章

A_A07_003 AS608指纹模块上位机软件使用

A_A07_003 AS608指纹模块上位机软件使用一、软件获取二、测试用模块与接线三、上位机界面分块和功能1、指纹图像显示区域2、硬件信息3、打开设备4、设备配置5、信息显示6、图像管理7、指纹处理8、辅助功能四、注意事项一、软件获取 网盘链接 直戳跳转 二、测试用模块与接线 …

【Kubernetes】【五】资源管理和YML

第三章 资源管理 本章节主要介绍yaml语法和kubernetes的资源管理方式 资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通过操作资源来管理kubernetes。 ​ kubernetes的本质上就是一个集群系统&#xff0c;用户可以在集群中部署各种服务…

软件测试面试刷题app包含了各种难题

软件测试的生命周期&#xff1a; V模型&#xff1a;与软件开发阶段呼应 软件开发&#xff1a;需求分析-->概要设计-->详细设计-->编码阶段软件测试&#xff1a;单元测试-->集成测试-->系统测试-->验收测试从基本流程的角度讲&#xff1a; 需求阶段&#xff…

Java基础:接口

1.接口的概念 当不是所有子类, 而是多个子类都包含一个方法时, 为了代码书写规范性, 可以用自定义的接口来统一该方法的书写规范. 所以接口可以看作是一种书写规则. 接口是对行为的抽象 抽象类一般是书写在父类当中, 接口是单独书写的, 不是一种类 2.接口的定义和使用 3.接口…

MATLAB/Simulink 通信原理及仿真学习(一)

文章目录MATLAB/Simulink 通信原理及仿真学习&#xff08;一&#xff09;基本操作 (23.2.16)MATLAB 变量矩阵运算画图工具函数函数文件操作MATLAB/Simulink 通信原理及仿真学习&#xff08;一&#xff09; 基本操作 (23.2.16) MATLAB 变量 变量以字母开头&#xff0c;后接字…

Ubuntu16.04搭建Fabric1.4环境

一、换源 为了提高下载速度&#xff0c;将ubuntu的源改成国内的源&#xff08;推荐阿里云源和清华源&#xff09; apt源保存在 /etc/apt/sources.list / 代表根目录 /etc 这个文件夹几乎放置了系统的所有配置文件 1.备份 sudo cp /etc/apt/sources.list sources_backup.l…

媒体邀约之企业如何加强品牌的宣传力度

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。胡老师分享了许多媒体传播方面的经验&#xff0c;今天就跟大家分享下我对企业宣传方面的看法。企业如何加强品牌的宣传力度&#xff1a;1&#xff0c;网络宣传在社交媒体上建立企业账户&am…

Jmeter常用断言之响应断言详解

响应断言是最常用的一种断言方法&#xff0c;主要是对响应结果中的文本内容进行断言&#xff0c;比如响应结果是否包含指定的值&#xff0c;或者是否等于指定的值。响应断言可以适用各种返回类型的响应结果&#xff0c;如&#xff1a;Test、html、application/json、applicatio…

安装 cplex 求解器

安装 cplex 求解器 安装 cplex 求解器和python-docplexcplex 安装matlab 用户安装 cplexpython 版本安装 cplex 求解器和python-docplex cplex 安装 cplex 是解决优化问题的一个工具箱&#xff0c;用来线性规划、混合整数规划和二次规划的高性能数学规划求解器。可以理解成&a…

《爆肝整理》保姆级系列教程python接口自动化(十六)--参数关联接口后传(详解)

简介 大家对前边的自动化新建任务之后&#xff0c;接着对这个新建任务操作了解之后&#xff0c;希望带小伙伴进一步巩固胜利的果实&#xff0c;夯实基础。因此再在沙场实例演练一下相关接口。我们用自动化发随笔之后&#xff0c;要想接着对这篇随笔操作&#xff0c;不用说就需 …

实现聊天消息绘制、图文混排(源码,支持Windows、Linux)

在实现聊天软件时&#xff0c;渲染文字表情图文混排是一项非常繁琐的工作&#xff0c;再加上还要支持GIF动图、引用消息、撤回消息、名片等不同样式的消息渲染时&#xff0c;就更加麻烦了。 好在我们可以使用 ESFramework 提供的 IChatRender 组件&#xff0c;使用它我们就能轻…

小众免费的短视频素材库

推荐5个小众但好用的视频素材网站&#xff0c;免费可商用&#xff0c;视频剪辑、自媒体必备~ 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYxMjky ​ 菜鸟图库网素材非常丰富&#xff0c;网站主要还是以设计素材为主&#xff0c;高清视频素材也很多&#xff0c;像风…

(考研湖科大教书匠计算机网络)第四章网络层-第六节3:开放最短路径优先OSPF的基本工作原理

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;OSPF概述&#xff08;1&#xff09;概述&#xff08;2&#xff09;细节阐述A&#xff1a;链路状态和代价B&#xff1a;问候分组和邻居表C&#xff…

如何保证集合是线程安全的 ConcurrentHashMap如何实现高效地线程安全?

第10讲 | 如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全&#xff1f; 我在之前两讲介绍了 Java 集合框架的典型容器类&#xff0c;它们绝大部分都不是线程安全的&#xff0c;仅有的线程安全实现&#xff0c;比如 Vector、Stack&#xff0c;在性能方面也…

智能家居项目(一)之基础概念引入

目录 一、智能家居功能整体流程 二、设计模式 三、C语言中结构体的新玩法&#xff0c;承接上一步 四、工厂模式概念引入 一、智能家居功能整体流程 1.控制区语音识别模块socket客户端 2.外设区继电器组控制灯&#xff0c;远程终端子系统&#xff0c;窗帘等&#xff0c;火灾…

数据分析面试---假设检验知识点

文章目录一、假设检验是干啥的二、假设检验基本原理/思想三、假设检验步骤显著性检验(significance test)统计显著性和置信度补充&#xff1a;第一类错误和第二类错误p值是啥链接1数理统计中的统计推断问题主要有两大类&#xff1a;参数估计问题和假设检验问题参数估计问题 所指…

[datawhale202302]CS224W图机器学习:图的基本表示及特征工程

结论速递 本章涉及了图基本表示及传统的特征工程。 图由节点和连接组成&#xff0c;节点和连接上都可以有不同的属性。根据属性的特点&#xff0c;分为几类不同的图&#xff0c;其中异质图和二分图是比较重要的特殊图。 图可以用邻接矩阵进行结构化表示&#xff0c;如果图过于…

单元测试的优势

单元测试提供了许多好处&#xff0c;包括及早发现软件错误、促进变化、简化集成、提供文档来源以及许多其他优点&#xff0c;接下来将对其进行详细介绍。 1、使流程更灵活 单元测试的主要好处之一是它使编码过程更加灵活&#xff0c;更遵循敏捷开发方法论。 当向软件中添加越来…

如何加入new bing候补名单

如何加入new bing候补名单 我们都知道现在最新版edges中已经提示我们可以加入new bing候补名单&#xff0c;但国内环境下无法正常加入new bing候补名单&#xff0c;这篇文章讲告诉你如何绕过限制加入new bing候补名单 下载配置 HeaderEditor 插件 下载地址microsoftedge.mic…

XDSpy APT组织近期针对俄罗斯国防部的攻击活动分析

一 概述 XDSpy是ESET于2020年首次披露的APT组织&#xff0c;该组织最早活跃于2011年&#xff0c;主要针对东欧和塞尔维亚地区的政府、军队、外交部及私人公司进行窃密活动。2020年9月&#xff0c;该组织在攻击活动中使用Covid-19主题诱饵下发恶意Windows脚本文件&#xff08;W…