Linux知识回顾总结----基础IO

        

目录

1. 理解“文件”

        1.1 文件的定义

2. 回顾 C 语言的文件操作

        2.1 文件操作

2.2 实现cat

2.3  可以实现打印的几种方式

 3. 系统文件的IO

 3.2 使用系统的接口

 3.3 内部的实现

 3.4 重定向

4. 文件系统的内核结构

5. 缓冲区

        5.1 是什么

5.2 为什么

5.3 有什么

5.4 见见缓冲区

    6. 其他知识汇总

 6.1 FILE

 6.2 自己实现lib


 

1. 理解“文件”

    1.1 文件的定义

      文件具有狭义与广义的定义。狭义的理解:存放在磁盘里面的文件。因为狭义的文件定义是在磁盘上的所以对于一切文件的操作就是对于外设的操作,就是对于外设的输入与输出简称为IO。

        广义的文件:在 Linux 下一切皆文件。为什么一切皆文件呢?如何理解?Linux 认为一切皆文件也就是说操作系统将所有对于外设的操作都看成是对于文件的操作,这个的原因可以方便进程对于低下所有的不同外设进行管理(类似于多态的思想,结构体改变指针的指向)。尽管不同的外设的内部的具体方法以及函数不同但是都具有相同类型的函数,通过欺骗进程认为一切皆文件,然后达到用户层同时认为一切皆文件。这个解释通过后面看Linux的内核结构会更好理解。

2. 回顾 C 语言的文件操作

       2.1 文件操作

        通过回顾C的具体的文件的接口操作,可以帮我们更好的理解操作系统的IO操作。下面展示一个文件的打开,关闭,追加,写入,读文件操作。

    FILE* fd = fopen("myfile.txt", "w");if(!fd){printf("fopen file\n");}//要写入的需要使用write,先定义一个msg字符产const char* msg = "hello word\n";fwrite(msg, strlen(msg), 1, fd);
//展示读的操作FILE* fd = fopen("myfile.txt", "r");if(!fd){printf("fopen file\n");}//读需要使用buffer//printf("hello word\n");const char* msg = "hello word\n";char buffer[1024];// fread 参数含义:数据存放区,按照几个字节读取,读多少,从哪里读ssize_t s = fread(buffer, 1, 2, fd);if(s > 0){buffer[s] = 0; // 最后读取的实际个数会存放在s中printf("%s", buffer);}
    //追加操作FILE* fd = fopen("myfile.txt", "a");if(!fd){printf("fopen file\n");}const char* msg = "hello word\n";fwrite(msg, strlen(msg), 1, fd);fclose(fd);

2.2 实现cat

        通过对于上面代码的简单修改即可实现 cat 指令的具体内容。

#include <stdio.h>
#include <cstring>
int main(int argc,char* argv[])
{if(argc < 2){printf("you need write right formote:\n ./hello xxxx\n");return -1;}//使用读的方式进行打开FILE* fd = fopen(argv[1], "r");char buffer[1024];while(1){//对于不确定的就要使用循环实现最后使用fopf进行判断是否读到了最后int s = fread(buffer, 1, sizeof(buffer), fd);if(s > 0){buffer[s] = 0;printf("%s", buffer);}if(feof(fd)){//读到最后不能再读了就要进行返回break;}}fclose(fd);return 0;
}

2.3  可以实现打印的几种方式

        再屏幕上打印有三个常见的方式 fwrite, printf, fprintf。下面是实现的代码。

	const char* msg = "hello djx\n";fwrite(msg, strlen(msg), 1, stdout);printf("%s", msg);fprintf(stdout, msg);

 3. 系统文件的IO

     3.1 标志位,文件描述符

        标志位可以理解为使用位图表示的一个数,通过宏定义的方式变成易读的命名。这个标识位可以用来实现系统的打开以及其他的操作。

                文件描述符:专门用来表示打开文件的数组下标,每打开一个文件就会形成一个文件描述符从3开始(因为os会自动占用0,1,2 表示标准输入,标准输出,标准错误)如果修改文件描述符指向(通过调用 dup2 这个函数)可以实现重定向。

 3.2 使用系统的接口

        系统的接口主要是有 open, wirte, read 等。一下是详细的解释以及代码。

    umask(0);//写size_t fd = open("myfile", O_CREAT | O_WRONLY | O_TRUNC, 0666);const char* msg = "I am DJX\n";write(fd, msg, strlen(msg));//追加 size_t fd = open("myfile",  O_APPEND | O_WRONLY, 0666);const char* msg = "I am DJX\n";write(fd, msg, strlen(msg));close(fd);//读size_t fd = open("myfile",  O_RDONLY , 0666);char buffer[1023];ssize_t s = read(fd, buffer, 123);if(s > 0){buffer[s] = 0;printf("%s", buffer);}close(fd);

        需要注意的几点是可以看到,我们主要看的是文件描述符 fd,然后我是以2进制,还是其他的读法继续写入,或者是读出都不关心,其实我们的语言层就是封装了这些接口实现的 fwrte 等 。后面会有代码来进行实现我自己封装一个stdid。下图表示的就是r,w等对应的文件的打开方式。

 

 3.3 内部的实现

        我们说了这么多,其实在Linux的代码中对于这么多打开的文件是对于进行了先描述,再组织的方式对于打开的文件文件进行了管理。在 task_struct 中具有 file_struct ,在 file_struct 中存放着文件描述表,通过文件描述表就可以找到已经打开了的 file。

