【Linux】:文件fd

news2024/11/15 12:06:53

朋友们、伙计们,我们又见面了,本期来给大家带来关于文件fd的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

​ 

目录

1. 文件基础介绍

1.1 文件 = 内容 + 属性 

1.2 文件操作

1.3 文件数据

1.4 访问文件

1.5 描述文件 

2. 语言级文件接口

3. 系统级文件接口

3.1 open返回值

3.2 如何理解一切皆文件 

3.3 文件数据读写本质 

3.4 文件fd的分配规则


1. 文件基础介绍

当我们建立一个文件时,此时的文件是一个空文件,那么它要不要占据空间呢?

1.1 文件 = 内容 + 属性 

在我们的计算机内部存在许许多多的文件,这些文件默认都是在磁盘中保存的,那么在磁盘中保存的不仅仅是文件的内容,还有一些描述文件的属性,所以文件通常由两部分构成:

① 文件内容 ② 文件属性

因此我们建立的空文件也是需要占据内存空间的,占据的空间用于存储文件的属性

1.2 文件操作

文件 = 内容 + 属性,因此对文件进行操作的方式有两种:

  • ① 对文件内容操作
  • ② 对文件属性操作

1.3 文件数据

文件是由两部分构成:内容 + 属性,所以一个文件来说,文件的内容和属性都属于文件的数据,所以要能完整的存储一个文件必须既存储该文件的内容又存储文件的属性,它默认是存储在磁盘中的。

1.4 访问文件

我们要访问一个文件,通常是使用代码进行访问,所以要能访问到文件的数据,首先得打开这个文件,所以我们的代码必须运行起来,换句话说我们访问文件其实就是进程访问文件。由于文件在磁盘中存储,所以打开文件之后就会将文件从磁盘加载到内存中,加载磁盘上的文件,就一定会涉及到访问磁盘设备,这个过程由OS来做。

1.5 描述文件 

一个进程很有可能打开不止一个文件,有可能打开多个文件,又或者说多个进程打开多个文件,将这些文件打开就是加载到内存,在操作系统运行时,可能会打开很多的文件,因此为了防止打开的文件混乱,操作系统也是要将这些打开的文件管理起来的,如何管理呢?先描述、再组织!

一个文件要被打开,一定要先在内核中形成被打开的文件对象:

所以文件按照是否被打开可以分为两类:被打开文件、未被打开文件;打开的文件在内存中,未被打开的文件在磁盘中,所以我们本次研究文件操作的本质就是:进程与被打开文件之间的关系。

2. 语言级文件接口

在这里我们主要来回顾一下C语言中的文件接口:

打开文件接口:fopen

打开方式:

  • r:只读方式打开文件,文件不存在则打开失败;
  • w:写入的方式打开文件,文件不存在则创建;
  • a:追加式写入的方式打开文件,文件不存在则创建。

小细节:

以"w"方式打开文件,首先会清空文件内容,再写入;

以"a"方式打开文件,会在文件结尾开始写入,不清空。

关闭文件接口:fclose

 

写入接口:fputs、fwrite

#include <stdio.h>

int main()
{
    // 打开
    FILE* fp = fopen("log.txt", "w");
    if(fp == NULL)
    {
        perror("fopen");
        return 1;
    }
    const char * msg = "hello\n";
    int cnt = 10;
    while(cnt--)
    {
        fputs(msg, fp);  // 写入
    }
    // 关闭
    fclose(fp);
    return 0;
}

3. 系统级文件接口

我们上面写的C语言打开文件的相关接口,在底层一定封装了系统调用接口!

一个进程通过操作系统打开文件,所以操作系统一定要给我们提供系统调用接口。

接下来就来看一看文件操作相关的系统调用接口:

打开文件接口:open

这里来说一下这个标志位,标志位的常用选项有:

  • O_WRNOLY:写入
  • O_CREAT:创建
  • O_TRUNC:清空
  • O_APPEND:追加

这些标志位可以通过 | 来组合在一起使用。

文件权限我们设置为默认权限0666即可。

写入文件接口:write

关闭文件接口:close

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

