Linux——IO之系统接口+文件描述符详解

news/2024/11/7 13:35:39/

IO

  • 文件再次理解
  • 系统接口文件操作
  • 理解文件描述符 fd

文件再次理解

文件 = 文件内容 + 文件属性
其中文件属性也是数据–>即便你创建一个空文件,其也是要占据磁盘攻坚的。
文件操作 = 文件内容的操作 + 文件属性的操作
有可能在操作文件的过程中即改变文件的内容,又改变文件的属性。
我们在打开文件的时候,系统会将文件的属性加载到内存中
因为根据冯诺依曼体系结构,CPU需要对文件进行读写的话,需要对内存进行读写。
系统中的文件大致可以分为两类
打开的文件是内存文件,没有打开的文件在磁盘上静静的躺着可以称为磁盘文件。
通常我们所说的打开文件,访问文件关闭文件,是谁在进行相关操作?
一般是fopen,fclose,fread,fwrite... 这是我们的代码,然后将代码编译了二进制可执行程序 ,只有文件运行起来的时候,才会执行相应的代码,然后才会正真的对文件进行相关操作。所以是真正对文件进行操作的是进程
什么是当前路径?
看下列代码

{                                                                                                   5 6   FILE *fp = fopen("log.txt", "w");//写入7   if(fp == NULL) {8     perror("fopen");9     return 1;10   }11   printf("mypid: %d\n", getpid());12   while(1) {13     sleep(1);14   }15    //fprintf 将特定的数据格式化显示到文件流中16    const char *msg = "hello zjt";17    int cnt = 1;18    while(cnt < 20) {19      fprintf(fp, "%s: %d\n", msg, cnt++);20    }21    fclose(fp);22    printf("为什么不能创建文纪念\n");23   return 0;24 }

运行后发现
在这里插入图片描述
此时如果chdir更改当前的进程路径

chdir("/home/zjt");//更改当前进程的工作路径

在这里插入图片描述
所以当前路径并不是指源代码所在路径下,实际上指的是当前进程所在的路径下,也就是进程的工作路径,只不过默认情况下进程的工作路径是他当前自己所处的路径,不过这是可以改的。

在这里插入图片描述

小结一下 open打开方式

a:追加重定向,不断向文件中新增内容
w:以w方式打开文件时,准备写入的时候,其实文件已经被清空了

在这里插入图片描述
所以我们可以推导出,所有语言都对系统接口做了封装,C语言也对系统接口做了封装,对于其他语言而言,对于不同的系统接口的封装就类似于多态,也就是说,在最上层的接口调用都一样的,但是底层对于不同系统而言,其调用的结果是不一样的。C语言对系统接口的封装采用的是最直接的穷举所有的底层接口 + 条件编译做到的

为什么要封装?

  1. 因为原生系统接口,对于普通程序员而言,使用的成本比较高
  2. 而且如果直接使用系统接口的话,语言不具有跨平台性,不同系统的接口实现都是不一样的。

我们为什么要学习文件级别的系统接口?
因为万变不离其中,所有的语言,只要是文件操作,其最底层都会调用系统接口,只要我们将最本质的系统接口学习了,那么学习其他的语言最上层的调用就会得心应手了!

系统接口文件操作

open函数
在这里插入图片描述
O_RDONLY:以只读模式打开文件。
O_WRONLY:以只写模式打开文件。
O_RDWR:以读写模式打开文件。
上述三个宏如果文件不存在就会报错
O_CREAT:如果指定的文件不存在,则创建一个新的文件。如果文件已经存在,则不执行任何操作。需要指定文件的访问权限,
O_APPEND:在文件末尾追加;
O_TRUNC:将文件阶段清空

1 #include<stdio.h>2 #define PRINT_A 0x1 // 00013 #define PRINT_B 0x2 // 00104 #define PRINT_C 0x4 // 01005 #define PRINT_D 0x8 // 10006 #define PRINT_DFL 0x07 8 //类比open函数9 void Show(int flags)10 {11   if(flags & PRINT_A) printf("hello 我是A\n");12   if(flags & PRINT_B) printf("hello 我是B\n");13   if(flags & PRINT_C) printf("hello 我是C\n");14   if(flags & PRINT_D) printf("hello 我是D\n");15   if(flags == PRINT_DFL) printf("hello 我是Default\n");16 }17 int main()18 {19 20   printf("打印default\n");21   Show(PRINT_DFL);22   printf("打印A\n");23   Show(PRINT_A);24   printf("打印B\n");25   Show(PRINT_B);26   printf("打印A和B\n");27   Show(PRINT_A | PRINT_B);28   printf("打印C和D\n");29   Show(PRINT_C | PRINT_D);                                                                          30   return 0;31 }
~

在这里插入图片描述
这就是传宏标志的一种好的做法。
打开文件的系统调用接口有多重,但向文件写入的接口只有write
在这里插入图片描述

打开不存在的文件只能用三个参数的open
第三个参数表示文件的权限,打开已存在的文件用两个参数的open

 #include<stdio.h>2 #include<unistd.h>3 #include<sys/types.h>4 #include<sys/stat.h>5 #include<fcntl.h>6 #include<string.h>7 8 int main()9 {10 11   umask(0);//umask设置为0,以确定的方式打开文件12   int fd = open("log.txt", O_WRONLY | O_CREAT,  0666);//设置文件的基本权限13   if(fd < 0) {14     perror("open error");15     return 1;16   }17   printf("fd: %d \n",fd);18   int cnt = 0;19   const char *str = "我是一个即将要被写入的缓冲区\n";20   while(cnt < 5) {21     write(fd, str, strlen(str));//此时strlen不用加1,因为\0是C语言独有的标志,与系统无关22     ++cnt;                                                                                          23   }24   close(fd);25   return 0;

在这里插入图片描述
将上述代码只改一部分运行后显示下面的情况
在这里插入图片描述

在这里插入图片描述
说明系统调用的W与C语言中的"w"是不一样的,C语言fopen的"w"
对应着系统调用的三个接口O_WRONLY | O_CREAT | O_TRUNC
eg
在这里插入图片描述

在这里插入图片描述
因此还是C语言较方便,同时C语言还有一些fopen打开方式是以格式化写入等所以系统接口的调用非常麻烦
read函数
在这里插入图片描述

  umask(0);//umask设置为0,以确定的方式打开文件  12  // int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);//设置文件的基本权限  13    int fd = open("log.txt", O_RDONLY);  14   if(fd < 0) {  15     perror("open error");  16     return 1;  17   }  18   printf("fd: %d \n",fd);19   char buffer[128];20   //因为read读取文件的第二个参数是不关心数据是什么类型的21   //但是我们还是得把它当做字符串类型,所以在最后加一个'\0'                                          22   ssize_t s = read(fd, buffer, sizeof(buffer) - 1);                   23   if(s > 0) {                                                         24     buffer[s] = '\0';                                                 25     printf("%s\n",buffer);                                            26   }                                            

在这里插入图片描述

理解文件描述符 fd

 umask(0);//umask设置为0,以确定的方式打开文件12 13   int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);//设置文件的基本权限14   int fda = open("loga.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);//设置文件的基本权限15   int fdb = open("logb.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);//设置文件的基本权限16   int fdc = open("logc.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);//设置文件的基本权限17   int fdd = open("logd.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);//设置文件的基本权限18   printf("fd:  %5d\n",fd);                                                                          19   printf("fda: %5d\n",fda);20   printf("fdb: %5d\n",fdb);21   printf("fdc: %5d\n",fdc);22   printf("fdd: %5d\n",fdd);

此时运行
在这里插入图片描述
两个问题

  1. 为什么从3开始,0,1,2呢?
    在这里插入图片描述
    下面我们验证一下文件描述符0,1,2就是标准输入标准输出标准错误。
//FILE*是结构体指针,在结构体指针一定能找到文件描述符14    printf("stdin: %d\n",stdin->_fileno);15    printf("stdout:%d\n",stdout->_fileno);16    printf("stderr:%d\n",stderr->_fileno); 

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

总结:C语言的接口一定封装了系统接口
2. 0,1,2,3,4,5…你见过什么数据是这个样子的?
数组下标
因为用的都是系统接口,所以这些数组下标也就是操作系统的返回值。
一个进程是可以打开多个文件的,所以进程 :文件 = 1:n
所以在系统中,有可能会存在大量的被打开的文件,OS需要对这些文件做管理,所以先描述在组织

在这里插入图片描述
此时又产生了一个问题:
stdin,stdout,stderr对应的分别是键盘,显示器,显示器这些都是硬件,也用上述的struct file来标识对应的文件吗?

在这里插入图片描述

在这里插入图片描述
我们可以在源码中证明上述体系结构的存在

在这里插入图片描述


http://www.ppmy.cn/news/77217.html

相关文章

【华为OD机试真题2023B卷 JAVA】阿里巴巴找黄金宝箱(II)

华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 阿里巴巴找黄金宝箱(II) 知识点数组哈希表优先级队列 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0~N的箱子,每个箱子上…

ZeLinAI是什么?国产ChatGPT快速搭建自己的AI应用

ChatGPT使用门槛高&#xff0c;需要科学上网短信接码等&#xff0c;不如直接选择国产ZelinAI&#xff0c;使用超简单轻轻松松从0到1零代码创建自己的AI应用。目前模型仅支持GPT-3.5-turbo&#xff0c;后续应该会接入文心一言、GPT-4、GPT-4.5和Bard&#xff0c;新手站长分享国产…

RAM Sequential

前段时间&#xff0c;在微信公众号上偶然看到一篇很不错的技术分享文章&#xff1a;《南湖处理器DFT设计范例》。文中详细介绍了中科院计算所的RISC-V处理器实施的DFT设计。 去年&#xff0c;也基于一款处理器应用过Share Test Bus技术&#xff0c;但在memory界面fault测试的问…

绝世内功秘籍《调试技巧》

本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 内容专栏&#xff1a;这里是《C知识系统分享》专栏&#xff0c;笔者用重金(时间和精力)打造&#xff0c;基础知识一网打尽&#xff0c;希望可以帮到读者们哦。 内…

vulnhub靶场之bassamctf

1.信息收集 探测存活主机&#xff0c;输入&#xff1a;netdiscover -r 192.168.239.0/24 &#xff0c;发现192.168.239.177存活。 对目标主机192.168.239.176进行端口扫描&#xff0c;发现存活22(SSH)、80端口。 在浏览器上输入&#xff1a;http://192.168.239.177&#xff…

C语言学习分享(第八次)------初阶指针

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C语言学习分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C语言知识   &#x1f51d;&#x1f51d; 初阶指针 1. 前言&#x1f6a9;2. …

(8)(8.8) CAN总线设置

文章目录 前言 1 概述 2 配置设置 3 CAN ESCs 前言 本文展示了如何设置 CAN 总线,以及用户有哪些选项来完成适合其特定需求的设置。 !

【JOSEF约瑟 应用于输配电路、变压器 JDP-1004双位置继电器 端子排】

名称&#xff1a;双位置继电器&#xff1b;品牌&#xff1a;JOSEF约瑟&#xff1b;型号&#xff1a;JDP-1440&#xff1b;触点容量&#xff1a;250V2A&#xff1b;功率消耗&#xff1a;≤5W&#xff1b;额定电压&#xff1a;220V/110V&#xff1b;外形尺寸&#xff1a;868573特…