当前位置: 首页 > news >正文

Linux基础IO

对文件的理解:

文件 = 文件内容 + 属性(即数据)

linux认为,一切皆文件。

站在系统的角度而言,能够被input读取或者能够被output写出的设备就叫做文件。

狭义上的文件:普通的磁盘文件

广义上的文件:显示器,键盘,网卡,声卡,显卡等几乎所有的外设,都可以称为文件。

当前路径:当一个程序运行起来的时候,每一个进程都会记录自己当前所处的工作路径,所谓当前路径就是指,该进程所处的工作路径。

一、C标准库IO操作

1.1、写文件


#include <stdio.h>
#include <string.h>
int main()
{//使用只写的方式打开文件,fp为 FILE* 类型指针FILE *fp = fopen("myfile", "w");//如果打开失败,打印错误	if(!fp){printf("fopen error!\n");}const char *msg = "hello bit!\n";int count = 5;while(count--){//使用fwirte将msg写入文件,该接口为c标准库接口fwrite(msg, strlen(msg), 1, fp);}//写入完成后,关闭文件fclose(fp);return 0;
}

1.2、读文件

#include <string.h>
int main()
{	//使用只读的方式打开文件,fp为 FILE* 类型指针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);rerturn 0;
}

1.3、输出信息到显示器

#include <stdio.h>
#include <string.h>
int main()
{const char *msg = "hello fwrite\n";//使用fwirte将字符串打印到stdout中fwrite(msg, strlen(msg), 1, stdout);printf("hello printf\n");fprintf(stdout, "hello fprintf\n");return 0;
}

1.4、stdin & stdout & stderr

c语言进行运行的时候会默认打开三个输入输出流:stdin ,stdout ,stderr

这三个流的返回值都是FILT* 类型的文件指针

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

1.5、总结

在这里插入图片描述

打开文件的方式如上所示

二、系统文件IO操作

2.1、写入操作

2.1.1、关于open操作的解释

在这里插入图片描述

  1. pathname:要打开的文件的路径名。
  2. flags:打开文件的标志,指定了打开文件的方式。常用的标志包括:
    • O_RDONLY:只读模式,打开文件用于读取。
    • O_WRONLY:只写模式,打开文件用于写入。
    • O_RDWR:读写模式,打开文件用于读取和写入。
    • O_CREAT:如果文件不存在,则创建文件。
    • O_TRUNC:如果文件存在且以写入方式打开,则清空文件内容。
    • O_APPEND:写入时追加到文件末尾。
  3. mode:如果使用了O_CREAT标志,指定新建文件的权限。在大多数情况下,这个参数可以被忽略。
2.1.2、写入代码
#include<stdio.h>
#include<sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{//umask为权限掩码umask(0);//open函数参数:第一个为文件路径,第二个参数为打开文件的方式,第三个参数指定文件的权限int fd = open("myfile", O_WRONLY|O_CREAT, 0644);if(fd < 0){perror("open");return 1;}int count = 5;const char *msg = "hello bit!\n";int len = strlen(msg);while(count--){write(fd, msg, len);//fd: 后面讲, msg:缓冲区首地址, len: 本次读取,期望写		入多少个字节的数据。 返回值:实际写了多少字节数据}close(fd);return 0;
}

2.2、读文件

2.2.1、关于read操作

在这里插入图片描述

  1. fd:文件描述符,指定要读取的文件。
  2. buf:用于存储读取数据的缓冲区的指针。
  3. count:要读取的字节数。
2.2.2、代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}const char *msg = "hello bit!\n";char buf[1024];while(1){ssize_t s = read(fd, buf, strlen(msg));//类比writeif(s > 0){printf("%s", buf);}else{break;}}close(fd);return 0;
}

2.3、open函数返回值

  • 上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。
  • 而, open close read write lseek 都属于系统提供的接口,称之为系统调用接口

在这里插入图片描述

三、文件描述符(fd)

3.1、fd的引入

在这里插入图片描述

如上图代码,可看到如下结果:

在这里插入图片描述

为什么文件描述符是从3开始的,理论上应该是从0开始,实际上0,1,2已经被stdio,stdout,stderr占用。

在这里插入图片描述

在这里插入图片描述

而这些open,fopen等等的接口的返回值都是一个FILE* 类型的,那么FILE*是什么呢,通过查阅底层代码可看到FILE其实是一个结构体,而C文件,库函数内部一定要调用系统调用,在系统的角度,只认fd,那么就可以知道,在FILE这个结构体中一定封装了fd。

3.2、对fd的理解(本质是数组下标)

什么是fd呢:

​ 一个进程想要访问文件,就需要先打开文件,一般而言,一个进程可以打开多个文件,而文件要被访问的前提是要加载到内存中,只有文件被加载到内存中才可以直接被访问。

​ 系统中存在大量的被打开的文件,所以操作系统(OS)需要通过先描述再组织的方法将这些文件管理起来,在内核中,操作系统内部为了管理一个被打开的文件,构建一个struct file结构体,充当一个被打开的文件,如果文件很多,则会使用双链表来进行连接。

struct file
{struct file* next;strucr file* prev;//包含一个被打开文件的几乎所有的内容。
}

在这里插入图片描述

四、重定向

4.1、输出重定向

在这里插入图片描述

此时运行上述代码可以看到如下结果:

在这里插入图片描述

可以发现,本来应该输出到显示器的内容却输出到了文件log.txt中,这种现象叫做输出重定向,常见的重定向方式有>,>>,<.

在这里插入图片描述

重定向的本质:在操作系统内部,更改fd对应的内容的指向。

4.2、dup2系统调用重定向

4.2.1、dup2介绍

在这里插入图片描述

  1. oldfd:表示需要复制的源文件描述符。这个参数是一个整数值,通常是一个已经打开的文件描述符。
  2. newfd:表示目标文件描述符。这个参数也是一个整数值,通常是另外一个已经打开的文件描述符。如果该文件描述符已经被打开,则会在复制之前关闭它。
4.2.2、dup2使用在这里插入图片描述

在这里插入图片描述

4.3、缓冲区、

***缓冲器就是一段内存空间,这个空间有c标准库维护。***该结构被包含在FILE结构体中。

缓冲区的缓冲策略:

  1. 立即刷新。

  2. 行刷新(行缓冲)

  3. 满刷新(全缓冲)

  4. 特殊情况:用户强制刷新(fflush),进程退出。

    关于缓冲区的认识:

一般而言,行缓冲的设备文件为:显示器,全缓冲的设备文件:磁盘文件。

所有的设备都倾向于全缓冲,因为缓冲器满了才刷新,这样只需要更少次的 IO 操作,更少次的外设访问,能提高效率。

显示器:显示器上的内容是直接给用户看的,一方面需要照顾效率,另一方面也需要照顾用户体验,在极端情况下是可以自定义规则的。

在这里插入图片描述

在该程序中同样的一个程序,向显示器打印输出4行文本,但是向普通文件(磁盘上),打印的时候,变成了7行,其中:

​ 1.C I0接口是打印了2次的

​ 2.系统接口,只打印一次和向显示器打印一样!

​ 如果向显示器打印,刷新策略是行刷新,那么最后执行fork的时候–一定是函数执行完了 &&数据已经被刷新了! fork也就无意义了!

​ 如果你对应的程序进行了重定向 – 要向磁盘文件打印 – 隐形的刷新策略变成了全缓冲! 边没有意义了fork的时候 --一定是函数一定执行完了,但是数据还没有刷新!!-在当前进程对应的C标准库中的缓冲区中!!–这部分数据是父进程的数据

五、文件系统

5.1、背景知识

​ 在磁盘中还有一类文件,这类文件叫做磁盘级文件,这类文件在进行文件操作的时候未被打开。

5.2、了解磁盘

​ 内存:掉电易失存储介质;磁盘:永久性存储介质—SSD ,U盘,flash卡,光盘,磁带等。磁盘是一个机械设备,是操作系统中唯一一个机械设备,是一个外设。

