LINUX--基础IOLINUX--基础IO

简介: LINUX--基础IO

什么是IO?

I input;O output 以我目前的理解,我认为IO就是“写进去”与“拿出来”的过程。

比如说文件IO,就是把内容写进文件里面,得到文件里的内容的过程。

那么,往文件内容写东西,得到文件内容,有哪些“手段”呢?

先来看C语言为我们提供了哪些手段(函数接口)

fopen--打开文件

函数原型:

参数1:filename 可以使用绝对路径和相对路径(什么是绝对路径和相对路径?)

参数2:mode 以哪种方式打开文件

具体有以下几种打开方式

fwrite--对文件进行写入

参数原型:

参数1:ptr 指向任意数据类型的指针,他所指向的内容将写入stream中去

参数2:size,ptr指向数据类型的大小

参数3:count  写多少个size大小的元素到stream中去

参数4:stream FILE*类型的指针(FILE其实是一个结构体类型)

fread--读取文件内容

函数原型:

参数与fwrite类似,只是将stream里的文件内容写到ptr中

fclose--关闭文件

参数 stream要关闭的文件名

操作实例:对一个文件进行写入

可以看到,内容已经被写入到test.txt中去了

操作实例,对一个文件进行读取

运行结果:

成功读取到了test.txt中的内容

接下来再看看LINUX提供的系统调用接口

在此之前,先说明一下LINUX提供的系统接口与C语言提供的接口之间的关系,简单说就是,C语言提供的接口是对系统接口的的封装,即C接口的底层是使用系统接口实现的

open--打开文件

函数原型:

参数1 pathname 文件名(相对路径/绝对路径)

参数2 flags 以哪种方式打开文件(O_WRONLY(只读),O_CREAT(不存在则创建文

件)O_TRUNC(每次打开文件时清空文件)等等)

参数3 mode 设置文件权限

返回值:返回一个文件描述符(fd),它是非负的整数,被用于read、write等系统调用。成功返回的文件描述符会是最小的、当前没有为该进程所打开的文件描述符

write--对文件进行写入

函数原型:

参数1 fd  文件描述符

参数2 buf  一个指向任意数据类型的指针,其指向内容将被写到文件中去

参数3 count  写count个字节到文件中去

返回值:成功返回写入的字节个数,失败返回-1.

read--读取文件

函数原型:

将fd所在文件中至多count个字节存储到buf指针所指向空间中去

参数与write一致返回值:成功返回成功读到的字节个数,失败返回-1

close--关闭文件

函数原型

使文件描述符不再指代一个文件

返回值:成功返回0 失败返回-1

操作实例,对一个文件进行写入

运行结果:log1.txt中成功写入了msg的内容

操作实例,读取一个文件的内容

运行结果:文件内容成功存入rdmsg中

文件描述符--fd(file descriptor)

是谁打开了文件?是进程。为了让进程知道哪些文件属于自己,所以要建立进程与文件之间的关系,因此在PCB中会存在一个指向files_struct的结构体类型指针,该结构体存有一个

struct files* fd array[32]的数组,数组元素是指向文件的指针,而fd就是这些数组的下标。

可以说,文件描述符是用户使用文件的媒介。

值得注意的是,当一个进程启动时,OS会自动为它打开0、1、2下标所对应的IO流,他们分别是标准输入流、标准输出流、标准错误流。也就是进程不用打开这三个文件,也可以向他们进行写入/读取。

文件描述符分配规则

优先分配进程最近的,未被使用的数组下标作为文件描述符。

验证:

运行结果:

fd的返回值是3,符合预期,因为0 1 2下标已经分配给了键盘、显示器、显示器文件

利用fd完成文件重定向

什么是文件重定向:更改struct files* fd array[32]数组元素的指向。

如何实现文件重定向:以输出重定向为例

运行结果:

分析:close(1)的作用是取消进程与显示器文件的联系,可以理解fd_array[1]不再指向显示器文件,随后我们又创建了log.txt文件,根据文件描述符分配规则,fd_array[1]指向了log.txt,因此当我们再向1号下标所指代的文件中写入,直接写入log.txt而不是显示器文件

