[基础IO]文件描述符{C库函数\系统接口\初识fd}

文章目录

  • 1.基础知识
    • 1.1对文件的认识
    • 1.2对系统调用接口的认识
    • 1.3如何理解LInux下一切皆文件?
  • 2.C语言的库函数
    • 2.1FILE *fopen(const char *path, const char *mode);
    • 2.2对fopen()的mode的w/a的深层认识
    • 2.3fclose()
    • 2.4size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
    • 2.5put()系列 -- fputs()
    • 2.6get()系列 -- fgets()
    • 2.7C语言文件库函数
      • 1.用C语言将字符串写到文件里 计算字符串长度时strlen()函数用+1吗?
      • 2.代码理解
  • 3.系统调用接口
    • 3.1如何用函数传递标志位 -- 数据结构之位图
    • 3.2FILE 是什么?
    • 3.3fd是什么?
    • 3.4内核中如何看待被打开的文件?
    • 3.5ssize_t read(int fd, void *buf, size_t count);
    • 3.6int fprintf(FILE * stream, const char* format, ...);
    • 3.7ssize_t write(int fd, const void *buf, size_t count);`
    • 3.8int open(const char *pathname, int flags, mode_t mode);`
    • 3.9代码理解

1.基础知识

1.1对文件的认识

  1. 文件 = 内容 + 属性(也是数据)
  2. 文件的所有操作: a. 对内容 b. 对属性 对内容操作可能引起属性操作 如: 增加内容 文件变大 标识文件大小的属性变化
  3. 文件在磁盘上存放 磁盘是硬件 用户访问文件: 代码–编译—可执行程序—运行—进程通过OS提供的系统调用接口访问文件
  4. 向磁盘(硬件)写入内容 只有OS有权利 普通用户要想写入 只能使用OS提供的文件类系统调用接口
    在这里插入图片描述

1.2对系统调用接口的认识

  1. 要想用系统调用接口 首先得了解怎么用 及使用者需要对系统有一定的认识与了解 而系统调用接口使用较难或者说系统太难理解 学习计算机的同学都是从易到难学习的 C/C++/java等等 或者说先是学习较简单的语言 然后再学例如计算机组成原理/编译原理/操作系统等较难课程 而从计算机发展历史看 实际上是先有的计算机组成/OS 而后才有的语言 但是我们不得不从语言开始学 因为一开始就学OS根本就学不懂 这就好比我们先学语言 再学数据结构一样 我们首先需要具备一定的语言知识 才能更好的学习底层 其次 底层配合语言让我们对计算机的理解更加深刻
  2. 为了让初学者去使用接口 或者说让这些系统调用接口能够被更好的使用 在接口上做了封装 形成各类语言的库函数 学习这些语言的人就能调用库函数来间接实现对接口的调用
  3. 不同的语言有不同的风格和体系 这也造成了不同语言的文件函数使用上具有很大差异 但是底层都是封装的系统接口
  4. 而你使用的OS基本上只有一个 基于此OS的系统调用接口 只有一套 如果我们学习了本质即底层 那么学习其他语言的接口将会是如鱼得水
  5. 上面为我们讲到 语言对系统调用接口封装形成库函数一大目的是更好更方便更简单的使用系统调用接口 那么还有第二大目的就是实现语言的跨平台性 如果语言不提供对文件的系统调用接口的封装 那么用该语言写出的代码一旦涉及到访问文件的操作都必须/只能调用系统调用接口 而在linux下写的语言就必须调用Linux的接口 这份语言 到windows下 由于Windows没有linux的接口或者说二者根本就无法用统一接口调用对应操作 使得这份语言不具备跨平台性

语言是如何实现跨平台的?

把基于所有平台(windows/Linux/MacOS)的代码都实现一遍条件编译 动态裁剪 在什么机器上跑 就用对应的封装

对显示器的认识

  1. 显示器是硬件 pintf/cout向显示器打印 本质上也是向硬件写入
  2. 磁盘我们无法向显示器一样直观看到 所以向显示器打印内容时 我们不觉得奇怪 因为一眼就能看到
  3. 向显示器打印和磁盘上文件的写入本质没有区别

1.3如何理解LInux下一切皆文件?

在Linux中,一切皆文件的原则是指Linux将所有的资源都视为文件或文件类型的一种。这包括硬件设备、目录、文件、进程、套接字等等。在Linux中,每个文件都有一个唯一的文件描述符,它是一个非负整数,用于标识打开的文件。通过文件描述符,我们可以对文件进行读写等操作。这种一切皆文件的设计理念使得Linux系统的操作变得非常简单和统一,因为所有的资源都可以通过相同的方式进行访问和操作。同时,这种设计理念也为Linux系统提供了很好的可扩展性和灵活性,因为新的设备和资源可以很容易地被添加到系统中,并且可以通过相同的方式进行管理和访问。

