Linux基础IO

Linux基础IO

  • 1.理解文件
    • 1.1 狭义理解
    • 1.2 广义理解
    • 1.3 文件操作的归类认知
    • 1.4 系统角度
  • 2.c的文件接口
    • 2.1 hello.c打开文件
    • 2.2 hello.c写文件
    • 2.3 hello.c读文件
    • 2.4 stdin & stdout & stderr
  • 3.系统打开文件接口
    • 3.1 一种传递标记位的方法
    • 3.2 open函数
    • 3.3 文件描述符
      • 3.3.0 文件描述符表
      • 3.3.1 文件描述符0&1&2
      • 3.3.2 文件描述符的分配规则
      • 3.3.3 重定向
      • 3.3.4 系统调用dup2
    • 4. 重谈重定向
  • 5.理解一切皆文件
  • 6.缓冲区
    • 缓冲区是什么
    • 缓冲类型
    • File
    • libc读写函数的封装

1.理解文件

1.1 狭义理解

  • ⽂件在磁盘⾥
  • 磁盘是永久性存储介质,因此⽂件在磁盘上的存储是永久性的
  • 磁盘是外设(即是输出设备也是输⼊设备)
  • 磁盘上的⽂件 本质是对⽂件的所有操作,都是对外设的输⼊和输出简称 IO

1.2 广义理解

  • Linux 下⼀切皆⽂件(键盘、显⽰器、⽹卡、磁盘…… 这些都是抽象化的过程)

1.3 文件操作的归类认知

  • 对于 0KB 的空⽂件是占⽤磁盘空间的
  • ⽂件是⽂件属性(元数据)和⽂件内容的集合(⽂件 = 属性(元数据)+ 内容)
  • 所有的⽂件操作本质是⽂件内容操作和⽂件属性操作

1.4 系统角度

  • 对⽂件的操作本质是进程对⽂件的操作

  • 磁盘的管理者是操作系统

  • ⽂件的读写本质不是通过 C 语⾔ / C++ 的库函数来操作的(这些库函数只是为⽤⼾提供⽅便),⽽是通过⽂件相关的系统调⽤接⼝来实现的

2.c的文件接口

2.1 hello.c打开文件

#include<stdio.h>                                 
int main()
{FILE* fp = fopen("test.txt", "w");if (fp == NULL){perror("open test.txt failure!!\n");}return 0;
}

2.2 hello.c写文件

#include<stdio.h>
#include<string.h>
int main()
{FILE* fp = fopen("test.txt", "w");const char* message = "Hello Day";if (fp == NULL){perror("open failure!!!\n");return 0;}for (int i = 1; i <= 5; i++){char bufer[1024];snprintf(bufer, sizeof(bufer), "%s%d\n", message, i);fwrite(bufer, 1, strlen((const char*)bufer), fp);}fclose(fp);
}

2.3 hello.c读文件

#include<stdio.h>
#include<string.h>
int main()
{//FILE * ptr=fopen("test.txt","r");if(ptr==NULL){perror("open failure");return 0;}char buffer[1024];fread(buffer,sizeof(buffer),1,ptr);printf("%s",buffer);return 0;
}

在这里插入图片描述

2.4 stdin & stdout & stderr

  • C默认会打开三个输⼊输出流,分别是stdin, stdout, stderr
  • 仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,⽂件指针

3.系统打开文件接口

3.1 一种传递标记位的方法

#include <stdio.h>
#define ONE 0001 //0000 0001
#define TWO 0002 //0000 0010
#define THREE 0004 //0000 0100
void func(int flags) {
if (flags & ONE) printf("flags has ONE! ");
if (flags & TWO) printf("flags has TWO! ");
if (flags & THREE) printf("flags has THREE! ");
printf("\n");
}
int main() {
func(ONE);
func(THREE);
func(ONE | TWO);
func(ONE | THREE | TWO);
return 0;
}

这样我们想要那些模式就可以通过传入一个整形来进行多个操作。

3.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);

