基础IO(文件描述符,理解文件系统,动静态库)

1.文件描述符

①回顾C语言文件操作

打开文件fopen
在这里插入图片描述
向特定的文件写入我们的字符串
在这里插入图片描述
然后我们实践一下基本的文件输入
在这里插入图片描述
在这里插入图片描述
读文件我们在C语言中用的是fgets
在这里插入图片描述
我们可以将文件中的内容给读出来
在这里插入图片描述
在这里插入图片描述
r是读,w是写,注意一下a是追加,可以在w的基础上继续写,写入和追加是由区别的

②加深理解

C语言默认会打开三个输入输出流,stdin ,stdout, stderr
类型是FILE*说明就是C语言给我们提供的
在这里插入图片描述
stdin对应的硬件设备:键盘
stdput对应的硬件设备:显示器
stderr对应的硬件设备:显示器
我们向流中写入其实就是向硬件中写入
在这里插入图片描述
在这里插入图片描述
我们将文件的内容重定向到文件中,可以发现文件中竟然有内容,加深了我们对重定向的理解:就是我们将本来应该打印到屏幕上的内容转移到了文件中,本质是把stdout的内容重定向到文件中
在这里插入图片描述
stderr是打印到屏幕上,但是不能进行重定向操作,虽然都是往屏幕上打,但是stderr和stdout还是不一样的
在这里插入图片描述
C++中 也有相应的 cin , cout , cerr
在这里插入图片描述

fputs向一般文件(磁盘,也是硬件)或者硬件设备都能写入
一切皆文件!
—————— —————— —————— -—————— ————————
最终都是访问硬件:显示器,键盘,文件(磁盘)
OS是硬件的管理者!
所有语言上的对“文件”的操作,都必须贯穿OS!(不可以跳过操作系统直接跟硬件交互,操作系统不相信任何人,访问操作系统,需要通过系统调用接口的!!)
所所有语言的fopen ,fclose ,fread ,fwrite等底层一定需要调用OS提供的系统接口在这里插入图片描述
——— ——— ——— ——— ——— ——— ——— —— —————
一个文件没有被打开,这个文件在哪?
磁盘中
如果创建一个空文件,那它占不占磁盘空间?
占的,磁盘文件 = 文件内容 + 文件属性
之前所学的所有的文件操作:对文件内容的操作,对文件属性的操作

③学习文件的系统调用接口

命令man 2 open
mode可以设置我们新建文件的权限信息
在这里插入图片描述
man 2 close
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里的int flags 指的是传递标志位
int : 32 bit
一个bit 代表一个标志
0_WRONLY 0_RDONLY 0_CREAT都是只有一个比特位是1的数据,而且不重复
底层可能是这样的:
#define 0_WRONLY 0x1 0000 0001
#define 0_RDONLY 0x2 0000 0010
#define 0_CREAT 0x4 0000 0100
其实在自己这个内核版本查看时底层是八进制的,但每个符号代表一个数字,不重复,理解这个即可
在这里插入图片描述
在这里插入图片描述
我们知道小于0是报错的情况,但为什么是从3开始的呢,前面的0,1,2去哪了呢?
其实对应的就是我们的标准输入,标准输出,标准错误
当我们的程序运行起来,变成进程后,默认情况下,OS会帮助我们的进程打开三个标准输入输出!
0:标准输入,键盘
1:标准输出,显示器
2:标准错误,显示器

在这里插入图片描述
struct file{
//文件相关的属性信息
}
进程中有一个结构是struct files_struct, 里面包含一个数组叫fd_array[ ],指向文件结构,其中数组的前三个下标0,1,2 指向的是标准输入,标准输出,标准错误
fd : 本质是内核中进程和文件关联的数组的下标!
在这里插入图片描述

④进一步加深理解

一切皆文件的理解,类似于C++的多态
在这里插入图片描述
既然说会自动打开0,1,2 那么我们直接往1写入会怎么样
结果成功!
在这里插入图片描述
在这里插入图片描述
输入也是可以的
注意1.回车也会读入 2.linux不会自动+\n,需要手动加
在这里插入图片描述
在这里插入图片描述
文件描述符的分配规则,给新文件分配的fd, 是从fd_array中找一个最小的,没有被使用过的,作为新的fd
如果把默认的流关掉的话,新的fd则会从他们开始

