我的编程空间,编程开发者的网络收藏夹
学习永远不晚

【Linux】 基础IO——文件(下)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

【Linux】 基础IO——文件(下)

1. 文件描述符为什么从3开始使用?

修改test.c文件内容

#include    #include    #include    #include    #include    #include    #include    #define LOG "log.txt"        int main()    {      umask (0);//将权限掩码设置成0              int fd=open(LOG, O_RDONLY  );//打开一个文件,若文件不存在则重新创建一个          if(fd==-1)//说明打开失败          {        printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息          }      else      printf("fd :%d\n",  fd);                       char buffer[1024];     ssize_t n= read(fd,buffer,sizeof(buffer)-1);//使用系统接口来进行IO的时候,一定要注意\0的问题     if(n>0)//成功了,实际读到了多少字节     {      buffer[n]='\0';      printf("%s\n",buffer);     }      close(fd); //关闭文件          return 0;    } 

运行可执行程序,发现文件描述符返回的是3

但为啥是3,不是0 ,1,2
任何一个进程,在启动的时候,默认会打开当前进程的三个文件:
标准输入、标准输出、标准错误 ——本质都是文件
C语言:标准输入(stdin) 标准输出(stdout) 、标准错误(stderr) ——文件在系统层的表现
C++: 标准输入(cin) 标准输出(cout) 、标准错误(cerr) ——文件在系统层的表现,它是一个类


因为Linux下一切皆文件,所以向显示器打印,本质就是向文件中写入
标准输入—设备文件—>键盘文件
标准输出—设备文件—> 显示器文件
标准错误—设备文件—> 显示器文件


创建test.cc文件(cc后缀即cpp代码)

#include      #include//写C++时,使用C++风格的C语言代码      int main()      {        //C语言        printf("hello printf->stdout\n");//向stdout进行输出        fprintf(stdout,"hello printf->stdout\n ");//将数据向stdout进行输出        fprintf(stderr,"helllo printf->stderr\n");//将数据向标准错误打印                    //C++        std::cout<<"hello cout->cout"<cerr"<

输出重定向是将标准输出重定向,此时log.txt文件中只会存在标准输出的内容
所以标准输出和标准错误都会向显示器打印,但是其实是不一样的

0默认对应标准输入,1默认对应标准输出、2默认对应标准错误


修改myfile.c文件内容

#include    #include    #include    #include    #include    #include    #include    #define LOG "log.txt"        int main()    {    int fd1=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    int fd2=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    int fd3=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    int fd4=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    int fd5=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    int fd6=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    printf("%d\n",fd1);    printf("%d\n",fd2);    printf("%d\n",fd3);    printf("%d\n",fd4);    printf("%d\n",fd5);    printf("%d\n",fd6);   return 0;    }

运行可执行程序,发现 打印结果为 3 4 5 6 7 8
因为 标准输入、标准输出、标准错误分别占用了0 、1、2,所以只能从3开始

文件描述符(open对应的返回值)本质就是数组的下标

2. 文件描述符本质理解

在这里插入图片描述
启动代码时就会变成一个进程,该进程在内核中就必须有自己的数据结构 struct task_struct,
称之为当前进程所对应的进程描述符
打开文件时,操作系统会把文件加载到内存里,以供CPU通过进程的方式来访问对应的文件

任何一个进程,在启动的时候,默认会打开进程的三个文件,系统中一定会存在大量被打开的文件,这些文件一定会被操作系统管理起来,通过先描述,在组织,创建 struct file 结构体,该结构体一定包含文件属性等,每一次创建并打开文件时,都是在内核中创建一个struct file的结构体

目前认为只要找到file,就可以找到所有文件内容
为了维护一个进程和多个文件的映射关系,在内核中定义了数据结构struct files_struct,该结构体内部有一个数组struct file* fd [ ] ,是一个内容为struct file*的数组
当进程初始化时,会创建struct files_struct 结构体,通过结构体找到数组,只要有数组一定有下标

3. 如何理解Linux下的一切皆文件?

在这里插入图片描述
内存把数据写到显示器上,属于写入的过程,读取是从键盘中读取的,键盘输入后,操作系统把输入的数据回显到显示器上了,所以显示器只能负责打印

不同的硬件所对应的方法是完全不一样的,打开键盘时,操作系统内部会创建struct file对象

将键盘的read方法和 write方法 保存到函数指针中