函数参数:

  • pathname:就是你要打开文件的路径
  • flags表示你想要打开这个文件的方式
  • O_RDONLY: 只读打开;O_WRONLY: 只写打开;O_RDWR : 读,写打开这三个常量,必须指定⼀个且只能指定⼀个
    O_CREAT : 若⽂件不存在,则创建它。需要使⽤mode选项,来指明新⽂件的访问
    O_APPEND: 追加写
  • mode就是我们要传入的权限位通常是我们打算穿件文件时需要指定权限。

函数返回值:

  • 成功:新打开的⽂件描述符
  • 失败:-1

3.3 文件描述符

3.3.0 文件描述符表

当我们打开一个文件时会创建一个struct file结构体来描述这个被打开的文件,该结构体里面有一个内存级别的缓冲区我们的数据会先加载到该缓存区。因此当我们打开多个文件那我们是不是要来管理这些被打开的文件因此文件描述符表就出来了。
在这里插入图片描述

3.3.1 文件描述符0&1&2

  • Linux进程默认情况下会有3个缺省打开的⽂件描述符,分别是标准输⼊0, 标准输出1, 标准错
    误2.
  • 0,1,2对应的物理设备⼀般是:键盘,显⽰器,显⽰器

3.3.2 文件描述符的分配规则

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{umask(0);int fd=open("myfile,txt",O_WRONLY|O_CREAT,0666);printf("%d\n",fd);close(fd);return 0;
}
//运行结果:3

我们关闭0

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{umask(0);close(0);int fd=open("myfile,txt",O_WRONLY|O_CREAT,0666);printf("%d\n",fd);close(fd);return 0;
}
//运行结果:0

可⻅,⽂件描述符的分配规则:在files_struct数组当中,找到当前没有被使⽤的最⼩的⼀个下标,作为新的⽂件描述符。

3.3.3 重定向

因此我们的重定向就是修改我们文件描述符表指向的文件。

3.3.4 系统调用dup2

#include <unistd.h>
int dup2(int oldfd, int newfd);
//参数解释:表示newfd是oldfd的一份拷贝因此此时两个文件描述符都是oldfd;全都只想oldfd指向的文件了。

4. 重谈重定向

在这里插入图片描述
下面我们进行重定向
在这里插入图片描述
可以看到我们往1里面写的数据写到了新文件中;这是因为我们
./test.exe > log.txt其实就是./test.exe 1>log.txt;因此我们1里面现在指向了新打开的文件。而2我们没有改变它的指向因此还会向显示器输出。

5.理解一切皆文件

在这里插入图片描述
上图中的外设,每个设备都可以有⾃⼰的read、write,但⼀定是对应着不同的操作⽅法!!但通过struct file 下 file_operation 中的各种函数回调,让我们开发者只⽤file便可调取 Linux 系统中绝⼤部分的资源!!这便是“linux下⼀切皆⽂件”的核⼼理解。因此在上层我们就把一个个设备当做一个打开的文件。调用各自的读写方法。

6.缓冲区

缓冲区是什么

缓冲区其实就是一块存数据的地方,有了·缓冲区可以方便我们用户的操作加快效率,在语言层的缓冲区的建立有利于我们减少调用系统接口的次数,从而减少我们调用系统的成本。

缓冲类型

  1. 全缓冲区:这种缓冲⽅式要求填满整个缓冲区后才进⾏I/O系统调⽤操作。对于磁盘⽂件的操作通常使⽤全缓冲的⽅式访问
  2. ⾏缓冲区:在⾏缓冲情况下,当在输⼊和输出中遇到换⾏符时,标准I/O库函数将会执⾏系统调⽤操作。当所操作的流涉及⼀个终端时(例如标准输⼊和标准输出),使⽤⾏缓冲⽅式。因为标准I/O库每⾏的缓冲区⻓度是固定的,所以只要填满了缓冲区,即使还没有遇到换⾏符,也会执⾏I/O系统调⽤操作,默认⾏缓冲区的⼤⼩为1024。
  3. ⽆缓冲区:⽆缓冲区是指标准I/O库不对字符进⾏缓存,直接调⽤系统调⽤。标准出错流stderr通
    常是不带缓冲区的,这使得出错信息能够尽快地显⽰出来。
    上面缓冲类型全缓冲效率最高除此之外当缓冲区满是或者调用fflush会强制刷新缓冲区

