【Linux】基础IO—1

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

文章目录

前言

先来段代码回顾C文件接口

"w"写文件

"a"追加文件

"r"读文件

输出信息到显示器,你有哪些方法

stdin & stdout & stderr

提炼一下对文件的理解(第一阶段)

理解文件并采用系统调用接口来访问文件

总结



前言

世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!


提示:以下是本篇文章正文内容,下面案例可供参考

先来段代码回顾C文件接口

"w"写文件

#include <stdio.h>
#include <string.h>int main()
{FILE* fp = fopen("myfile", "w");if (!fp) {printf("fopen error!\n");}const char* msg = "hello bit!\n";int count = 5;while (count--) {fwrite(msg, strlen(msg), 1, fp);}fclose(fp);return 0;
}

我们要进行文件操作,前提是我们的程序跑起来。文件的打开和关闭,是CPU在执行我们的代码。

以“w”的方式打开文件:

1、如果文件不存在,就在当前路径下,新建指定的文件;

2、默认打开文件的时候,就会先把目标文件清空。

echo "hello bite" > log.txt

>(输出重定向):

1、文件不存在,就新建文件;

2、打开文件,先清空,再写入

>>(追加重定向):

不会清空文件,会在文件内容后面添加内容

"a"追加文件

#include <stdio.h>
int main()
{// 系统怎么知道当前创建的log.txt文件在这个路径下呢?// 因为我们在运行文件操作的时候,执行我们所写的代码,执行的时候,就已经变成了一个进程了,// 所以,我们建立log.txt文件时,默认会结合我们当前进程所在路径,拼上我们的log.txt,创建log.txt文件。// 我们进程在启动时,所处的路径,叫做当前进程的当前工作路径。FILE* fp = fopen("log.txt", "a");if (NULL == fp){perror("fopen");return 1;}fprintf(fp, "helloworld, %d, %s, %lf\n", 10, "whb", 3.14);fclose(fp);return 0;
}

"r"读文件

#include <stdio.h>
#include <string.h>int main()
{FILE* fp = fopen("myfile", "r");if (!fp) {printf("fopen error!\n");}char buf[1024];const char* msg = "hello bit!\n";while (1){//注意返回值和参数,此处有坑,仔细查看man手册关于该函数的说明ssize_t s = fread(buf, 1, strlen(msg), fp);if (s > 0) {buf[s] = 0;printf("%s", buf);}if (feof(fp)) {break;}}fclose(fp);return 0;
}

输出信息到显示器,你有哪些方法

#include <stdio.h>
#include <string.h>int main()
{const char* msg = "hello fwrite\n";fwrite(msg, strlen(msg), 1, stdout);printf("hello printf\n");fprintf(stdout, "hello fprintf\n");return 0;
}
int fprintf(FILE *stream,const char *format,...)
// 向显示器当中进行打印,把指定的内容按照指定的格式,写到特定的文件当中

stdin & stdout & stderr

  • C默认会打开三个输入输出流,分别是stdin(键盘), stdout(显示器), stderr(显示器)
  • 仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针

文件操作详解

提炼一下对文件的理解(第一阶段)

打开文件:本质其实是进程(struct task_struct)打开文件(struct file)!!!

文件没有被打开的时候,在哪里?在磁盘当中。

进程能打开很多文件吗?可以。

系统当中可不可以存在很多进程呢?可以。

很多清空下,OS内部,一定存在大量的被打开的文件。

那么OS要不要把这些被打开的文件进行管理呢?答案是要的。先描述,再组织!所以每一个被打开的文件,在OS内部,一定要存在对应的描述文件属性的结构体。类似PCB。

每一个语言对文件的操作方法都是不一样的。

理解文件并采用系统调用接口来访问文件

a、操作文件,本质:进程在操作文件。进程的文件的关系。

b、文件 -> 磁盘 -> 外设 -> 硬件 -> 向文件中写入,本质是向硬件中写入 -> 用户没有权利直接在硬件内写入 -> OS是硬件的管理者 -> 通过OS写入 -> OS必须给我们提供系统调用接口(OS不相信任何人) -> 我们用的C/C++/...中操作文件的方法,都是对系统调用接口的封装!

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问, 先来直接以代码的形式,实现和上面一模一样的代码:

 系统调用的文件操作man 2 open  2号手册,系统调用 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);第一个参数:打开的文件是谁,可以带路径,也可以直接写文件名;如果只有文件名,就在当前的路径下创建文件;第二个参数:想要怎么创建这个文件,flags是一个整数,但是flags可以传递很多标记位;第二个参数:flags是一个整数,是32个比特位。用比特位来进行标记位的传递。 ---- OS设计很多系统调用接口的常见方法  本质:位图第三个参数:文件的起始权限返回值:是一个整数,文件描述符;失败:返回-1,错误码被设置
 man 2 close#include <unistd.h>int close(int fd); 参数:open()函数返回的整数
 man 2 write  向文件当中写入#include <unistd.h>ssize_t write(int fd,const void *buf,size_t count);// 把指定的缓冲区写入指定的文件里第二个参数:指定一个缓冲区,缓冲区的起始地址第三个参数:缓冲区的大小