3.4 重定向

        有输出重定向 > >>,一个是覆盖式,另外一个为追加。 > 就是使用 O_CREAT | O_WRONLY | O_TRUNC 结合 dup2 将标准输出定位到另外一个文件当中(本来应该打印到显示器上,将 fd 新的文件描述符覆盖到旧的文件描述符的上面, dup2(fd, 1))。

        也就是说,我本来 printf是要打印到文件描述的 1 上面(也就是打印到显示器上面)但是我现在改变了指向就让其指向了 fd 这个文件描述符从而把东西打印到了文件里面,所以打开的文件需要以写的方式进行打开。

        输入重定向:本来是要从 stdin 文件描述符为 0, 这个里面进行读入数据,但是改变一下,让另外的一个文件指向变成 0,也就是从这个文件里面读到内容放到相应的位置,进行对应的操作。使用函数为dup2(fd, 0),打开的文件的方式为读的方式打开从 stdin 中读入,但是需要改变文件描述符;

4. 文件系统的内核结构

       在内核当中,可以看到 file 这个接口里面有一个 struct file_operations 的结构体,这个结构体定义了一个结构体指针,在这个结构体里面可以看到具有许多的函数指针,指向了不同设备的相同类型的操作,但是每个函数的具体操作是不相同的。类似于多态。     

        

struct file 
{struct inode *f_inode; /* cached value */const struct file_operations *f_op;...atomic_long_t f_count; // 表⽰打开⽂件的引⽤计数,如果有多个⽂件指针指向它,就会增加f_count的值。unsigned int f_flags; // 表⽰打开⽂件的权限fmode_t f_mode; // 设置对⽂件的访问模式,例如:只读,只写等。所 有的标志在头⽂件<fcntl.h> 中定义loff_t f_pos; // 表⽰当前读写⽂件的位置...
}struct file_operations 
{struct module *owner;//指向拥有该模块的指针;loff_t (*llseek) (struct file *, loff_t, int);//llseek ⽅法⽤作改变⽂件中的当前读/写位置, 并且新位置作为(正的)返回值.ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//⽤来从设备中获取数据. 在这个位置的⼀个空指针导致 read 系统调⽤以 -EINVAL("Invalid argument") 失败. ⼀个⾮负返回值代表了成功读取的字节数( 返回值是⼀个"signed size" 类型, 常常是⽬标平台本地的整数类型).ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//发送数据给设备. 如果 NULL, -EINVAL 返回给调⽤ write 系统调⽤的程序. 如果⾮负,返回值代表成功写的字节数.ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long,loff_t);//初始化⼀个异步读 -- 可能在函数返回前不结束的读操作.ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsignedlong, loff_t);//初始化设备上的⼀个异步写.int (*readdir) (struct file *, void *, filldir_t);//对于设备⽂件这个成员应当为 NULL; 它⽤来读取⽬录, 并且仅对**⽂件系统**有⽤....
}

  ​​​​​​

        这样实现的目的前面也已经说过了,进程对于设备的管理也就变成了对于文件的管理,所以一切皆文件。更加深层次一点说就是,我们通过 file 这个结构体就可以调用 Linux 下面的所有资源。这便是一切皆文件!!!!

5. 缓冲区

        5.1 是什么

         缓冲区简单可以理解为:当我们进行打印一个东西的时候相关的数据会存放在一个区域中,当这个区域符合了一定的条件(比如缓冲区满了放不下了不要刷新,强制刷新,进程退出,满足了刷新条件) 就会将相关的内容给到os(操作系统),让os完成相应的操作。

         官方的解释为:内存中的一部分,也就是会预留出这一部分空间来进行数据的输入与输出,起到了一个缓冲的作用。缓冲区根据其对应的是输⼊设备还是输出设备,分为输⼊缓冲区和输出缓冲区。本章主要探讨以及上面所说的就是输出缓冲区。