在这里插入图片描述
​ 在物理上,磁盘中分为一个一个的扇区,将数据通过特定方式存储在扇区中,当要找到该数据时候,先找到该数据存储的扇区,然后再在该扇区中寻找想要的数据。那么如何找到该扇区呢。那就需要使用CHS寻址。在哪一个面上对应的就是哪一个磁头,找到磁道,然后找到扇区。对磁盘的管理也就说对一个小分区的管理

5.3、文件系统结构

在这里插入图片描述

​ 磁盘的基本单位是扇区(512字节),但是操作系统和磁盘管理的基本单位是:4KB(8*512byte)。如果文件基本单位太小就会导致多次IO 操作,进而使效率降低;如果操作系统使用和磁盘一样大,当磁盘的基本带下改变,就会到子OS的源代码改变,

文件 = 内容 + 属性

  1. Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相 同的结构组成。政府管理各区的例子
  2. Data block :多个4KB大小的集合,报错的都是特定文件的内容。
  3. 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量, 未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个 文件系统结构就被破坏了
  4. GDT,Group Descriptor Table:块组描述符,描述块组属性信息。
  5. inod Table:inode是一个大小为128字节的空间,保存的是对应文件的属性,该块组内,所有文件的inode空间的集合,需要标识唯一性,每一个inode块都要有一个inode编号,一般而言,一个inode,一个inode编号。
  6. 块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用 ,假设有10000+个blocks,10000+比特位:比特位和特定的block是一一对应的,其中比特位为1,代表该block被占用,否则表示可用!
  7. inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
  8. i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
  9. 数据区:存放文件内容

创建一个新文件主要有一下4个操作:

  1. 存储属性 内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。
  2. 存储数据 该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据 复制到300,下一块复制到500,以此类推。
  3. 记录分配情况 文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
  4. 添加文件名到目录

5.4、软硬链接

5.4.1、软链接

软链接有独立的inode,是一个独立的文件,软链接的文件内容是指向文件对应的路径,相当于windows系统下的快捷方式。

ln -s 源文件 目标文件

在这里插入图片描述

如图所示,a.txt就是一个链接到abc.txt的软链接。

5.4.2、硬链接

硬链接没有独立的inode,不是一个独立的文件,创建硬链接只是在指定目录下建立了文件名和指定inode建立了映射关系。

ln 源文件 目标文件

在这里插入图片描述

建立了硬链接之后我们发现图示所表示的数由1 变成了2,这个数就是所谓的硬链接数,当创建硬链接这个数就会加1
在这里插入图片描述

通过观察inode可以发现,硬链接的inode和源文件的inode相同,说明创建硬链接不会创建新的inode。

在这里插入图片描述

通过上图可以看见一个发现一个新目录的硬链接数为2,是因为,该目录下有一个 " . "文件的硬链接,那么这样就可以通过硬链接数来看出该目录下有几层。

六、动静态库

6.1、静态库

ar lib静态库名.a 目标文件.o

静态库的前缀为必须为lib,后缀为.a

6.1.1、静态库的使用
6.1.1.1、直接使用

在这里插入图片描述

-I :文件头文件搜索
-L : 库文件搜索路径
-l : 在特定路径下使用的库(去前缀和后缀)
6.1.1.2、静态库安装

自己写的库为第三方库,所以操作系统不会自动连接

C语言默认静态库路径:ls /lib64/libc.a
头文件默认搜索路径为 /usr/include
库文件默认搜索路径为 /usr/lib64

在这里插入图片描述

安装后使用静态库:gcc main.c -lhello

6.2、动态库

在这里插入图片描述

动态库形成:形成的 .o 文件需要添加 -fPIC选项,该选项意思为:形成一个与位置无关的目标二进制文件动态库的前缀为lib 后缀为 .so 需要添加一个-shared选项
6.2.1、动态库使用
6.2.1.1、直接使用

在这里插入图片描述