示例代码如下:

在这里插入图片描述
可以看到是正常的输出表现
在这里插入图片描述
当我们进行重定向后发现系统调用最先打出来,后面才是对应的库函数。这是因为我们没进行重定向之前是默认往1号文件描述符打的因此是行刷新,但是重定向到一个文件就是全刷新,因此在进程退出时,我们的库函数才会打印出来。

接着我们在代码后面加上fork()接着进行重定向
在这里插入图片描述
在这里插入图片描述
这是因为我们的库函数现在是全刷新因此我们创建子进程后两者结束时会刷新两次用户级的缓冲区因此我们的库函数被刷新了两次。

File

  • 因为IO相关函数与系统调⽤接⼝对应,并且库函数封装系统调⽤,所以本质上,访问⽂件都是通过fd访问的。
  • 所以C库当中的FILE结构体内部,必定封装了fd。

libc读写函数的封装

//mylib.h
#pragma once
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<stdlib.h>
#include <fcntl.h>
#include<string.h>
#include<unistd.h>
#define SIZE 1024
#define NOFRUSG 1<<0
#define LINEFRUSH 1<<1
#define QUANFUSH 1<<2
typedef struct MyFile
{int fd;char buffer[SIZE];int flag;      // 标记打开方式int bufferlen; // 当前缓冲区大小int frush_method;
} MyFile;
size_t myfread(void *ptr, size_t size, size_t nmemb, MyFile *stream);
size_t myfwrite(const void *ptr, size_t size, size_t nmemb, MyFile *stream);
MyFile *myfopen(const char *pathname, const char *mode);
int myfclose(MyFile *stream);
int myfflush(MyFile *stream);//mylib.c
#include "mylib.h"void  Init(MyFile *file, int fdd, int flag)
{memset(file->buffer, 0, sizeof(file->buffer));file->bufferlen = 0;file->fd = fdd;file->flag = flag;file->frush_method=QUANFUSH;
}
MyFile *myfopen(const char *pathname, const char *mode)
{int flag = -1;int st = 1;if (strcmp(mode, "w") == 0){st = 0;flag = O_WRONLY | O_CREAT | O_TRUNC;}else if (strcmp(mode, "r") == 0){flag = O_RDONLY;}else if (strcmp("a", mode) == 0){flag = O_APPEND | O_CREAT | O_WRONLY;st = 0;}else{}int fdd = -1;umask(0);if (st == 0)fdd = open(pathname, flag, 0666);elsefdd = open(pathname, flag);if (fdd < 0)return NULL;MyFile *file = (MyFile *)malloc(sizeof(MyFile));Init(file,fdd,flag);return file;
}
int myfflush(MyFile *stream)
{//语言层的刷新其实就是直接写入到内核中int n=write(stream->fd,stream->buffer,stream->bufferlen);(void)n;
}
void printfbuff(MyFile*stream)
{for(int i=0;i<=stream->bufferlen;i++) printf("%c",stream->buffer[i]);
}
size_t myfwrite(const void *ptr, size_t size, size_t nmemb, MyFile *stream)
{//写入本质先拷贝到用户的缓冲区memcpy(stream->buffer+stream->bufferlen,ptr,size*nmemb);//更新缓冲区长度stream->bufferlen+=nmemb*size;//考虑是不是要进行刷新到我们的内核缓冲区if(stream->fd==1||stream->fd==2) stream->frush_method=LINEFRUSH;if(stream->frush_method==LINEFRUSH&&stream->buffer[stream->bufferlen-1]=='\n'||stream->bufferlen==SIZE){myfflush(stream);}printfbuff(stream);}
int myfclose(MyFile *stream)
{close(stream->fd);
}
size_t myfread(void *ptr, size_t size, size_t nmemb, MyFile *stream)
{char buffer[1024];return read(stream->fd,buffer,sizeof(buffer));
}//test.c
#include"mylib.h"
int main()
{MyFile*ptr=myfopen("log.txt","w");if(ptr==NULL){perror("myfopen failure\n");return 0;}const char*message="hello world\n";myfwrite(message,1,strlen(message),ptr);return 0;
}

执行结果:
在这里插入图片描述
可以看到我们的打印缓冲区写入了我们的helloworld但是由于我们打开的文件不是显示器文件因此是全缓冲并不会直接打印,但是们的缓冲区是有的。

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

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

相关文章

Linux下学【MySQL】中如何实现:多表查询(配sql+实操图+案例巩固 通俗易懂版~)

每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论&#xff1a; 本章是MySQL篇中&#xff0c;非常实用性的篇章&#xff0c;相信在实际工作中对于表的查询&#xff0c;很多时候会涉及多表的查询&#xff0c;在多表查询的…

C#调用Ni板卡进行实现采集任务(模拟量输入输出)示例1

本文介绍如何使用C#控制Ni的USB-6008板卡进行模拟量输入、模拟量输出、输出量输入、数字量输出。代码详见下面的链接: C#调用Ni板卡进行实现采集任务(模拟量输入输出)示例1资源-CSDN文库 https://download.csdn.net/download/qq_34047402/90457042 步骤1、确认NI MAX可以正…

Mysql基础-事务

目录 一、事务简介 二、 事务操作 1 未控制事务 ​2 控制事务一 3 控制事务二 三、事务四大特性 ​四、并发事务问题 1). 脏读: 2). 不可重复读: 3). 幻读: 五、事务隔离级别 1). 查看事务隔离级别 ​2). 设置事务隔离级别 一、事务简介 事务 是一组操作的集合&a…

