【Linux】系统文件IO·文件描述符fd

前言

 C语言文件接口

 C 语言读写文件

1.C语言写入文件

2.C语言读取文件

stdin/stdout/stderr

系统文件IO

文件描述符fd:

文件描述符分配规则:

文件描述符fd:

前言

我们早在C语言中学习关于如何用代码来管理文件,比如文件的输入和文件的输出,一些文件的接口,如何深入学习文件的知识,在Linux下一切皆文件,今天我们探讨Linux的基础I/O。

1>文件=内容+属性

2>访问文件之前,都需要先打开文件,并且对文件的修改都是通过执行代码的方式完成的,文件必须加载到内存中

文件被访问被修改必须得在内存中完成,因为CPU只能访问内存,所以文件必须加载到内存中。 打开文件->文件被加载到内存中

3>文件由谁打开? ->  由进程打开文件

4> 一个进程可以打开多个文件,由操作系统管理多个被打开的文件,那么这些文件是怎样被管理的:  先描述,再组织,内核中一定要有描述被打开文件的结构体,使用其定义对象

5>系统中不是所有的文件都被进程打开了, 没有被打开的文件就在磁盘中

 C语言文件接口

 C 语言读写文件

🗡文件操作:

    首先要打开文件:打开成功,返回文件指针;打开失败,返回NULL
    最后要关闭文件

🗡代码操作:

    FILE *fopen(const char *path, const char *mode);
    int fclose(FILE *fp);

fwritesize_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream )
fcloseint fclose ( FILE * stream );

1.C语言写入文件

int fputs(const char *s, FILE *stream); 
int fprintf(FILE *stream, const char *format, ...);

1. 如下,我们以"w"的方式打开文件,以"w"方式打开文件会先清空文件的内容然后再向文件写入内容。

#include <stdio.h>int main()
{FILE *fp=fopen("./log.txt","w");if(fp==NULL){perror("fopen");return 1;}fclose(fp);    return 0;
}

 

2. 我们使用"a"方式(append) 打开文件  "a"方式 是向文件的末尾追加内容

#include <stdio.h>
#include <unistd.h>
#include <string.h>int main()
{FILE *fp = fopen("log.txt","a");if(fp == NULL){perror("fopen");return 1;}const char* s = "hello linux\n";fwrite(s,strlen(s),1,fp);return 0;	
}

2.C语言读取文件

char *fgets(char *s, int size, FILE *stream); 
int fscanf(FILE *stream, const char *format, ...);
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{FILE *fp = fopen("./log.txt","r");if(fp == NULL){perror("fopen");return 1;}char buffer[64];while(fgets(buffer,sizeof(buffer),fp)){printf("%s",buffer);}return 0;	
}

stdin/stdout/stderr

C默认会打开三个输入输出流,分别是

stdin       标准输入  键盘设备

stdout     标准输出  显示器设备

stderr      标准错误  显示器设备

仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针

 stdin、stdout、stderr 都可以直接使用,例如:

系统文件IO

上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。而, open close read write lseek 都属于系统提供的接口,称之为系统调用接口

访问文件不仅仅要有C语言上的文件接口,OS必须提供对应的访问文件的系统调用

man open查看

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

 参数:

pathname: 要打开或创建的目标文件

flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。参数:

O_RDONLY: 只读打开O_WRONLY: 只写打开O_RDWR : 读,写打开

这三个常量,必须指定一个且只能指定一个

O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限O_APPEND: 追加写

 返回值:

返回值:

成功:新打开的文件描述符

失败:-1