每一个设备也只需要把方法的地址放入函数指针中
在当前进程看来,所有的东西都是文件对象,要有数据放到缓冲区里,底层读写时只需要调用对应的方法,来完成对应的读写,不关心底层的差异化
操作系统也有自己的wirte和read,本质上是拷贝,将应用层的数据拷贝到缓冲区里,在调用底层不同设备的方法,所以看起来就是Linux下一切皆文件

4. FILE是什么,谁提供?和内核的struct有关系么?

操作系统层面,必须要访问fd,才能找到文件
任何语言层访问外设或者文件必须经历操作系统
FILE *fopen(const char *path, const char *mode);

FILE是一个结构体,FILE由C语言提供的


C语言动态库


C语言头文件
在这里插入图片描述

证明struct FILE结构体中存在文件描述符fd

#include      #include      #include      #include      #include      #include      #include      #define LOG "log.txt"          int main()      {    printf("%d\n",stdin->_fileno);//fileno代表文件描述符    printf("%d\n",stdout->_fileno);    printf("%d\n",stderr->_fileno);    FILE*fp=fopen(LOG,"w");    printf("%d\n",fp->_fileno);     return 0;                           }   

说明结构体struct FILE内部存在文件描述符
同时因为0 1 2 被占用了,所以我们自己写的文件描述符返回3

5. 重定向的本质

关闭文件描述符0后,发现从0开始可以被输出了


关闭文件描述符0和2后,发现0和2都可以被使用了


进程中,文件描述符的分配规则:在文件描述符表中,最小的,没有被使用的数组元素分配给新文件

输出重定向

若不关闭文件描述符1,当前printf打印的结果显示到显示器上面


关闭文件描述符1,再打开新的文件log.txt


此时运行可执行程序没有显示出you can see me,打开新文件发现本来应该打印到显示器的内容,打印到log.txt中了

本来应该打印到显示器上的内容,打印到文件里 ,这种现象叫做重定向


在这里插入图片描述

在文件描述符表中,最小的,没有被使用的数组元素分配给新文件,所以把文件描述符1分配给了log.txt

1号下标里面的地址填成了log.txt文件的地址,上层printf打印它知道吗?
不知道,它也不关心,它只认文件描述符1

重定向的原理:在上层无法感知的情况下,在OS内部 ,更改进程内部对应的文件描述符表中,特定下标的指向

输入重定向

先在log.txt文件中输入内容 123 456
修改myfile.txt文件内容

关闭文件描述符0,所以scanf读取时会读取log.txt文件中的内容

读取的内容与log.txt文件内容相同


本来要从键盘中读取,结果现在要在文件中读取,这叫做输入重定向

在这里插入图片描述

追加重定向

关闭文件描述符1后,导致printf不会打印在显示器上,而是追加到log.txt文件中

运行可执行程序,无显示,都追加到log.txt文件中


重定向函数 ——dup2

输入 man dup2 查看
在这里插入图片描述

刚刚重定向时,需要先关闭文件描述符1,再打开文件
现在可以直接将文件打开,使用dup2重定向
输出重定向对应的文件描述符是1
打开myfile文件,假设其文件描述符是fd
newfd为oldfd的一份拷贝,最后只剩下oldfd
dup2(fd,1)


将3号描述符里面的内容拷贝到1里面,用3号内容覆盖1号内容,此时1号描述符就不再指向标准输出了,转而指向myfile文件,写入1的内容,就会写入文件中


把本来应该显示到标准输出的内容,显示到log.txt文件中


此时printf打印内容显示到log.txt文件中


6. 如何理解缓冲区?

修改myfile.c文件的内容

#include    #include    #include    #include    #include    #include    #include    #define LOG "log.txt"            int main()    {      //C库    fprintf(stdout,"hello world\n");    //系统调用     const char*msg="hello write\n";     write(1,msg,strlen(msg));     fork();  return 0;    }

运行可执行程序只有两行信息,但是重定向到log.txt文件后,打印出三行信息,说明重复打印了


若将fork函数注释掉后,发现 两者显示结果相同\


struct FILE除了会封装fd之外,还会预留一部分输出缓冲区
当把字符串想写入stdout中时 ,struct FILE除了fd,还有一部分缓冲区
当我们想写的时候,并不是把数据拷贝到操作系统内部,而是把数据放到缓冲区当中
此时这个fprintf函数会直接返回
C库会结合一定的刷新策略,将缓冲区中的数据写入操作系统(write(FILE->fd,xxxx))


刷新策略:
1.无缓冲 (不提供缓冲)
2.行缓冲
如果碰到\n,就会把\n在内之前的内容刷新出来
3. 全缓冲
只有把缓冲区写满的时候,才会刷新缓冲区
显示器采用的刷新策略:行缓冲
普通文件采用的刷新策略:全缓冲