5.2 为什么

        它存在的意义就在于可以提高计算机的运行效率!

        在理解他是如何如何提高效率之前,我们需要直到 os 去继续工作(无论是进行打印,去磁盘中寻找文件)任何操作都会消耗资源,降低 计算机的速度。然后 缓冲区 就可以解决这个样的问题。举个例子:1. 我们需要打印一份东西,如何有一条打印语句就进行打印的话,他的效率远远不如我们将所有的要打印信息都放到一个内存中,然后对于这个内存进行统一的打印。 2. 向磁盘的文件读取数据的时候我们一次性读取大量的数据,然后放回到 os 中,可以极大的提高效率。

        我们还可以把它理解为一个快递驿站,将东西从一个地方运送到另外一个地方的时候,统一打包以及集中的继续邮寄可以极大的提高我们想要邮寄东西需求。操作系统设计缓冲区的目的也在于此。

5.3 有什么

        根据类型的分类可以分为:行缓冲区(打印到\n 的时候就进行打印,调用系统), 全缓冲区(满整个缓冲区后才进⾏I/O系统调⽤操作。),⽆缓冲区(标准I/O库不对字符进⾏缓存,直接调⽤系统调⽤。)

        下列特殊情况也会引发缓冲区的刷新:1. 缓冲区满时;2. 执⾏flush语句 3. 进程结束

 5.4 见见缓冲区

        下面一段代码,会有不一样的结果请看。

#include <stdio.h>
#include <cstring>
#include <unistd.h>
int main()
{const char* msg1 = "hello print\n";const char* msg2 = "hello fwrite\n";const char* msg3 = "hello write\n";printf("%s", msg1);fwrite(msg2, strlen(msg2), 1, stdout);write(1, msg3, strlen(msg3));fork();
}

        最后跑出来结果为,可以看到 当对于程序的内容进行重定向放到myfile中时,首先使用的是write因为这是一个系统的函数,直接放到了显示器上面,然后后面的 print 和 fwrite 打印了两次是因为当我们进行重定向的时候会修改文件的刷新方式,从行缓冲(因为有 \n )变成了 文件缓冲。也就是这个 p 和 fwrite 的打印的内容都在缓冲区当中,然后子进程(对于父进程代码的一份拷贝)结束了讲缓冲区的内容刷新到了操作系统进行打印,然后是父进程结束刷新缓冲区,进行打印。

         但是当我们进行普通的使用的时候(也就是运行程序的时候显示的就还是行缓冲)遇到 \n 就会进行打印。 

       

        将没有fork的重定向到文件当中。会先使用系统函数write。

        这样的例子就很好的说明了缓冲区对于磁盘文件的使用,以及在进行打印时候的作用。 

    6. 其他知识汇总

        6.1 FILE

             它是c语言库中使用文件操作的数据类型,那么它究竟是什么? 没错他就是一个结构体,里面进行封装了系统调用函数需要的文件描述符 fd。原码的结构如下:

{#define _IO_file_flags _flags//缓冲区相关/* The following pointers correspond to the C++ streambuf protocol. *//* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr; /* Current read pointer */char* _IO_read_end; /* End of get area. */char* _IO_read_base; /* Start of putback+get area. */char* _IO_write_base; /* Start of put area. */char* _IO_write_ptr; /* Current put pointer. */char* _IO_write_end; /* End of put area. */char* _IO_buf_base; /* Start of reserve area. */char* _IO_buf_end; /* End of reserve area. *//* The following fields are used to support backing up and undo. */char *_IO_save_base; /* Pointer to start of non-current get area. */char *_IO_backup_base; /* Pointer to first valid character of backup area*/char *_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno; //封装的⽂件描述符#if 0int _blksize;#elseint _flags2;#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small. */#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/* char* _save_gptr; char* _save_egptr; */_IO_lock_t *_lock;#ifdef _IO_USE_OLD_IO_FILE}

6.2 自己实现lib

        里面需要实现 fwrite(使用系统 open ,以写入,或者是追加的方式进行打开), fopen,fclose, fflush。下章对于这部分代码进行说明。

#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>static MyFile *BuyFile(int fd, int flag)
{MyFile *f = (MyFile*)malloc(sizeof(MyFile));if(f == NULL) return NULL;f->bufferlen = 0;f->fileno = fd;f->flag = flag;f->flush_method = LINE_FLUSH;memset(f->outbuffer, 0, sizeof(f->outbuffer));return f;
}MyFile *MyFopen(const char *path, const char *mode)
{int fd = -1;int flag = 0;if(strcmp(mode, "w") == 0){flag = O_CREAT | O_WRONLY | O_TRUNC;fd = open(path, flag, 0666);}else if(strcmp(mode, "a") == 0){flag = O_CREAT | O_WRONLY | O_APPEND;fd = open(path, flag, 0666);}else if(strcmp(mode, "r") == 0){flag = O_RDWR;fd = open(path, flag);}else{//TODO}if(fd < 0) return NULL;return BuyFile(fd, flag);
}
void MyFclose(MyFile *file)
{if(file->fileno < 0) return;MyFFlush(file);close(file->fileno);free(file);
}
int MyFwrite(MyFile *file, void *str, int len)
{// 1. 拷贝memcpy(file->outbuffer+file->bufferlen, str, len);file->bufferlen += len;// 2. 尝试判断是否满足刷新条件!if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen-1] == '\n'){MyFFlush(file);}return 0;
}
void MyFFlush(MyFile *file)
{if(file->bufferlen <= 0) return;// 把数据从用户拷贝到内核文件缓冲区中int n = write(file->fileno, file->outbuffer, file->bufferlen);(void)n;fsync(file->fileno);file->bufferlen = 0;
}

          以上是对于IO的回顾。这个文章用于我的学习记录,如果是有其他的错误还请批评指正。如果对你有帮助还请给我点个赞👍👍👍。     

                

         

 

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

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

相关文章

网络:基础概念

网络&#xff1a;基础概念 在计算机发展过程中&#xff0c;最开始每个计算机时相互独立的&#xff0c;后来人们需要用计算机合作处理任务&#xff0c;这就牵扯到了数据交换&#xff0c;所以最开始的网络就诞生了。一开始&#xff0c;网络都是局域网LAN&#xff0c;后来技术成熟…

图像识别边缘算法

文章目录1. 基本概念2. 边缘检测原理边缘类型&#xff1a;3. 常见边缘检测算法3.1 Sobel算子3.2 Canny边缘检测3.3 Laplacian算子4. Canny边缘检测详细流程流程图示例&#xff1a;详细步骤说明&#xff1a;5. 边缘检测算法比较6. 参数调优建议Canny边缘检测参数&#xff1a;Sob…

【Java Web实战】从零到一打造企业级网上购书网站系统 | 完整开发实录(终)

&#x1f9ea; 测试与质量保证 &#x1f50d; 全方位测试体系 我建立了企业级的全方位测试体系来确保系统质量&#xff1a; &#x1f9ea; 测试金字塔模型 #mermaid-svg-u4I8UuUAyxJVjcqs {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill…

QT开发---网络编程下

HTTP协议 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网上应用最为广泛的协议之一&#xff0c;用于客户端和服务器之间的通信。默认端口80&#xff0c;传输层使用的是TCP协议特点无连接&#xff1a;HTTP协议是无连接的&#xff…

mac 苹果电脑 Intel 芯片(Mac X86) 安卓虚拟机 Android模拟器 的救命稻草(下载安装指南)

引言&#xff1a; 还在为你的Intel芯片MacBook&#xff08;i5, i7, i9等&#xff09;找不到合适的安卓虚拟机而发愁吗&#xff1f;随着Apple Silicon (M1/M2/M3) 芯片的普及&#xff0c;大量优秀的安卓模拟器&#xff08;如Android Studio自带的模拟器、网易MuMu等&#xff09;…

C语言:顺序表(上)

C语言&#xff1a;顺序表&#xff08;上&#xff09; 1.顺序表的介绍 2.顺序表的实现 1.顺序表的介绍 线性表是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在…

GPT - 5被曝将在8月初发布!并同步推出mini、nano版

据《TheVerge》最新报道&#xff0c;OpenAI 正准备在 8 月发布新版本旗舰大模型 GPT-5&#xff0c;如果顺利的话发布节点最早会在 8 月初。同时&#xff0c;下个月发布 GPT-5 时&#xff0c;还会一并推出 mini&#xff08;小型&#xff09;和 nano&#xff08;微型&#xff09;…

【Linux操作系统】简学深悟启示录:Linux环境基础开发工具使用

文章目录1.软件包管理器yum2.Linux编辑器vim2.1 三模式切换2.2 正常模式2.3 底行模式2.4 可视化模式2.5 vim 配置3.Linux编译器gcc/g3.1 预处理3.2 编译3.3 汇编3.4 连接3.5 函数库4.Linux自动化构建工具Makefile5.Linux调试器gdb希望读者们多多三连支持小编会继续更新你们的鼓…

八大神经网络的区别

神经网络名称全称/修正名称主要作用核心特点典型应用场景CINICNN&#xff08;卷积神经网络&#xff09;处理图像、视频等空间数据&#xff0c;提取局部特征。使用卷积核、池化操作&#xff1b;擅长平移不变性。图像分类、目标检测、人脸识别。RINIRNN&#xff08;循环神经网络&…

从 SQL Server 到 KingbaseES V9R4C12,一次“无痛”迁移与深度兼容体验实录

#数据库平替用金仓 #金仓产品体验官 摘要&#xff1a;本文以体验项目案例为主线&#xff0c;从下载安装、数据类型、T-SQL、JDBC、性能基准、踩坑回退六大维度&#xff0c;全景验证 KingbaseES V9R4C12 对 SQL Server 的“零改造”兼容承诺&#xff1b;并给出 TPCH 100G 性能对…

EasyPlayer播放器系列开发计划2025

EasyPlayer系列产品发展至今&#xff0c;已经超过10年&#xff0c;从最早的EasyPlayer RTSP播放器&#xff0c;到如今维护的3条线&#xff1a;EasyPlayer-RTSP播放器&#xff1a;Windows、Android、iOS&#xff1b;EasyPlayerPro播放器&#xff1a;Windows、Android、iOS&#…

通信名词解释:I2C、USART、SPI、RS232、RS485、CAN、TCP/IP、SOCKET、modbus等

以下内容参考AI生成内容1. I2C&#xff08;Inter-Integrated Circuit&#xff0c;集成电路间总线&#xff09;定义&#xff1a;由飞利浦&#xff08;现恩智浦&#xff09;开发的短距离串行通信总线&#xff0c;用于芯片级设备间的低速数据传输。工作原理&#xff1a;采用两根信…

bash的特性-常见的快捷键

一、前言在 Linux Shell 编程和日常使用中&#xff0c;Bash 快捷键 是提升命令行操作效率的利器。熟练掌握这些快捷键&#xff0c;不仅可以节省大量输入时间&#xff0c;还能显著提升你在终端环境下的操作流畅度。本文将带你全面了解 Bash 中常用的快捷键&#xff0c;包括&…

【Java Web实战】从零到一打造企业级网上购书网站系统 | 完整开发实录(三)

&#x1f3a8; 核心功能设计 &#x1f464; 用户管理系统 用户管理是整个系统的基础&#xff0c;我设计了完整的用户生命周期管理&#xff1a; &#x1f510; 用户注册流程 #mermaid-svg-D0eUHWissjNhkqlB {font-family:"trebuchet ms",verdana,arial,sans-serif;fon…

uniapp input 聚焦时键盘弹起滚动到对应的部分

实现效果代码如下<template><view idapp><view class"aa"></view><iconfont name"left"></iconfont>姓氏&#xff1a;<input style"background-color: antiquewhite;" type"text" v-model&quo…

【基础篇三】WebSocket:实时通信的革命

目录 一、传统HTTP的"痛点"分析 1.1 HTTP的单向通信模式 1.2 "实时"效果的痛苦尝试 ​编辑 1.3 性能对比分析 二、WebSocket 协议详解 2.1 WebSocket是什么&#xff1f; ​编辑 2.2 WebSocket的核心特性 2.2.1 全双工通信&#xff08;Full-Duple…

设计模式(十八)行为型:中介者模式详解

设计模式&#xff08;十八&#xff09;行为型&#xff1a;中介者模式详解中介者模式&#xff08;Mediator Pattern&#xff09;是 GoF 23 种设计模式中的行为型模式之一&#xff0c;其核心价值在于通过引入一个中介者对象来封装一组对象之间的交互&#xff0c;从而降低对象间的…

Upload-Labs通关全攻略详细版

前端校验绕过:pass 01 两种思路:1.通过抓包,修改后缀 2.前端禁用js绕过前端后缀检验 首先写一个木马,改为图片格式GIF89a<?php eval($_POST[cmd])?>抓包之后改为PHP格式: 使用蚁剑连接木马,第一次尝试一直是返回数据为空,原因是没有链接到木马,于是寻找木马地址…

C#观察者模式示例代码

using System; using System.Collections.Generic; using System.Threading;namespace RefactoringGuru.DesignPatterns.Observer.Conceptual {// Observer观察者 也可以叫做订阅者 subscriberspublic interface IObserver{// Receive update from subject// 接收来自主题的更新…

电子电子架构 --- 软件项目的开端:裁剪

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…