【Azure 架构师学习笔记】- Azure Databricks (15) --Delta Lake 和Data Lake

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (14) – 搭建Medallion Architecture part 2 前言 ADB 除了UC 这个概念之外&#xff0c;前面【Azure 架构师学习笔记】- Azure Databricks (1…

FPGA 高速接口Aurora8B/10B 协议详解与仿真

FPGA 高速接口Aurora8B/10B 协议详解与IP仿真 1 摘要 Aurora 8B/10B 是一种用于高速串行通信的协议&#xff0c;通常用于 FPGA 设计和其他数字通信应用。即一种编码方案&#xff0c;旨在在传输数据时提供可靠性、时钟恢复和错误检测。主要用于在点对点串行链路间移动数据的可…

【Linux-网络】深入拆解TCP核心机制与UDP的无状态设计

&#x1f3ac; 个人主页&#xff1a;谁在夜里看海. &#x1f4d6; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 道阻且长&#xff0c;行则将至 目录 &#x1f4da;引言 &#x1f4da;一、UDP协议 &#x1f4d6; 1.概述 &#x1f4d6; 2.特点 &#x1…

一文学会Spring

一、Spring简介 Spring的优点 Spring是一个开源免费的框架、容器Spring是一个轻量级的框架&#xff0c;非侵入式的控制反转IOC、面向切面AOP支持事务 Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器 二、IOC 2.1 IOC本质 控制反转IOC&#xff0c;是一种设计思想…

AWR microwave office 仿真学习(三)各类传输线模型学习

目录 引言Phase Spec: Coupled Lines (Closed Form): CLINPhysical Spec: Coupled Lines, Grounded Shield, Improved Accuracy (Closed Form): CLINPPhysical Specification, Floating Shield (Closed Form): COAXElectrical Specification, Grounded Shield (Closed Form): C…

TrustRAG:通过配置化模块化的检索增强生成(RAG)框架提高生成结果的可靠性和可追溯性