举个例子,如果你想要查看CPU的温度,你可以在Linux中打开一个文件,该文件位于/sys/class/thermal/thermal_zone0/temp,然后读取该文件的内容,就可以得到CPU的温度信息。这个文件就是一个虚拟文件,它代表了CPU的温度传感器,通过读取该文件的内容,我们就可以获取到CPU的温度信息。

另外,Linux中的文件类型也非常丰富,包括普通文件、目录文件、字符设备文件、块设备文件、套接字文件等等。每种文件类型都有自己的特点和用途,例如普通文件用于存储文本、二进制数据等,目录文件用于存储其他文件和目录等。理解Linux中的文件类型对于正确地使用和管理Linux系统非常重要。

如何理解文件?

  1. 最初我们认识到的文件是从语言级别认识的 例如C语言调用fopen/fread/fwrite等函数可以对文件进行打开关闭读写操作
  2. 后来我么认识并了解到 显示器和键盘也具有文件的一些属性如 读和写 printf/cout向显示器打印内容 实际上就是一种write scanf/cin从键盘获取数据 实际上就是一种read 至于为什么从键盘输入的内容能显示到显示器上 这实际上是一种回显 这些调用库函数实现对文件的读写操作 是站在程序的角度 而程序要想能够执行这些操作 首先他得运行成为进程 即代码和数据加载到内存 即以上这些过程 是站在内存的角度 通过进程来完成的
  3. 从文件中读数据到文件中的流程: 进程调用fopen/fread(Input)函数从A文件中读入数据到进程的内部(内存) 然后调用fwrite(Outpute)函数写到B文件中
  4. 由此我们得出文件的概念: 站在系统的角度 能够被input读取或者能够被output写出的设备 就叫做文件
  5. 之前认识的文件是文件的子集,是狭义上的文件即磁盘上的文件
  6. 广义上的文件如显示器,键盘,网卡,声卡,显卡,磁盘,几乎所有的外设,都可以称之为文件

2.C语言的库函数

2.1FILE *fopen(const char *path, const char *mode);

在这里插入图片描述

mode的不同作用和目的

在这里插入图片描述
r: 为了读打开一个文件 文件流定位在文件首 原文件不存在则报错
w: 为了写打开一个文件 清空文件原有内容 文件不存在创建一个新文件 文件流定位在文件首
a: 为了追加打开一个文件 文件不存在创建一个新文件 文件流定位在文件尾
(r+ w+ a+ : 为了读和写打开文件 其他对应一致)

2.2对fopen()的mode的w/a的深层认识

w/a 打开不存在的文件时 会创建一个新文件 这个文件创建在哪里?

  1. 当前路径
  2. 准确一点是源代码对应的可执行程序所在的路径
  3. 精确一点是Linux下源代码对应的可执行程序运行变为进程所处的工作路径 (Windows-VS下的源代码和可执行程序不在同一路径 有所区别)
  4. fopen()会将不存在文件名建立在 “当前路径” 下 以此作为新文件的路径

谁创建的?

源代码对应的可执行程序运行起来成为进程后进程调用系统接口创建的 ==> OS创建的!

ls /proc/pid -l.会显示什么?

  1. cwd: current work directory 当前进程所执行的程序的所在目录
  2. exe: 当前进程所执行的可执行程序路径+文件名
    即 当一个进程运行时 会记录自己当前所处的工作路径 可执行程序的路径变化 对应的进程的工作路径也会变化
    继续回答上面的问题 当前路径指的是什么?. 一个进程运行时 当前所处的工作路径

2.3fclose()

在这里插入图片描述

2.4size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

在这里插入图片描述

回顾之前的字符串函数

在这里插入图片描述

2.5put()系列 – fputs()