int main()
{
    // 打开文件                    写入 + 创建 + 清空
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    const char *msg = "Hello Linux";
    // 写入
    write(fd, msg, strlen(msg));
    // 关闭
    close(fd);
    return 0;
}

 在往文件写入内容时不需要在最后面加上'\0'。

3.1 open返回值

我们一次打开多个文件,看看它们的返回值有何特点:

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

int main()
{
    // 打开文件                    写入 + 创建 + 清空
    int fd1 = open("log1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd2 = open("log2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd3 = open("log3.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd4 = open("log4.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    
    printf("fd1: %d\n", fd1);
    printf("fd2: %d\n", fd2);
    printf("fd3: %d\n", fd3);
    printf("fd4: %d\n", fd4);
    // 关闭
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    return 0;
}

可以看到这些返回值都是一个个连续的小整数,很类似于数组的下标

接下来就从系统的层面来理解一下文件fd:

在磁盘中存在众多的文件,每打开一个文件就需要在内核中形成对应的文件结构体对象,OS既然要管理这些被打开的文件,久而久之当被打开的文件越来越多,那么OS怎么知道哪几个文件是哪个进程打开的呢?

所以在OS中就会有一个struct files_struct的结构体对象,而这个结构体中存在一个struct file *fd_array[]的结构体指针数组,该数组被我们亲切的称为进程文件描述符表。每一个进程PCB都有一个指向该数组的指针,在这个数组中,记录了进程与文件结构体对象的对应关系,所以每一个进程都可以找到自己打开的文件。

整个过程就是open先给文件创建对应的结构体对象,再将对应的文件与文件描述符表联系起来,再通过返回之将下标返回给外部。

所以文件描述符fd本质就是数组的下标,OS访问文件只认文件描述符。

那么C语言的FILE是什么呢?

它是C语言提供的一个结构体类型,它的底层必定封装了文件描述符,接下来就通过代码的方式验证一下:

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

int main()
{
    // 打开文件       
    FILE *fp1 = fopen("log.txt1", "w");
    FILE *fp2 = fopen("log.txt2", "w");
    FILE *fp3 = fopen("log.txt3", "w");

    printf("fp1->fd: %d\n", fp1->_fileno);
    printf("fp2->fd: %d\n", fp2->_fileno);
    printf("fp3->fd: %d\n", fp3->_fileno);
    fclose(fp1);
    fclose(fp2);
    fclose(fp3);
    return 0;
}

那么文件描述符表中的0、1、2是什么呢?

在我们进程运行的时候会默认打开三个文件流:

  • 标准输入流:stdin (键盘)     0
  • 标准输出流:stdout(显示器)1
  • 标准错误流:stderr(显示器) 2

OS/C语言为什么要默认把这三个流打开呢?

最主要的原因是为了方便程序员进行默认的输入输出的代码编写。

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

int main()
{
    printf("stdin->fd: %d\n", stdin->_fileno);
    printf("stdout->fd: %d\n", stdout->_fileno);
    printf("stderr->fd: %d\n", stderr->_fileno);
    return 0;
}

3.2 如何理解一切皆文件 

磁盘、键盘、显示器、网卡等,这些外设统一按照文件的方式去看待,如何理解呢?

在底层的这些外设,站在方法的角度去考虑,每一种设备都有对应的读写方法,例如键盘,它具有读方法,写方法认为是空,显示器具有写方法,读方法认为是空,所以,这些设备的读写方法均不同,为了提升使用效率,在他们各自对应的文件结构体对象中会设置读写方法的函数指针,用于指向各自设备的具体读写方法,在访问这些设备时,直接去各自的文件结构体对象中调用读写方法,不需要关注底层,所以访问外设这一工作只需要访问各自对应的文件结构体,统一把它们当成文件对待,所以Linux下一切皆文件。

3.3 文件数据读写本质 

文件一般是存储在磁盘外设上的,struct file是在内核中创建,专门用来管理被打开文件的。

我们对文件进行读写操作的时候,并不能直接操作文件数据,所以在struct file存在一个字段,指向一块空间,叫做文件缓冲区,无论是我们读写文件数据,都需要先把数据加载到文件缓冲区中,这个加载的过程是由OS来完成的。所以我们在应用层进行数据读写的本质就是将内核缓冲区中的数据来回拷贝。

3.4 文件fd的分配规则

1. 进程默认已经打开了文件描述符0、1、2,可以直接使用0、1、2进行数据访问:

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

int main()
{
    char buffer[1024];
    ssize_t s = read(0, buffer, 1024);  // 从0号fd(键盘)中读取数据至buffer
    if(s > 0)
    {
        buffer[s - 1] = 0; // 将读取的'\n'置为'\0'
    } 
    write(1, buffer, strlen(buffer)); // 将buffer内容写入到1号fd(显示器)
    return 0;
}

2. 文件描述符分配规则:寻找最小的,没有被使用的位置,分配给指定的打开文件!

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

int main()
{
    // 先关闭2号fd
    close(2);
    // 重新分配
    int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
    printf("fd: %d\n", fd); // 2
    close(fd);
    return 0;
}

  

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!        

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

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

相关文章

Java二十三种设计模式-单例模式(1/23)

引言 在软件开发中&#xff0c;设计模式是一套被反复使用的、大家公认的、经过分类编目的代码设计经验的总结。单例模式作为其中一种创建型模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。本文将深入探讨单例模式的概念、实现方式、使用场景以及潜…

C语言 ——— 模拟实现strcpy函数

目录 strcpy函数功能介绍 strcpy函数的模拟实现 strcpy函数功能介绍 学习并使用strcpy函数-CSDN博客 strcpy函数的模拟实现 代码演示&#xff1a; #include<stdio.h> #include<assert.h> char* my_strcpy(char* destination, const char* source) {assert(des…

单目3D和bev综述

文章目录 SOTA2D 检测单目3d检测3d bev cam范式1 Transformer attention is all you need 20172 ViT vision transformer ICLR 2021google3 swin transformer 2021 ICCV bestpaper MS4 DETR 20205 DETR3D 20216 PETR 20227 bevformerLSSbevdetcaddn指标 mAP NDS标注&#xff1a…

kubernetes——Istio(三)

一、安全 将单一应用程序分解为微服务可提供各种好处&#xff0c;包括更好的灵活性、 可伸缩性以及服务复用的能力。但是&#xff0c;微服务也有特殊的安全需求&#xff1a; 为了抵御中间人攻击&#xff0c;需要流量加密。为了提供灵活的服务访问控制&#xff0c;需要双向 TL…

【P2P_BMA_P2MP_NBMA】

基本概念介绍 1. BMA&#xff08;Broadcast&#xff09; 广播型多路访问技术&#xff0c;在一个MA&#xff08;多路访问&#xff0c;在一个网段内的节点数量不限制。&#xff09;网络中同时存在广播机制。 特点&#xff1a; 允许将数据包广播到网络上的所有主机。路由器之间…

科普文:微服务技术栈梳理

概叙 如上两图所示&#xff0c;微服务架构下&#xff0c;需要的组件很多&#xff0c;上面中也并未列全。下面将梳理一下国内微服务架构下&#xff0c;用到的技术栈&#xff0c;仅供参考。 科普文&#xff1a;12种常见的软件架构-CSDN博客 没有最好的架构&#xff0c;只有最适…

开启音乐新纪元,AI人工智能创新歌词

在音乐的漫长历史长河中&#xff0c;每一次的创新都如同璀璨星辰&#xff0c;照亮了前行的道路。如今&#xff0c;人工智能的崛起正引领着音乐创作步入一个全新的纪元&#xff0c;为歌词领域带来了前所未有的变革。 “妙笔生词智能写歌词软件&#xff08;veve522&#xff09;”…

智慧园区智能化解决方案PPT(173页)

智慧园区智能化解决方案摘要 智慧园区智能化解决方案是一项综合性的系统工程&#xff0c;它通过集成先进的信息技术&#xff0c;实现园区管理的自动化、智能化&#xff0c;提高园区的安全性、效率和舒适度。本文详细介绍了某智慧园区项目的规划与设计&#xff0c;该项目建筑面…

python的字符串

字符串 简单操作 创建 利用 ‘ ’ 或 “ ” 将字符或数字包裹起来的都为字符串 a"你好" 格式化字符串 元组的字符格式化 字符串格式化函数 srt.format() f格式化 方法 split()//指定分割符经行分割 strip()//指定移除字符头尾的字符 join()//指定序列中的字符连接成新…

C#学习

C#学习 1.B站丑萌气质狗C#的循环-判断泛型错误处理面向对象static的使用定义showInfo类和Hero类 在这里插入图片描述 然后在该解决方案add新建一个类库&#xff0c;点击rebuild&#xff0c;会在bin文件夹下生成.dll文件 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direc…

SAC-IA粗配准算法记录

1. 算法思路 SAC-IA(Sample Consensus Initial Aligment,SAC-IA)粗配准算法是一种基于局部特征描述子的点云粗配准算法,其需要计算点云的快速点特征直方图(FPFH)来保持对应点对之间的相似关系,根据相似关系来搜索点云中的对应点。其基本原理是采用采样一致性的思想,通过查…

Zabbix6.0使用自带模板(Redis by Zabbix agent 2)监控Redis数据库

注意&#xff1a;Zabbix6.0使用Redis by Zabbix agent 2 模板可直接监控Redis数据。 1、添加Redis账号密码信息(如果Redis没有设置密码可省略此步骤) vim zabbix_agent2.confPlugins.Redis.Sessions.redis.Uritcp://redis.huayunworld.com:6379 Plugins.Redis.Sessions.redis…

工具推荐|语音轻松记笔记,AI帮你识别和润色

# 你日常有没有遇到这样的场景&#xff1f; 偶尔有一些奇思妙想想要记录下来&#xff0c;但没有一个轻量的工具&#xff0c;往往会想着想着就把这个想法抛之脑后。特别是搞短视频的&#xff0c;你也许希望把当时的想法录下来&#xff0c;稍微剪辑下就能出一条不错的口播视频。…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——5.拷贝数变异及突变图谱(1)

内容如下: 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的相关性分析…

CMU 15-213 CSAPP. Ch9. Virtual Memory

CMU 15-213 CSAPP (Ch1~Ch3) CMU 15-213 CSAPP (Ch5~Ch7) CMU 15-213 CSAPP (Ch8) CMU 15-213 CSAPP (Ch9) CMU 15-213 CSAPP (Ch10) 视频链接 课件链接 课程补充 该课程使用 64位 编译器&#xff01; Ch9. Virtual Memory 9.1 Address spaces 将内存看成数组&#xff0c;物…

OpenGL笔记十二之实现三角形在屏幕横向上往复运动的动画

OpenGL笔记十二之实现三角形在屏幕横向上往复运动的动画 —— 2024-07-14 晚上 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记十二之实现三角形在屏幕横向上往复运动的动画1.运行2.vs3.fs4.main.cpp的关键部分 1.运行 2.vs #version 330 core layout …

成都工业学院2022级数据库原理及应用专周课程学生选课系统(进阶篇)

运行环境 操作系统&#xff1a;Windows 11 家庭版 运行软件&#xff1a;Visual Studio Code Navicat Premium 16 进阶内容 过程函数改为触发器 例如将学生选课的过程函数改为对选课表添加触发器 使用ruoyi-vue实现可视化 配置并运行ruoyi-vue 进行代码生成 将生成的代码添…

【Linux】03.权限

一、权限的概念 Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;、普通用户。 超级用户&#xff1a;可以在 linux 系统下做任何事情&#xff0c;不受限制普通用户&#xff1a;在linux下做有限的事情超级用户的命令提示符是“#”&#xff0c;普通用户的命…

ctfshow-web入门-php特性(web104-web108)

目录 1、web104 2、web105 3、web106 4、web107 5、web108 1、web104 需要传入的 v1 和 v2 进行 sha1 加密后相等。 解法1&#xff1a; 这里都没有判断 v1 和 v2 是否相等&#xff0c;我们直接传入同样的内容加密后肯定也一样。 ?v21 post&#xff1a; v11 拿到 flag…

C++从入门到起飞之——输入输出!

目录 1.命名空间 1.1namespace的价值 1.2namespace的定义 1.3命名空间使⽤ 2.C输⼊&输出 3.完结散花 个人主页&#xff1a;秋风起&#xff0c;再归来~ C从入门到起飞 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己…