来看下面的例子

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{int fd = open("./log.txt",O_WRONLY | O_CREAT);if(fd < 0){printf("open error\n");// return 1;}close(fd);return 0;	
}

 

此时我们可以观察到 创建出来的文件的权限是乱的

这是因为,没有这个文件,要创建它,系统层面就必须指定权限是多少!我们采用权限设置的八进制方案

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{int fd = open("./log.txt",O_WRONLY | O_CREAT,0644);if(fd < 0){printf("open error\n");return 1;}close(fd);return 0;	
}

 此时的权限就正常了。

其中我们发现,我们传入的flag为 O_WRONLY|O_CREAT,中间为什么要用|连接起来呢:

这是一种用户层给内核传递标志位的常用做法。int有32个bit位,一个bit代表一个标志,就可以传递多个标志位且位运算效率较高。这些O_RDONLY、O_WRONLY、O_RDWR 都是只有一个比特位是1的数据,并且相互不重复,这样 |在一起,就能传递多个标志位。

看看下面这个例子

#include <stdio.h>
#include <unistd.h>
#include <string.h>#define ONE 1
#define TWO (1<<1)
#define THREE (1<<2)
#define FOUR (1<<3)
#define FIVE (1<<4)void Print(int flag)
{if(flag & ONE ) printf("1\n");if(flag & TWO) printf("2\n");if(flag & THREE) printf("3\n");if(flag & FOUR) printf("4\n");if(flag & FIVE) printf("5\n");
}int main()
{Print(ONE);printf("------------\n");Print(TWO);printf("------------\n");Print(ONE|TWO);printf("------------\n");Print(THREE|FOUR|FIVE);printf("------------\n");Print(ONE|TWO|THREE|FOUR|FIVE);printf("------------\n");return 0;
}

 

文件描述符fd:

open函数的返回值是就是文件描述符,类型为int,下面我们来看看fd的值

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int fd1 = open("./log1.txt",O_WRONLY | O_CREAT, 0644);int fd2 = open("./log2.txt",O_WRONLY | O_CREAT, 0644);int fd3 = open("./log3.txt",O_WRONLY | O_CREAT, 0644);int fd4 = open("./log4.txt",O_WRONLY | O_CREAT, 0644);printf("fd:%d\n",fd1);printf("fd:%d\n",fd2);printf("fd:%d\n",fd3);printf("fd:%d\n",fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;	
}

我们可以看到fd的值是从3开始的,一次打印出了3、4、5、6 那么前面的0,1,2去了哪里?

这时候我们想到了stdin,stdout,strerr ,当我们的程序运行起来变成进程,默认情况下,OS会帮助我们打开三个标准输入输出,012其实分别对应的就是标准输入stdin、标准输出stdout、标准错误stderr。对应硬件设备也是键盘、显示器、显示器。

0代表标准输入

1代表标准输出

2代表标准错误

#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

文件描述符分配规则:

这样文件描述符被分配为01234678… 这样从0开始,连续的整数。 并且会优先分配 最小的,未被使用过的。每次给新文件分配的fd,是从fd_array[]中找一个最小的、未被使用的作为新的fd。

    所有的文件操作都是进程执行对应的函数,即本质上是进程对文件的操作。

    如果一个文件没有被打开,这个文件是在磁盘上。如果我创建一个空文件,该文件也是要占用磁盘空间的,因为文件的属性早就存在了(包括名称、时间、类型、大小、权限、用户名所属组等等),属性也是数据,所谓“空文件”是指文件内容为空。

即磁盘文件 = 文件内容 + 文件属性。事实上,我们之前所学的所有文件操作都可以分为两类:对文件内容的操作 + 对文件属性的操作(fseek、ftell、rewind、chmod、chgrp等等).

    要操作文件,必须打开文件(C语言fopen、C++打开流、系统上open),本质上,就是文件相关的属性信息从磁盘加载到内存。

操作系统中存在大量进程,进程可以打开多个文件,即进程 : 文件 = 1 : n ,系统中可能存在着更多的打开的文件(暂时不考虑一个文件被多个进程打开的特殊情况)。那么,OS要不要把打开的文件在内存中(系统中)管理起来呢?那么就要上管理的六字真言:先描述,再组织!

    打开的这么多文件,怎么知道哪些是我们进程的呢?操作系统为了让进程和文件之间产生关联,进程在内核创建struct files_struct 的结构,这个结构包含了一个数组 struct file* fd_array[] ,也就是一个指针数组,把表述文件的结构体地址填入到特定下标中。

文件描述符fd:

此时我们cat命令查看log.txt文件,内容为空

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{close(1);int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);printf("fd:%d\n",fd);return 0;
}

我们这段代码旨在打开log.txt文件,并且向显示器上打印fd的值。

但是我们执行程序之后,显示器上并没有出现我们期望的fd的值

反而我们cat 一下log.txt,发现fd的值竟然打印在了log.txt文件中

 首先分析一下fd的值, 我们关闭了"1" 此时1 就是最小的且未被使用的,所以此时open的返回值是1;