操作系统为每一个被打开的文件,都创建了内核数据结构(struct file),用于文件的描述,包含了文件的属性;struct file内部中会包含了两个个指针,一个指针会指向我们在系统当中对应的一段与该文件所对应的,叫做文件内核级的缓存(其实就是OS给我们申请的一块内存);另一个指针,指向的是操作底层方法的指针表(比如:一些硬件设备的操作方法)。

文件 = 属性 + 内容

  • 属性初始化 struct file    
  • 文件内容写到缓存里:日后读、写就在缓存里进行,然后再把数据刷新到磁盘里。

所以,每一个文件都要有一个:文件内核级的缓存
最终我们对OS内的文件的管理,转化成了对 struct file的内核数据结构的管理。
进程task_struct里有一个指针:struct files_struct *files,这个指针指向的是struct files_struct空间,空间里有一个指针数组(struct file *fd array[N]),这个指针数组中的每一个元素都是被打开文件的地址,而指针数组的下标是fd(文件描述符)

操作系统只认文件描述符fd,只要拿到了fd就可以对文件进行操作。

无论读写,都必须在合适的时候,让OS把文件的内容读到文件缓存区中。write、read函数,本质都是在拷贝数据。

open()在干什么呢?

1、创建file;

2、开辟文件缓冲区的空间,加载文件数据(延后);

3、查进程的文件描述符表;

4、file地址,填入对应的文件描述符表下标中;

5、返回下标。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{const char* message = "hello Linux file!\n";// 可以直接向“1”里面打印内容,“1”就是标准输出流write(1, message, strlen(message));// C语言的方式向标准输出流里打印内容fprintf(stdout, "hello: %d\n", 10);// 第一个参数是stdout的话,和printf()函数的结果一样fflush(stdout);// 刷新数据// 0:标准输入(键盘)  1:标准输出(显示器)  2:标准输出(显示器) // 0、1、2已经被占用了,所以open()函数的返回值从3开始int fda = open("loga.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);printf("fda: %d\n", fda);// 3int fdb = open("logb.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);printf("fdb: %d\n", fdb);// 4int fdc = open("logc.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);printf("fdc: %d\n", fdc);// 5int fdd = open("logd.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);printf("fdd: %d\n", fdd);// 6umask(0);// 权限掩码设置为0  就近原则:有设置的掩码,就用设置的;没有,就用系统的// system call// O_WRONLY:以只写的方式打开    O:代表open的意思// O_CREAT:如果文件不存在,创建这个文件// O_TRUNC:清空文件内容// O_APPEND:打开文件,用追加模式// 0666:文件的起始权限,起始权限也会和umask权限掩码处理,所以,不一定是0666的权限int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);// open()系统调用接口,以写的方式打开,默认不清空文件内容,下次写入文件是以覆盖式的写入内容if(fd < 0){perror("open");return 1;}const char *message = "hello Linux file!\n";//const char *message = "abcdefg\n";//const char *message = "123";write(fd, message, strlen(message));// C语言中有'\0',而Linux中没有'\0'的规则close(fd);return 0;
}

O_RDONLY: 只读打开

O_WRONLY: 只写打开

O_RDWR : 读,写打开 这三个常量,必须指定一个且只能指定一个

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

 怎么理解write(1, message, strlen(message))向一个整数里写,就相当于在一个文件里写呢?
 文件描述符fd,fd的本质是:内核的进程和文件映射关系的数组的下标。

 man 2 umask  //2号手册的权限掩码#include <sys/types.h>#include <sys/stat.h>mode_t umask(mode_t mask);//在程序运行时,动态的设置权限的掩码