追加重定向:

每次进行写入时文件偏移量都位于文件的末尾

输入重定向:

将写入键盘文件的数据写入到log.txt中

FILE与fd

在前面的接口中,有C提供的,例如fopen,fwrite等,返回值是FILE*,也有LINUX提供的系统调用接口,例如open,write等,返回值是fd。C提供的接口其实是对系统调用接口的封装(例如fopen底层一定封装了open),方便用户使用。所以FILE是C接口层面的概念,fd是系统调用层面的概念。

什么是FILE

  1. FILE是一个结构体,要想成功访问文件,那么FILE结构体中就一定有存储fd的变量。
  2. FILE是结构体_IO_FILE的重命名

如下是_IO_FILE的成员变量,_fileno是用于存放fd的

C缓冲区与内核缓冲区

什么是缓冲区?

缓冲区,是一块内存的空间,是用于存放数据的地方

有哪些缓冲区?

就我目前而知,缓冲区有用户级缓冲区(C语言提供的)以及内核缓冲区。

这两个缓冲区之间的关系?

用户先将数据刷新到用户级缓冲区,再等待合适的时机,将这些数据刷新到内核缓冲区。OS再根据自己的刷新策略将数据刷新到外设。

如何维护C缓冲区?

依靠FILE结构体,FILE结构体中有大量缓冲区相关字段

谈谈刷新时机

(访问文件,就是访问外设,访问显示器文件,就是访问显示器)

1.行刷新,碰到\n就刷新数据到内核缓冲区,通常是向显示器文件中写入

2.全缓冲,写满缓冲区才刷新数据到内核缓冲区,通常是向普通文件中写入

3.无刷新,直接刷新,不用等待,常见的是向stderr中写入

验证:

1.行刷新

按照我们上面说的,前面三个printf的内容会被刷到内核缓冲区,并打印到显示器上,后面三个不会

运行结果:

与预期一致

值得注意的是,我们在代码的末端加入了一个close(1),也就是说我这个进程无法再访问1所指向的文件,去掉close(1)会发生什么?            

运行结果:

我们发现,所有的数据都被打印出来了,由此可以得到一个结论:进程退出时,缓冲区的文件也会被刷新到内核缓冲区中(前提是缓冲区文件向内核缓冲区刷新的通道没有被关闭,也就是close(1))

全缓冲

运行结果:

我们发现log.txt的大小是0,这足以说明:数据现在仍然存在于用户级缓冲区当中,还没有刷新到内核缓冲区,那么log.txt的大小自然是0

现在,我们让数据多写一些到用户级缓冲区当中去

运行结果

用户级缓冲区的数据果然刷新到了磁盘文件上。

无刷新

运行结果:

可以看到,我们既没有加\n,写入的数据大小也不是很大,但它仍然成功写入了stderr中(也就是显示器文件),这足以说明往stderr中写入时采用的是无刷新策略。

文件系统

大部分文件为普通文件,他们存储在磁盘上,当进程调用文件时才会被加载到内存之中。

所以磁盘是什么呢?

  1. 磁盘是大容量的存储介质,既可以输入又可以输出
  2. 磁盘的组成:盘面、磁头
  3. 盘面上:扇区、磁道
  4. 柱面

(上图转引自:Linux基础IO_读写文件时,更改起始读写位置的函数名字-CSDN博客)

如何对存储在磁盘上的文件进行定位

1.确定文件所在磁头的那一个盘面

2.确定文件所在扇面的磁道

3.确定文件所在磁道的扇区

这就是CHS定位法

如何看待磁盘呢?

扇区是访问磁盘的基本单位(512byte),由于磁盘本质上是由一个一个的扇区组成的,因此整个磁盘可以被看作是一个很大的数组。

操作系统是管理软硬件的软件,所以他也会对磁盘做管理。由于磁盘空间太大,所以操作系统会对磁盘进去分组管理。

分区组成:是将磁盘分成若干个分区,每个分区由启动块,和块组组成

块组组成:

(以下六点转载自:Linux基础IO_读写文件时,更改起始读写位置的函数名字-CSDN博客)

  1. Super Block: 存放文件系统本身的结构信息。记录的信息主要有:Data Block和inode的总量、未使用的Data Block和inode的数量、一个Data Block和inode的大小、最近一次挂载的时间、最近一次写入数据的时间、最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。
  2. Group Descriptor Table: 块组描述符表,描述该分区当中块组的属性信息。
  3. Block Bitmap: 块位图当中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用。
  4. inode Bitmap: inode位图当中记录着每个inode是否空闲可用。
  5. inode Table: 存放文件属性,即每个文件的inode。
  6. Data Blocks: 存放文件内容。以块为基本单位(4KB)

何为inode?

inode存放了一个文件的所有属性,它是一个结构体,大小是128字节,其中有两个成员变量为我们比较关注,inode number即inode编号,int blocks[NUM]即文件内容存储于哪些块中。

为什么要有inode编号?

一个文件一个inode,文件数量太多,也就是inode也太多,所以需要inode编号来对inode进行区分。

inode编号的作用?

主要用于查找一个文件的inode,找到了inode就知道了文件的属性,知道了文件的内容存储在哪里,也就可以对文件进行修改。可是我们查找文件都是使用文件名的,并不知道inode编号。

目录会为我们解决这个问题——

Linux下一切皆文件,目录也是文件,它也有自己的inode结构和存储数据的块,它存储的就是当前目录下文件名与inode编号的映射关系。

如何删除一个文件?

inode bitmap与block bitmap置为0

如何新建一个文件?

根据目录的inode编号,找到当前分区,查询inode bitmap,为文件创建一个inode结构,并将文件属性填充进inode,将该文件的文件名和inode指针添加到目录文件的数据块中。

软/硬链接

什么是软链接:

软链接其实是指创造一个文件,使它存储的是另一个文件的绝对路径,该文件有自己独立的inode,这相当于windows操作系统中创建快捷方式的行为

如何使用软链接:

1.创建软链接(对softlink创造一个softlink-s的软链接)

运行结果一致

inode编号不同

软链接文件和源文件具有关联性,即源文件删除,软链接文件就无效了

什么是硬链接:

相当于对文件取别名,即inode编号相同,存储数据相同,但文件名不同

如何使用硬链接:

运行结果:

inode一致,文件大小一致

硬链接文件 与源文件具有独立性 ,既然一方被删除也不会影响另一方    

硬链接数:

硬链接数变成了2,对具有相同inode编号的文件来说,具有几个文件名就有几个硬链接数

特别的,对于目录来说,由于它的硬链接数默认是2,这是由于目录下有隐藏的.文件,它与目录有着相同的inode编号

以上便是对linux基础io的知识总结,各位大佬若有指教,欢迎在评论区留言,俺不胜感激!

相关文章
|
2月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
95 0
|
2月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
96 1
Linux C/C++之IO多路复用(aio)
|
4月前
|
缓存 安全 Linux
Linux 五种IO模型
Linux 五种IO模型
|
2月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
33 0
Linux C/C++之IO多路复用(poll,epoll)
|
4月前
|
小程序 Linux 开发者
Linux之缓冲区与C库IO函数简单模拟
通过上述编程实例,可以对Linux系统中缓冲区和C库IO函数如何提高文件读写效率有了一个基本的了解。开发者需要根据应用程序的具体需求来选择合适的IO策略。
35 0
|
4月前
|
存储 IDE Linux
Linux源码阅读笔记14-IO体系结构与访问设备
Linux源码阅读笔记14-IO体系结构与访问设备
|
5月前
|
Linux 数据处理 C语言
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(下)
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(下)
80 0
|
5月前
|
Linux 编译器 C语言
【Linux】基础IO----理解缓冲区
【Linux】基础IO----理解缓冲区
76 0
【Linux】基础IO----理解缓冲区
|
5月前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
249 2
|
6月前
|
Linux 编译器 C语言
【Linux】基础IO_4
【Linux】基础IO_4
31 3