在这里插入图片描述
在这里插入图片描述
C语言上的stdin , stdout, stderr对应的就是系统层面的0,1,2

实际上的追加重定向,就是在系统层面多了一个O_APPEND操作
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

⑤了解接口dup2

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
输入重定向
dup2(fd,0)本来应该在显示器上读取的内容,去fd上面读
在这里插入图片描述

在这里插入图片描述
执行exec*程序替换的时候,不会影响我们曾经打开的文件

父进程如果曾经打开了标准输入,标准输出,标准错误,子进程也要继承下

为什么我们所有的进程,都会打开默认的标准输入,标准输出,标准错误呢??
因为bash是所有进程的父进程,本质是bah打开的,创建的子进程自然打开的

⑥缓冲区的概念

说明都是打印到屏幕上去的
在这里插入图片描述
在这里插入图片描述
都是往屏幕上打印,但是重定向的时候,只有标准输出可以重定向到文本里,所以它叫输出重定向
在这里插入图片描述
这种写法可以将标准输出,标准错误都重定向
红色箭头所指可以这么理解:把1的内容拷贝到2的里面,1本来指向的是蓝色区域,现在1拷贝给2,2也就指向红色区域
在这里插入图片描述
在这里插入图片描述
进程退出的时候,会刷新FILE内部的数据到OS缓冲区

用户->OS
刷新策略:
1 .立即刷新(不缓冲)
2 .行刷新(行缓冲\n),比如,显示器的打印
3 .缓冲区满了以后,才刷新(全缓冲),比如,往磁盘中写入
以上刷新策略OS->硬件同样也适用
在这里插入图片描述
关闭close的情况,fd被关闭,C语言软冲区无法刷新到文件的内核缓冲区,从而无法进行下面的操作
在这里插入图片描述
在这里插入图片描述
没有关闭close,进程结束则会刷新
在这里插入图片描述
在这里插入图片描述
还有一种情况也是可以的,那就是适用fflush强制刷新
也是可以刷新成功的
在这里插入图片描述
在这里插入图片描述
总结:
为什么有时会发现我们写入的数据不见了呢?是因为存在了缓冲区当中,没有立即刷新,关闭了以后就不能够刷新了

在这里插入图片描述
在这里插入图片描述
打印到显示器上有一份代码,我们惊奇的发现,重定向到文件中以后竟然有7条(1+6)重复出现的是使用C接口的时候,系统接口并不受影响
刷新策略变了,本来是行刷新,现在变成了全缓冲
C语言提供的会写入到C语言提供的缓冲区buffer->在重定向文件的时候刷新策略变了
fork从而进行 写时拷贝,共享父进程里的代码,刷新数据!!
我们就发现重复刷新了
在这里插入图片描述
我们加上一个强制刷新之后,再fork以后,我们缓冲区里没有数据了,就不会进行写实拷贝,就不会重复打印了
在这里插入图片描述
在这里插入图片描述
问题来了,为什么write不会产生两份,我们讲的缓冲区一定不在操作系统内,如果在操作系统内,fprintf, fputs底层调用的都是write
可最后write是一份
说明缓冲区是在用户层,更验证了大多数缓冲区都是用户级缓冲区

stdout , cin/cout , iostream, fstream
类会包含缓冲区
C++中的std::endl类似于C语言中的\n,作用就是刷新C++刘中的数据到我们的
目标设备,比如说显示器
语言上有缓冲区
— — —— ———————— —————————— ——————————

2 .理解文件系统

①磁盘的分区管理

文件 = 文件内容 + 文件属性
如果一个文件没有被打开呢?它在我们的磁盘放着
磁盘是我们计算机中的一个机械设备(SSD(solid state disk)例外,FLASH卡,usb)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
磁盘写入的基本单位是:扇区,512字节
盘面(磁头),磁道,扇区
一般是4kb交互,8个扇区
操作系统上将数据刷新到磁盘实际上就是写到弹片上

可不可以将我们的碳片想象成线性结构呢?
在这里插入图片描述
磁盘大,管理成本高不高?
分区:大磁盘变成小空间
分区写入文件系统:生活在江苏省 写入江苏省领导班子
两个概念
①分区
②格式化:写入文件系统
小区域管好,大区域也就可以管好