对于本应打印在显示器上的值打印在文件中这件事情,printf底层封装了一些write,stdout等

此时传给printf的fd为1,那么将 文件描述符1 传递给进程后,进程就开始向log.txt文件中打印信息

同时我们也知道了 printf底层是在向标准输出(stdout)打印

int fprintf(FILE *stream, const char *format, ...);
stdout -> FIEL{fileno = 1} -> log.txt// stdout只认识1,只对1输入输出

extern  :  dup2

#include <unistd.h>
int dup2(int oldfd, int newfd); //oldfd->newfd
dup2() makes newfd be the copy of oldfd, closing newfd first if necessary, but note the following:
*  If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
*  If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.

拷贝的是fd对应内容,最终相当于全部变成old

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

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

相关文章

《Fundamentals of Power Electronics》——绕组导体中的涡流

绕组导体中的涡流也会导致功率损耗。这可能导致铜耗大大超过上述公式预测的值。特殊的导体涡流机制被称为集肤效应和紧邻效应。这些机制在多层绕组的大电流导体中最为明显&#xff0c;特别是在高频变换器中。 下图说明了一个简单变压器绕组中的邻近效应。

Android C++系列:C++最佳实践3继承与访问控制

1. 背景 Java中有四种访问控制:public、protected、default、private,它们的使用范围可以用下面一张表概括: 类内部本包子类外部包public是是是是protected是是是否default是是否否private是否否否整个结构还是比较简单的,从类内部到本包到子类到外部包权限越来越小,比较…

【LC刷题】DAY13:110 257 440

【LC刷题】DAY13&#xff1a;110 257 440 文章目录 【LC刷题】DAY13&#xff1a;110 257 440110. 平衡二叉树 [link](https://leetcode.cn/problems/balanced-binary-tree/description/)257. 二叉树的所有路径 [link](https://leetcode.cn/problems/binary-tree-paths/descript…

微软TTS最新模型,发布9种更真实的AI语音

很高兴与大家分享 Azure AI 语音翻译产品套件的两个重大更新&#xff1a; 视频翻译和增强的实时语音翻译 API。 视频翻译&#xff08;批量&#xff09; 今天&#xff0c;我们宣布推出视频翻译预览版&#xff0c;这是一项突破性的服务&#xff0c;旨在改变企业本地化视频内容…

二分查找(算法篇)

算法之二分查找 二分查找 概念&#xff1a; 针对于已经预先排序好的数据&#xff0c;每次将数据进行对半查找&#xff0c;然后看它中间的数据是否是要找的&#xff0c;如果是就返回中间位置&#xff0c;不是就判断该数据是在前半部分还是后半部&#xff0c;然后在进而取其中…

详解 ClickHouse 的建表优化

一、explain 查看执行计划 explain 功能是从 20.6 版本才成为正式的功能&#xff0c;之前的版本需要到 log 日志中查看执行过程 1. 基本语法 explain [plan | ast | syntax | pipeline] [setting1value1, setting2value2,...] select ... [format ...];plan&#xff1a;默认查…

记录一些可用的AI工具网站

记录一些可用的AI工具网站 AI对话大模型AI图片生成AI乐曲生成AI视频生成AI音频分离 AI对话大模型 当前时代巅峰&#xff0c;Microsoft Copilot&#xff1a;https://copilot.microsoft.com AI图片生成 stable diffusion模型资源分享社区&#xff0c;civitai&#xff1a;https…

更改ip后还被封是ip质量的原因吗?

不同的代理IP的质量相同&#xff0c;一般来说可以根据以下几个因素来进行判断&#xff1a; 1.可用率 可用率就是提取的这些代理IP中可以正常使用的比率。假如我们无法使用某个代理IP请求目标网站或者请求超时&#xff0c;那么就代表这个代理不可用&#xff0c;一般来说免费代…

mysql学习——SQL中的DQL和DCL

SQL中的DQL和DCL DQL基本查询条件查询聚合函数分组查询排序查询分页查询 DCL管理用户权限控制 学习黑马MySQL课程&#xff0c;记录笔记&#xff0c;用于复习。 DQL DQL英文全称是Data Query Language(数据查询语言)&#xff0c;数据查询语言&#xff0c;用来查询数据库中表的记…

SpringSecurity-重写默认配置