我们这样讲可能还会有些抽象,我们直接找到原码来看一下这些结构体之间的关系:

而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来 描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进 程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数 组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件 描述符,就可以找到对应的文件。

我们要对文件进行操作,我们可以用系统调用,也可以用语言提供的文件方法,但是最好还是用语言提供的文件方法。为什么呢?

因为系统不同,系统调用接口可能不一样,系统调用的方法不具有跨平台性。

语言为什么具有跨平台性?
比如:C语言文件的操作函数,底层都是用系统调用接口实现的,而不同的操作平台,系统调用接口可能不一样,就比如:C语言中的fopen()函数就使用不同的OS(windows、maxos、Linux)的系统调用接口实现fopen()函数,那么在编译的时候,不同的OS会生成不同的标准库,当然这些标准库,也都是C标准库,我们每再一个平台下,都需要下载对应平台的标准库,它们标准库中实现的函数都叫fopen(),所以C语言具有跨平台性。

我有一个显示器,但是可以有很多终端。

ls /lib64/libc.so 
//C语言的动态库(文件)

文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的 最小的一个下标,作为新的文件描述符。


总结

好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。

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

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

相关文章

Git入门实战教程之创建版本库

一、Git简介 Git是一个分布式版本控制系&#xff0c;分层结构如下&#xff1a; Git分为四层&#xff1a; 1、工作目录 当前正在工作的项目的实际文件目录&#xff0c;我们执行命令git init时所在的地方&#xff0c;也就是我们执行一切文件操作的地方。 2、暂存区 暂存区是…

Docker Compose从入门到熟悉

一、DockerCompose是什么&#xff1f; Compose 项目是 Docker 官方的开源项目&#xff0c;负责实现对 Docker 容器集群的快速编排。使用前面介绍的Dockerfile我们很容易定义一个单独的应用容器。然而在日常开发工作中&#xff0c;经常会碰到需要多个容器相互配合来完成某项任务…

栈的应用——用栈实现算数混合运算表达式的计算

1、单目运算符双目运算符 算数运算符分为单目运算符和双目运算符等 单目运算符只需要一个操作数,双目运算符需要两个操作数 双目运算符最常见:常见的算术运算符:*/,比较运算符:<>=等等以下是一些单目运算符:正号 (+): 用于表示正数或给数值一个正号。例如:+5 仍然…

基于卷积神经网络的土豆疾病识别系统(pytorch框架)【python源码+UI界面+前端界面+功能源码详解】

功能演示&#xff1a; 土豆疾病识别系统&#xff0c;基于vgg16&#xff0c;resnet50卷积神经网络&#xff08;pytorch框架&#xff09;_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于卷积神经网络的土豆疾病识别系统是在pytorch框架下实现的&#xff0c;系统中有两…

Linux | MySQL基础

MySQL MySQL是关系型数据库&#xff08;RDBMS&#xff09;&#xff0c;采用表格结构存储数据 同类型的还有Oracle、SQL Server 主要特点 适用于中小规模、关系型数据库系统(RDBMS) 支持Linux、Unix、Windows等多种操作系统 支持Python、Java、Perl、PHP等编程语言 典型应…

计组第三版书例题

基础知识过一下 存储器与CPU的连接主要通过数据总线、地址总线和控制总线实现。CPU首先向存储器发送地址信号&#xff0c;然后发出读写控制信号&#xff0c;最后在数据总线上进行数据的读写操作 。这种连接方式确保了CPU能够正确地访问和控制存储器中的数据。 https://blog.cs…

树的基本概念与二叉树

文章目录 树的基本概念与二叉树一、树的概念和结构1. 树的概念2. 树的相关概念 二、树的存储1. 左孩子右兄弟表示法2. 双亲表示法 三、二叉树1. 特殊的二叉树1.1 满二叉树1.2 完全二叉树 树的基本概念与二叉树 一、树的概念和结构 1. 树的概念 树是一种非线性的数据结构,它是…

节点与其祖先之间的最大差值(DFS,Java)

题目描述&#xff1a; 给定二叉树的根节点 root&#xff0c;找出存在于 不同 节点 A 和 B 之间的最大值 V&#xff0c;其中 V |A.val - B.val|&#xff0c;且 A 是 B 的祖先。 &#xff08;如果 A 的任何子节点之一为 B&#xff0c;或者 A 的任何子节点是 B 的祖先&#xff…

【数据结构】--- 探索栈和队列的奥秘