在这里插入图片描述
#include <stdio.h>
int fputc(int c, FILE *stream);
int fputs(const char *s, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
int puts(const char *s);

2.6get()系列 – fgets()

在这里插入图片描述 #include <stdio.h>
int fgetc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int getc(FILE *stream);
int getchar(void);
char *gets(char *s);
int ungetc(int c, FILE *stream);

2.7C语言文件库函数

1.用C语言将字符串写到文件里 计算字符串长度时strlen()函数用+1吗?

  1. 在C语言里字符串末尾有一个标识字符串结尾的标识符叫\0 它不属于字符串的内容 \0是C语言自己的规定
  2. 用C语言将字符串写到文件后 文件上的字符串与C语言再无瓜葛 文件要保存的是数据 不用也压根没必要遵守某种语言的规定 所以答案是不用+1
  3. 如果+1 文件内容会出现某一处乱码 因为文件无法对\0做有效解读

那文件里的字符串没有\0读的时候怎么分辨?\n怎么分辨?

读是用语言读的 语言级别上读的时候识别到后加上就行了 这就是系统与语言的区别

命令行下模拟w操作

echo hello txt > test.txt//向文件写内容.
> test.txt//输出重定向: 先打开文件 清空内容 又什么都不写 模拟完成.
实际上 echo命令 底层就是C语言调用fopen函数执行w操作完成的

按行读取char *fgets(char *s, int size, FILE *stream);

会自动在读到的字符串尾部+\0

C语言程序执行后三个标准输入输出流

FILE* : 把硬件看成了文件
extern FILE *stdin; --键盘
extern FILE *stdout; --显示器
extern FILE *stderr; --显示器

在这里插入图片描述

2.代码理解

int main(int argc, char *argv[])
{if(argc != 2) {printf("argv error!\n");return 1;}//FILE* fopen(const char* path, const char* mode);FILE* fp = fopen(argv[1], "r"); /* ./myfile test.txt *///./mycat log.txt 模拟实现输出文件内容if(fp == NULL){perror("fopen");return 2;}//按行读取char line[64];//char* fgets(char* s, int size, FILE * stream);  // fgetstring -- fgets 自动在所读取的字符结尾添加\0while(fgets(line, sizeof(line), fp) != NULL){//printf("%s", line);fprintf(stdout, "%s", line);}//文件操作//size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE* stream);//const char *s1 = "hello file\n"; //fwrite(s1, strlen(s1), 1, fp);//const char *s2 = "hello fprintf\n";//fprintf(fp, "%s", s2);//const char *s3 = "hello fputs\n";//fputs(s3, fp); int fputs(const char *s, FILE *stream);fclose(fp);return 0;
}

3.系统调用接口

C库函数: fopen fclose fread fwrite
系统接口: open close read write

3.1如何用函数传递标志位 – 数据结构之位图

应用层上一个简单的动作,在系统接口层面/OS层面,要做非常多的操作

//用比特位 标识状态
#define ONE 0x1   //0000 0001
#define TWO 0x2   //0000 0010
#define THREE 0x4 //0000 0100void show(int flags) 
{if(flags & ONE)    //0000 0001printf("hello one\n");if(flags & TWO)    //0000 0010printf("hello two\n");if(flags & THREE)  //0000 0100printf("hello three\n");
}int main()
{show(ONE); //000 0001printf("-----------------------------------------\n");show(TWO); //0000 0010printf("-----------------------------------------\n");show(ONE | TWO);  //000 0001 | 0000 0010 == 0000 0011printf("-----------------------------------------\n");show(ONE | THREE);printf("-----------------------------------------\n");show(ONE | TWO | THREE); //000 0001 | 0000 0010 | 0000 0100 == 0000 0111printf("-----------------------------------------\n");return 0;
}

fd: file descriptor 文件描述符 为什么创建一个文件从3开始? 0 1 2 呢?

在这里插入图片描述

3.2FILE 是什么?

  1. FILE是个C标准库提供的结构体 内部有很多成员 其中就封装了fd
  2. C库函数底层不一定都封装了系统接口 C关于文件的库函数底层都调用了系统接口 系统角度认识fd 但不认识C的FILE

3.3fd是什么?

  1. 进程要访问文件 必须先打开文件
  2. 一个进程可以打开多个文件 进程 : 文件 == 1 : n
  3. 文件被进程直接访问的前提是加载到内存中
  4. 一个进程有可以打开多个文件 多个进程会打开超多文件 此时系统中会存在大量的被打开的文件 OS要把如此多的文件管理起来 ==> 先描述再组织
  5. 在内核中 fd本质是一个数组下标

3.4内核中如何看待被打开的文件?

OS内部为管理每一个被打开的文件 为每一个被打开的文件构建一个file结构体对象 充当被打开的文件 然后把这些文件对象用双链表组织起来

struct FILE
{struct file* prev;struct file* next;//包含一个被打开的文件的几乎所有内容(权限,缓冲区,链接信息,属性等)
}

在这里插入图片描述

3.5ssize_t read(int fd, void *buf, size_t count);

在这里插入图片描述

  1. 从文件描述符fd向buf缓冲区读取最多count个字节
  2. 读取成功返回所读的字节数
  3. 读取错误返回-1

3.6int fprintf(FILE * stream, const char* format, …);

在这里插入图片描述

3.7ssize_t write(int fd, const void *buf, size_t count);`

在这里插入图片描述

3.8int open(const char *pathname, int flags, mode_t mode);`

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.9代码理解

int main()
{//int open(const char* pathname, int flags, mode_t mode);//不被系统的umask掩码影响 设置umask掩码为0 (就近)//目的是调用open()接口时 传的mode直接作为文件的权限umask(0);//只读// int fd = open("log.txt", O_RDONLY);//只写 目标文件不存在需创建新文件 每次打开清空原有内容  fopen("log.txt", "w");//int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); //rw-rw-rw-//if(fd < 0)//{//    perror("open");//    return 1;//}//char buffer[64];//memset(buffer, '\0', sizeof(buffer));// //  fread()调用read时会执行一个操作: // 在所读到的字符串结尾 + \0 // 而系统接口read()不会 read是按字节数读的 // 所以将buf数组全部置成\0 可以使得读完后最后有一个\0// //read(fd, buffer, sizeof(buffer));  //ssize_t read(int fd, void *buf, size_t count);//printf("%s", buffer);//const char *s = "hello write\n";//const char *s = "hi\n";//write(fd, s, strlen(s)); //ssize_t write(int fd, const void *buf, size_t count);//只写 目标文件不存在需创建新文件 追加写入  fopen("log.txt", "a");int fd1 = open("log1.txt", O_WRONLY | O_CREAT | O_APPEND, 0666); printf("open success, fd: %d\n", fd1);int fd2 = open("log2.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);printf("open success, fd: %d\n", fd2);int fd3 = open("log3.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);printf("open success, fd: %d\n", fd3);int fd4 = open("log4.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);printf("open success, fd: %d\n", fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;
}int main()
{char input[16] = { 0 };ssize_t s = read(0, input, sizeof(input));if (s > 0){input[s] = '\0';printf("%s\n", input);}int a = 0;fscanf(stdin, "%d", &a);  //scanffprintf(stdout, "%d\n", a); //printf//ssize_t write(int fd, const void *buf, size_t count);const char* s = "hello stdout(1)\n";write(1, s, strlen(s));return 0;
}

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

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

相关文章

测试经理主管面试题

测试专业技能 请谈谈您对软件测试生命周期&#xff08;STLC&#xff09;的理解 需求分析&#xff1a;在这个阶段&#xff0c;测试团队仔细分析项目需求&#xff0c;理解产品功能和非功能需求。这有助于确定测试的范围和目标&#xff0c;为后续阶段奠定基础。测试计划&#xf…

【桌面应用开发】Tauri是什么?基于Rust的桌面应用

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

深入.NET平台和C#编程总结大全

第一章 简单认识.NET框架 &#xff08;1&#xff09;首先我们得知道 .NET框架具有两个主要组件&#xff1a;公共语言进行时CLR&#xff08;Common Language Runtime&#xff09;和框架类库FCL&#xff08;Framework Class Library&#xff09; 配图&#xff1a; &#xff08;…

JSON

JSON指的是 JavaScript 对象表示法&#xff08;JavaScript Object Notation&#xff09; javascript对象&#xff1a;javascript中&#xff0c;除开JavaScript原始值(字符串&#xff0c;数字&#xff0c;布尔值&#xff0c;null&#xff0c;正则表达式)的都是javascript对象 JS…

Java - JVM内存区域的划分

Java 程序运行时&#xff0c;需要在内存中分配空间。为了提高运算效率&#xff0c;就对空间进行了不同区域的划分&#xff0c;因为每一片区域都有特定的处理数据方式和内存管理方式。 分配&#xff1a;通过关键字new创建对象分配内存空间&#xff0c;对象存在堆中。 释放 &…

柔性数组详解

前言&#xff1a;柔性数组是C99中新添加的概念&#xff0c;它是结构体里面的最后一个成员&#xff0c;因为它的大小未知&#xff0c;所以很灵活&#xff0c;称之为柔 1 柔性数组占不占结构体的空间呢&#xff1f; 不占 typedef struct Stu {char y;int x;int arr[];//有些编译器…

数据在内存中的存储(整型篇)

1.辨析原码反码补码&#xff1a; 1.原码&#xff1a;有32位&#xff08;int类四个字节&#xff0c;一个字节八个比特位&#xff09;&#xff0c;第一位是符号位&#xff0c;0正1负&#xff0c;其余为二进制位。 2.计算一般是对原码进行计算&#xff0c;但在负数计算使用原码会导…

强化学习(二)——Dueling Network(DQN改进)

与DNQ相比&#xff0c;使用优势函数(A函数)和状态价值函数&#xff08;V&#xff09;代替之前的Q(动作价值)函数&#xff0c; 最核心公式为 Q ∗ ( s , a ) A ∗ ( s , a ) V ∗ ( s ) − max ⁡ a A ∗ ( s , a ) Q^*(s,a)A^*(s,a)V^*(s)-\max_a A^*(s,a) Q∗(s,a)A∗(s,a)V…

高效利用内存资源之动态内存管理详解

目录 一、为什么存在动态内存分配 二、动态内存函数的介绍 2.1malloc 2.2free 2.3calloc 2.4realloc 三、常见的动态内存错误 3.1对NULL指针的解引用操作 3.2对动态开辟空间的越界访问 3.3对非动态开辟内存使用free释放 3.4使用free释放一块动态开辟内存的一部分 3.…

Spring Boot 3 集成 Druid 连接池详解

在现代的Java应用中&#xff0c;使用一个高效可靠的数据源是至关重要的。Druid连接池作为一款强大的数据库连接池&#xff0c;提供了丰富的监控和管理功能&#xff0c;成为很多Java项目的首选。本文将详细介绍如何在Spring Boot 3项目中配置数据源&#xff0c;集成Druid连接池&…

【ACM独立出版、确定的ISBN号】第三届密码学、网络安全和通信技术国际会议(CNSCT 2024)

第三届密码学、网络安全和通信技术国际会议&#xff08;CNSCT 2024&#xff09; 2024 3rd International Conference on Cryptography, Network Security and Communication Technology 随着互联网和网络应用的不断发展&#xff0c;网络安全在计算机科学中的地位越来越重要&…

Android Kotlin 泛型:强大的类型抽象和重用利器

一、什么是泛型&#xff1f; 泛型是一种参数化类型的机制&#xff0c;它允许我们在定义类、接口和方法时使用类型参数&#xff0c;从而实现代码的重用和类型安全。通过使用泛型&#xff0c;我们可以将类型作为参数传递给类或方法&#xff0c;在不同的场景中实现灵活的类型适配…

精选:免费且高效的邮件营销软件推荐

好用的邮件营销软件可以帮助企业获取客户、维系客户关系。对于有想做邮件营销想法&#xff0c;但是想先试试的企业来讲&#xff0c;免费版邮件营销软件就是个不错的选择。当然&#xff0c;免费的邮件邮件营销软件虽然在价格上有极大的优势&#xff0c;但是功能上会有各种限制。…

Java生态系统最受欢迎的工具类使用指南 ‍

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍专栏》 🐾 学会IDEA常用操作,工作效率翻倍~💐 �…

【小白专用】MySQL查询数据库所有表名及表结构其注释

一、先了解下INFORMATION_SCHEMA 1、在MySQL中&#xff0c;把INFORMATION_SCHEMA看作是一个数据库&#xff0c;确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名&#xff0c;数据库的表&#xff0c;表栏的数据类型与访问权 限等。在INF…

网络安全——SSH密码攻击实验

一、实验目的要求&#xff1a; 二、实验设备与环境&#xff1a; 三、实验原理&#xff1a; 四、实验步骤&#xff1a;​ 五、实验现象、结果记录及整理&#xff1a; 六、分析讨论与思考题解答&#xff1a; 一、实验目的要求&#xff1a; 1、了解SSH密码攻击、FTP密码攻击…

【BEV感知 EA-LSS 方案】Edge-aware Lift-splat-shot

前言 本文分享LSS方案的改进方案——EA-LSS,它解决了“深度跳变”问题,提出了一个新框架Edge-aware Lift-splat-shot 。 适用于“多视图转BEV”,可以代替原来的LSS模块,并有效地提高了检测精度,而推理时间的增加很少。 在nuScenes测试集上验证,纯相机模型或多模态模型…

Jmeter 请求签名api接口-BeanShell

Jmeter 请求签名api接口-BeanShell 项目签名说明编译扩展jar包jmeter 使用 BeanShell 调用jar包中的签名方法 项目签名说明 有签名算法的api接口本地不好测试&#xff0c;使用BeanShell 扩展jar 包对参数进行签名&#xff0c;接口签名算法使用 sha512Hex 算法。签名的说明如下…

Mybatis是如何进行分页的?

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

Django系列之Celery异步框架+RabbitMQ使用

在Django项目中&#xff0c;如何集成使用Celery框架来完成一些异步任务以及定时任务呢&#xff1f; 1. 安装 pip install celery # celery框架 pip install django-celery-beat # celery定时任务使用 pip install django-celery-results # celery存储结果使用2. Django集成…