TrustRAG旨在风险感知的信息检索场景中提高生成内容的一致性和可信度。用户可以利用私有语料库构建自己的RAG应用程序,研究库中的RAG组件,并使用定制模块进行实验。论文展示了TrustRAG系统在摘要问答任务中的应用,并通过案例研究验证了其有效性。总体而言,TrustRAG通过语义…

pgsql行列转换

目录 一、造测试数据 二、行转列 1.函数定义 2.语法 3.示例 三、列转行 1.函数定义 2.语法 3.示例 一、造测试数据 create table test ( id int, json1 varchar, json2 varchar );insert into test values(1,111,{111}); insert into test values(2,111,222,{111,22…

计算机视觉算法实战——人脑解码(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ ​​​ 1. 领域简介 人脑解码&#xff08;Brain Decoding&#xff09;是神经科学与计算机视觉交叉领域的一个重要研究方向&#xff0c;旨在…

[Computer Vision]实验六:视差估计

目录 一、实验内容 二、实验过程 2.1.1 test.py文件 2.1.2 test.py文件结果与分析 2.2.1 文件代码 2.2.2 结果与分析 一、实验内容 给定左右相机图片&#xff0c;估算图片的视差/深度&#xff1b;体现极线校正&#xff08;例如打印前后极线对&#xff09;、同名点匹配…

【Mac】git使用再学习

目录 前言 如何使用github建立自己的代码库 第一步&#xff1a;建立本地git与远程github的联系 生成密钥 将密钥加入github 第二步&#xff1a;创建github仓库并clone到本地 第三步&#xff1a;上传文件 常见的git命令 git commit git branch git merge/git rebase …

从0搭建Tomcat第二天:深入理解Servlet容器与反射机制

在上一篇博客中&#xff0c;我们从0开始搭建了一个简易的Tomcat服务器&#xff0c;并实现了基本的HTTP请求处理。今天&#xff0c;我们将继续深入探讨Tomcat的核心组件之一——Servlet容器&#xff0c;并介绍如何使用反射机制动态加载和管理Servlet。 1. Servlet容器的作用 S…

文件上传漏洞:upload-labs靶场11-20

目录 pass-11 pass-12 pass-13 pass-14 pass-15 pass-16 pass-17 pass-18 pass-19 pass-20 pass-11 分析源代码 &#xff0c;发现上传文件的存放路径可控 if(isset($_POST[submit])){$ext_arr array(jpg,png,gif);$file_ext substr($_FILES[upload_file][name],st…

【音视频】视频基本概念

一、视频的基本概念 1.1 视频码率&#xff08;kb/s&#xff09; 视频码率是指视频文件在单位时间内使用的数据流量&#xff0c;也叫码流率。码率越大&#xff0c;说明单位时间内取样率越大&#xff0c;数据流进度也就越高 1.2 视频帧率&#xff08;fps&#xff09; 视频帧率…

Sqlserver还原备份文件时提示缺少日志文件

Sqlserver还原备份文件时提示缺少日志文件 解决方案&#xff1a;

《2025年软件测试工程师面试》消息队列面试题

消息队列 消息队列&#xff08;Message Queue&#xff0c;简称 MQ&#xff09;是一种应用程序之间的通信方法。 基本概念 消息队列是一种先进先出&#xff08;FIFO&#xff09;的数据结构&#xff0c;它允许一个或多个消费者从队列中读取消息&#xff0c;也允许一个或多个生产者…

前端基础之vuex

是一个专门在Vue中实现集中式状态(数据)管理的一个Vue插件&#xff0c;对vue应用中多个组件的共享状态进行集中式管理(读或写)&#xff0c;也是一种组件间通信的方式&#xff0c;适用于任意组件间的通信 什么时候使用vuex&#xff1f; 1.多组件依赖同一状态 2.来自不同组件的行…

Node.js二:第一个Node.js应用

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 创建的时候我们需要用到VS code编写代码 我们先了解下 Node.js 应用是由哪几部分组成的&#xff1a; 1.引入 required 模块&#xff1a;我们可以使用 requi…