关注小庄 顿顿解馋૮(˶ᵔ ᵕ ᵔ˶)ა &#x1f4a1;个人主页&#xff1a;9ilk &#x1f4a1;专栏&#xff1a;数据结构之旅 上回我们学习了顺序表和链表&#xff0c;今天博主来讲解两个新的数据结构 — 栈和队列 &#xff0c; 请放心食用 文章目录 &#x1f3e0; 栈&#x1…

【C++】二叉搜索数

目录 一、二叉搜索树的概念 二、二叉搜索树的模拟实现 1、定义节点 2、构造二叉树 3、析构二叉树 ​4、拷贝二叉树 5、二叉树赋值 6、插入节点 &#x1f31f;【非递归方式】 &#x1f31f;【递归方式】 7、打印节点 8、搜索节点 &#x1f31f;【非递归方式】 &…

我的需求分析方法论

或网上看了无数博客文章、技术视频&#xff0c;或购买金装版本技术书籍&#xff0c;看过无数原理原则、各种各样经典方法论&#xff0c;真正在实际开发工作中&#xff0c;本能去遵守和执行的又留下多少呢。 启动一个新系统时&#xff0c;我们可能还会去花些时间遵循这些原理原则…

中文大模型隐私保护哪家强?InternLM 与 Baichuan2 胜出!

引言&#xff1a;中文大模型隐私保护能力探索 本文研究了大语言模型&#xff08;LLMs&#xff09;对隐私和安全的影响&#xff0c;采用了三层渐进框架对语言系统的隐私进行评估。主要目标是全面评估LLMs对私人信息的敏感性&#xff0c;并检查其在识别、管理和保护敏感数据方面…

每日面经分享(Git经典题目,Git入门)

1. GitHub是什么 a. Git是一个分布式版本控制系统&#xff0c;作用是跟踪、管理和协调软件开发项目中的代码更改。 b. 提供了一种有效的方式来管理代码的版本历史&#xff0c;以及多人协作开发的能力。 2. Git的作用有哪些 a. 版本控制&#xff1a;Git可以记录每次代码更改的…

混合专家(MoE)模型

文心一言 混合专家模型&#xff08;Mixture of Experts&#xff0c;简称MoE&#xff09;是一种基于Transformer架构的模型设计策略。它通过将多个模型&#xff08;称为“专家”&#xff09;直接结合在一起&#xff0c;以获得更好的预测性能。这种模型特别适用于处理大规模数据…

springAI初体验 让人人都能跑大模型

springAI初体验 让人人都能跑大模型 Spring AI是一个旨在简化开发包含人工智能功能的应用程序的项目。它受到Python项目如LangChain和Llama Index的启发&#xff0c;但并非这些项目的直接移植。Spring AI的核心理念是为开发AI应用程序提供基础抽象&#xff0c;这些抽象有多个实…

Everything搭建http服务器

突然发现everything还可以搭建http服务器&#xff0c;给大家分享一下 打开everything&#xff0c;按图示填写或选择内容 在浏览器输入你的本地地址和端口 再输入刚才设置的账户密码&#xff0c;即可使用

接口测试常用代理工具

些代理工具可以帮助我们构造各种测试场景、以及更好的完成测试工作。下面的介绍以 Charles 为主。 Charles Charles 是一款代理服务器&#xff0c;可以截取请求和响应达到分析抓包的目的&#xff0c;且支持多平台&#xff0c;能够在 Windows&#xff0c;Mac&#xff0c;Linux…

【OJ】动规练习七之【模板】01背包

个人主页 &#xff1a; zxctscl 如有转载请先通知 DP41 【模板】01背包 1. DP41 【模板】01背包2. 分析3. 代码4. 优化5. 优化后代码 1. DP41 【模板】01背包 2. 分析 一、题目解析&#xff1a; 来看一下例1&#xff0c;3代表有三个物品&#xff0c;5代表能够容纳的体积。第一…

1970-2021年全国区县级碳排放数据8

1970-2021年全国区县级碳排放数据 1、时间&#xff1a;1970-2021年 2、指标&#xff1a;2877个区县 3、来源&#xff1a;EDGAR 4、指标&#xff1a;二氧化碳排放量 5、样本量&#xff1a;14W 6、指标解释&#xff1a; 二氧化碳排放是一个生态环境专业术语&#xff0c;主…

【Python系列】读取 Excel 第一列数据并赋值到指定列

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…