在这里插入图片描述

在这里插入图片描述
Linux特有的
EXT系列的文件系统

Linux:文件在系统层面是没有意义的,是给用户使用的
Linux中真正表示一个文件,是通过文件的inode编号
一个文件一个inode,每个文件都有自己的inode编号
inode可以通过inode编号找到文件内容

目录是文件吗?是的
目录有自己的inode吗?必须的
目录有数据吗?必须的
目录的数据块里放什么呢?
文件名:inode编号!

你创建的所有文件,一定是在一个目录下!!
在这里插入图片描述
这一部分代码做了什么?
创建文件的时候,首先在inode Bitmap里找一个没有被占用的inode,然后将文件创建的默认信息搞进来
写入文件的时候,在block Bitmap里找没有使用的一个block和我们的inode建立关系,然后把数据写到对应的块里去,最后inode编号也就有了
cat的时候,
假如12345 hello.c
cat hello.c -> 先查看io_learning2 -> data block ->12345: hello.c -> 12345 ->
inode table ->inode ->blocks[] ->打印文件内容

在这里插入图片描述
rm删文件的时候很简单,只需要将inode Bitmap由1置0就可以将文件删掉了
不是将属性和内容删除,而是直接改位图,下次有新的文件,可以直接覆盖旧内容
恢复文件的原理其实就是将inode bitmap由0置1即可,将块再由0置1就可以恢复了
在这里插入图片描述
如果误操作,删除了文件,最好的做法是什么??
什么都不要做

②软硬链接

创建软链接
ln -s log.txt log_s
-s意思是soft ,建立软连接
软连接要连谁呢? 连的是log.txt
用log_s去连
在这里插入图片描述
删除链接可以用rm,但更建议使用unlink
在这里插入图片描述
什么好时候会用到软链接呢?
一个路径很深的文件,创建软链接,可以快速打开
相当于windows中的快捷方式

软链接是有自己独立的inode的!
软连接是一个独立文件!
有自己的inode属性(保存的是指向文件的所在路径+文件名)
在这里插入图片描述
硬链接本质根本就不是一个独立的文件,而是一个文件名和inode编号的映射关系,因为自己没有独立的inode

创建硬链接,本质是在特定的目录下,填写一对文件名和inode的映射关系

1和2是文件对应的硬链接数(有几个文件指向我)
在这里插入图片描述
硬链接有什么用??
一个典型应用场景:目录
在这里插入图片描述

③动静态库

在这里插入图片描述
Access:文件最近被访问的时间,我们发现实际操作下来,文件时间貌似并没有变化…在较新Linux内核中,Access时间不会被立即更新,而是有一定的时间间隔,OS才会自动进行更新
Modify:最后一次改变文件内容的时间
Change:最后一次改变文件属性的时间

当改变文件内容的时候,有可能修改文件的属性
比如:可能更改文件的大小属性

Makefile怎么判断我们的文件是否可以make
test.c 的modify时间要比 生成的test的modify时间新(晚)的条件下,可以重新make
总结:makefile和gcc会根据时间问题,来判定源文件和可执行文件谁更新,从而指导系统那些源文件需要被重新编译!
————— ————— ——————— ——————— ————————
ldd + 可执行文件
ldd 可显示可执行文件依赖的库
在这里插入图片描述
这个就是所谓的C标准库
蓝色的地方是一个软连接
在这里插入图片描述
库也是个文件(也就是说在某个分区里,有自己的inode…)
一般库分两种:动态库 和 静态库
在Linux中,如果是动态库,库文件是以.so作为后缀的
如果是静态库:库文件是以.a作为后缀的

库文件的命名: libXXXX.so or libYYYY.a-…
库的真实名字:去掉lib前缀,去掉a- ,so-(包含)后缀,剩下的就是库名称!

C++的后缀:cc,cpp,cxx,cyy(前三个比较常见,常用前两个)
在这里插入图片描述
在这里插入图片描述
安装C/C++ 静态库指令:yum install
用静态库生成的文件大小对比动态库比较大
静态库一般是把库文件直接拷贝进可执行文件的,动态库只会把里面的函数链接起来,不会产生拷贝的行为,当需要使用函数的时候,跳转到库当中就可以了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
系统中的很多命令也是动态链接的在这里插入图片描述
————— —————————— ——————— ———— —————