如果只有静态库,那么gcc只能针对该库进行静态链接
如果动态库和静态库同时存在,那么默认使用的是动态库
如果动态库和静态库同时存在,非要使用静态库那么需要添加 -static选项
-static:摒弃默认使用动态库的原则,而是使用动态库的原则

但是该命令生成的可执行文件不可以直接运行,需要在LD_LIBRARY_PATH中添加环境变量

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库文件目录
6.2.1.2、配置文件
在/etc/ld.so.conf.d目录下创建一个.conf配置文件,将库文件目录放到该文件目录中,再使用ldconfig使该文件生效
6.2.1.3、建立软链接

在lib64目录下建立一个软连接

6.3、库存在的意义

站在使用库的角度:库的存在可以大大减少开发的周期,提高软件本身的质量

M-1720530783950)]

如果只有静态库,那么gcc只能针对该库进行静态链接
如果动态库和静态库同时存在,那么默认使用的是动态库
如果动态库和静态库同时存在,非要使用静态库那么需要添加 -static选项
-static:摒弃默认使用动态库的原则,而是使用动态库的原则

但是该命令生成的可执行文件不可以直接运行,需要在LD_LIBRARY_PATH中添加环境变量

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库文件目录
6.2.1.2、配置文件
在/etc/ld.so.conf.d目录下创建一个.conf配置文件,将库文件目录放到该文件目录中,再使用ldconfig使该文件生效
6.2.1.3、建立软链接

在lib64目录下建立一个软连接

6.3、库存在的意义

站在使用库的角度:库的存在可以大大减少开发的周期,提高软件本身的质量

站在写库的角度:简单,代码安全

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 海外金融机构银行保险证券数字化转型营销销售数字化成功案例讲师培训师讲授开户销售营销客户AI人工智能创新思维
  • 红酒知识百科:从入门到精通
  • 入门PHP就来我这(高级)18 ~ 获取结果集
  • 【C++】开源:坐标转换和大地测量GeographicLib库配置使用
  • 【Java算法】二分查找 下
  • 大模型学习笔记0-前言
  • vue 切换主题色切换主题色切换主题色切换主题色切换主题色
  • python使用tkinter添加下载进度UI
  • 如何使用uer做多分类任务
  • 刷题之多数元素(leetcode)
  • 11.索引_创建不同种类索引(primary+unique+复合....)
  • Spring MVC深入理解之源码实现
  • .net core Redis 使用有序集合实现延迟队列
  • 【环境准备】 Vue环境搭建
  • AngularJS API 深入解析
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【347天】每日项目总结系列085(2018.01.18)
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • CSS实用技巧
  • JS变量作用域
  • MobX
  • Mybatis初体验
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • SpiderData 2019年2月25日 DApp数据排行榜
  • Theano - 导数
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 类orAPI - 收藏集 - 掘金
  • 责任链模式的两种实现
  • 做一名精致的JavaScripter 01:JavaScript简介
  • Semaphore
  • ​浅谈 Linux 中的 core dump 分析方法
  • ​油烟净化器电源安全,保障健康餐饮生活
  • # Redis 入门到精通(一)数据类型(4)
  • #DBA杂记1
  • (24)(24.1) FPV和仿真的机载OSD(三)
  • (八十八)VFL语言初步 - 实现布局
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (五十)第 7 章 图(有向图的十字链表存储)
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • .bat批处理(十一):替换字符串中包含百分号%的子串
  • .NET : 在VS2008中计算代码度量值
  • .net core使用EPPlus设置Excel的页眉和页脚
  • .NET 药厂业务系统 CPU爆高分析
  • .NET 指南:抽象化实现的基类
  • .NET 中使用 Mutex 进行跨越进程边界的同步
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .net中生成excel后调整宽度
  • .vimrc php,修改home目录下的.vimrc文件,vim配置php高亮显示
  • /var/log/cvslog 太大
  • @TableId注解详细介绍 mybaits 实体类主键注解
  • [2016.7 test.5] T1
  • [51nod1610]路径计数