重写UserDetailService组件 1.注入Bean的方式 /*** author: coffee* date: 2024/6/22 21:22* description: 重写springsecurity默认组件&#xff1a;注入Bean的方式*/Configuration public class ProjectConfig {/*** 重写userDetailsService组件*/Beanpublic UserDetailsSer…

【LC刷题】DAY12:226 144 94 145

【LC刷题】DAY12&#xff1a;226 144 94 145 文章目录 【LC刷题】DAY12&#xff1a;226 144 94 145226. 翻转二叉树 [link](https://leetcode.cn/problems/invert-binary-tree/)101. 对称二叉树 [link](https://leetcode.cn/problems/invert-binary-tree/description/)104. 二叉…

逆向学习数据库篇:多表查询技术详解

本节课在线学习视频&#xff08;网盘地址&#xff0c;保存后即可免费观看&#xff09;&#xff1a; ​​https://pan.quark.cn/s/081e020c1f29​​ 在数据库管理中&#xff0c;多表查询是一种常见的操作&#xff0c;它允许我们从多个相关联的表中检索数据。这种查询通常涉及使…

Flowable更改默认数据库H2到Mysql数据库

Flowable更改默认数据库H2到Mysql数据库 1、下载flowable安装包&#xff0c;从官方下载&#xff0c;下载后解压缩 2、将flowable-ui.war包拷贝到tomcat里面的webapps目录&#xff0c;tomcat的安装在此就不熬术了。 3、此时启动tomcat&#xff0c;flowable-ui会使用默认的H2…

碳+绿证如何能源匹配?考虑碳交易和绿证交易制度的电力批发市场能源优化程序代码!

前言 近年来&#xff0c;面对日益受到全社会关注的气候变化问题&#xff0c;国外尤其是欧美等发达国家和地区针对电力行业制定了一系列碳减排组合机制。其中&#xff0c;碳排放权交易&#xff08;以下简称“碳交易”&#xff09;和绿色电力证书交易&#xff08;以下简称“绿证…

【Docker】Docker简介_运行原理

1、简介 1.1基本概念 容器&#xff1a;容器是Docker的基本部署单元。它是一个轻量级的、独立的运行时环境&#xff0c;包含应用程序及其相关依赖。容器利用Linux内核的命名空间和控制组技术&#xff0c;实现了隔离性和资源管理&#xff0c;使得应用程序在不同的容器中运行不会…

ITREX大语言模型量化-优化工具

一、定义 定义demo 案例 二、实现 定义 ITREX 是Intel 提出的量化加速工具&#xff0c;https://github.com/intel/intel-extension-for-transformers 本实验基于英特尔大模型优化加速技术对大模型预测进行加速&#xff0c;常见的优化优化技术包括&#xff1a;量化&#xff08…

C++并发之协程实例(三)(co_await)

目录 1 协程2 实例3 运行 1 协程 协程(Coroutines)是一个可以挂起执行以便稍后恢复的函数。协程是无堆栈的&#xff1a;它们通过返回到调用方来暂停执行&#xff0c;并且恢复执行所需的数据与堆栈分开存储。这允许异步执行的顺序代码&#xff08;例如&#xff0c;在没有显式回调…

前端面试题日常练-day81 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末 在Sass中&#xff0c;以下哪个功能用于创建一个颜色列表&#xff1f; a) extend b) for c) import d) color Sass中的父选择器&#xff08;Parent Selector&#xff09;是通过以下哪个符号表示的&…

【Oracle】实验一 安装和使用Oracle数据库

【实验目的】 掌握Oracle软件安装过程&#xff0c;选择安装组件掌握建立Oracle数据库&#xff0c;配置网络连接使用SQL*Plus&#xff0c;登录到实例和数据库掌握命令方式的关闭和启动实例及数据库 【实验内容】 安装Oracle19c&#xff0c;记录安装过程。切记&#xff1a;创建…

自然语言处理学习路线(1)——NLP的基本流程

NLP基本流程 【NLP基本流程】 0. 获取语料 ——> 1. 语料预处理 ——> 2. 特征工程&选择 ——> 3. 模型训练 ——> 4. 模型输出&上线 【NLP基本流程图】 Reference 1. 自然语言处理(NLP)的一般处理流程&#xff01;-腾讯云开发者社区-腾讯云 2. …