④如何制作静态库?

库本身就是二进制的文件,我们如何得知,库给我们提供了什么方法呢?
一套完整的库:1 .库文件本身 2 .头文件.h 3.说明文档

库文件是要包含头文件和源文件的 ,分离 ->方便制作库

test_lib里面放的是两个头文件,两个源文件
在这里插入图片描述
在这里插入图片描述
方法一:gcc后面加上源文件
在这里插入图片描述
方法二:
%是一个通配符,把以.o结尾的文件全部展开
汇编后会形成.o,叫做目标文件
第四行意思是把.c后缀的一个个转换成.o
在这里插入图片描述
在这里插入图片描述
如果我们想把我们的代码给别人用
1 .提供源代码 + 头文件
2 .把所有的.o打包就是库,提供.o文件可以让你进行链接,*.o链接,就可以形成我们的mytest

ar是归档工具,rc表示(replace and create)有的话就replace,没有的话就create
静态库的本质就是把所有的.o文件打包形成静态库
在这里插入图片描述
在这里插入图片描述
查看静态库的命令
在这里插入图片描述
打包以后之后只要将output打包给别人就行了,同时也不会展示自己的源代码
在这里插入图片描述

在这里插入图片描述
将库导入到系统库中
在这里插入图片描述

⑤如何使用静态库

将库先拷贝给朋友
在这里插入图片描述
朋友在调add.h sub.h的时候为什么会报错呢??
因为头文件是在lib里面的,test.c在它的上一级目录,两个不在同级目录中,也就找不到
在这里插入图片描述
-I./lib头文件在当前目录的lib里 -L./lib 库在当前目录下的lib里
要指定库,不然gcc不知道我们要链接哪一个库
-l加上库名称(可以有空格,可以没有空格)
-lmymath 或-l mymath
库名称不是库文件名,去掉前缀,去掉后缀
gcc test.c -I./lib -L./lib -lmymath

我们之前写的代码,也用了库,为什么就不要使用这些选项呢?
之前的库,在系统的默认路径下:/lib64, /usr/lib , /usr/include等编译器是能够识别这些路径的

如果不想带这些选项,是不是可以把对应的库和头文件拷贝到默认路径下,可以,但是严重不推荐!
会污染别人的命令池,头文件池
上面的过程,也是一般软件的安装过程,相当于把头文件和库拷到系统路径下,这个就叫安装
在这里插入图片描述
在这里插入图片描述
就可以成功使用了
在这里插入图片描述

⑥如何制作动态库

静态库用的是ar命令,动态库则用的是-shared
在这里插入图片描述
friend1 要把库给给 friend2在这里插入图片描述

⑦使用动态库

编译过了,运行没过在这里插入图片描述
其实是有一个环境变量叫LD_LIBRARY_PATH需要自己设置
在这里插入图片描述
对比之前是一个not found 的状态
在这里插入图片描述
当前环境变量在此次登录有效,可以添加到系统配置中,但是不建议,如果要,建议开多个终端,防止意外发生

还有第二种方式,如果有多个动态库要导入
可以使用下面的方法
进入/etc/ld.so.conf.d/的目录
在这里插入图片描述
在这里插入图片描述
中间修改时候要用到root权限
写完以后加载一下就又可以运行了
在这里插入图片描述
这种做法仅做了解,前期建议使用LD_LIBRARY_PATH的方式进行设置

⑧其他

复习:
我们一直再直接或间接使用库(C/C++)
如何使用呢?拿到别人的头文件和库,加到自己的项目中

如何制作?所有的源代码,都需要先被成.o(可重定向目标文件)
1 .可以先把自己的源文件编译成.o
2 .制作动静态库的本质:就是把所有的.o打包,使用ar 或者gcc来进行打包
3 .交付:include + .a or .so文件

静态库:ar -rc
动态库:gcc -fFIC -shared

如果既有动态库又有静态库,gcc / g++ 优先链接动态
如果想要静态链接,手动添加-static

为什么我们之前所写的所有的代码都没有报错呢?
默认是动态的,因为我们一定有动态库!
为什么一定有动态库呢?
因为系统中有很多的命令使用C语言写的,而且是动态链接的

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值