为什么要有缓冲区?
节省调用者的时间
系统调用也会花费时间
可能写了10次,如果每次调用fprintf传给操作系统 都要花费时间
但若都写入缓冲区中,统一传给操作系统 效率就变高了


write接口不论有没有重定向,都会正常打印,因为调用write是系统调用 没有缓冲区,直接调用就写给操作系统了
而使用fprintf ,数据会先写入缓冲区
当要打印到显示器中时 刷新策略:行缓冲
因为打印的内容都存在\n,在调用fork时,打印的内容已经在缓冲区中被刷新走了,刷新之后在fork就没有任何意义了
所以fork就什么也没干

当打印到普通文件时 刷新策略:全缓冲
使用 hello world 没办法把缓冲区写满,就无法刷新,父子两个进程都要刷新
刷新就要对缓冲区做清空,即对数据做修改,此时谁先刷新就先发生写时拷贝,所以最终就会打印两次相同数据

来源地址:https://blog.csdn.net/qq_62939852/article/details/129786284

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

【Linux】 基础IO——文件(下)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

【Linux】 基础IO——文件(下)

文章目录 1. 文件描述符为什么从3开始使用?2. 文件描述符本质理解3. 如何理解Linux下的一切皆文件?4. FILE是什么,谁提供?和内核的struct有关系么?证明struct FILE结构体中存在文件描述符fd 5
2023-08-19

【Linux】基础IO——文件系统

文章目录 1.了解磁盘的物理结构磁盘的具体物理存储结构在一面上,如何在硬件上定位一个扇区? 2.逻辑抽象磁盘只认CHS,LBA如何跟磁盘地址互相转化? 3.文件系统一个组的结构细节问题 4.软硬链接1. 制作软硬链
2023-08-19

Linux基础IO【文件理解与操作】

✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Great minds discuss ide
2023-08-18

【Linux】基础IO——系统文件IO&&fd&&重定向

大家好我是沐曦希💕 文章目录 一、前言1.重新谈论文件2.重新谈论文件操作 二、回归C文件接口1.打开和关闭2.读写文件3.扩展 三、系统文件1.open和close2.write和read3.总结
2023-08-19

【创作赢红包】| 【Linux】 基础IO——自己实现文件接口FILE

文章目录 1. 创建makefile2. mystdio.h ——接口的声明3. mystdio.c —— 接口的实现1. MY_fopen的实现1.识别标志位2. 尝试打开文件3. 给用户返回MY_FILE对象,需要先创建对象4.
2023-08-20

Linux基础命令---lpr打印文件

lprlpr指令用来打印文件,如果没有指定文件名,那么从标准输入读取内容。CUPS提供了许多设置默认目标的方法。首先查询“LPDEST”和“PRINTER”环境变量。如果没有设置,则使用lpoptions(1)命令的当前默认集,然后使用lp
2023-06-05

Linux基础命令---验证组文件grpck

grpckgrpck指令可以验证组文件“/etc/group”和“/etc/gshadow”的完整性。检查的内容包括:正确的字段数、唯一有效的组名称、有效的组标识符、成员和管理员的有效列表、“/etc/gshadow”文件中的相应条目。检查
2023-06-05

Linux基础命令---检查密码文件pwck

pwck检查用户密码文件“/etc/passwd”和“/etc/shadow”的完整性,将验证结果送到标砖输出。提示用户删除格式不正确或有其他不可更正错误的条目。检查以验证每个条目是否具有:正确的字段数、唯一有效的用户名、有效的用户和组标识
2023-06-05

linux用户与文件基础命令整理

这篇文章主要介绍“linux用户与文件基础命令整理”,在日常操作中,相信很多人在linux用户与文件基础命令整理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”linux用户与文件基础命令整理”的疑惑有所帮助!
2023-06-09

python文件基础之(文件操作)

在之前学习了python的列表、元组、集合等知识,接下来将python的文件相关的知识做一总结和分析。一 open函数 在我们用word、excel、vim对文件操作时,肯定要先打开文件,同样在编程里面也是需要将文件打开,然后再对文件操作,
2023-01-31

Linux系统文件系统及文件基础是怎么样的

这篇文章将为大家详细讲解有关Linux系统文件系统及文件基础是怎么样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。学习Linux,重难点在于掌握不同类别的文件系统及其作用。通过对Linux
2023-06-05

编程热搜

目录