Linux 基础I/O :文件描述符,重定向,文件系统,软链接和硬链接,动态库和静态库


文件描述符

在这里插入图片描述在这里插入图片描述
上面两个接口分别是c语言的fread接口和linux的read接口,当我们在使用的时,可能会有疑问,为什么linux的io接口能只通过一个整型的fd来操控文件和io?这个fd又是什么?

文件描述符fd

大家应该听说过,linux下一切皆文件
在这里插入图片描述
task_struct也就是pcb在Linux下的实现,然后其中的*files指针,就指向了一个文件描述表,表包含的数组fd_array[]中的每一个就是一个文件指针file *,所以这个fd,就是这个数组中的一个下标,通过这个下标来找到对应位置的文件指针。

fd:fd是一个文件描述符,是文件的操作句柄,同时是fd_array[]的下标,通过这个下标来找到对应文件的文件指针。

同时,我们创建文件时这个下标是从3开始的,因为0,1,2分别对应stdin(标准输入),stdout(标准输出),stderror(标准错误)


重定向

通过替换文件描述符,使得数据不再写入原来的文件,而写入新的文件

常见的重定向符号:

> 重定向输出符号,覆盖原来的内容并写入
>>重定向输出符号,追加写入

在这里插入图片描述
通过 > 符号将原本写入到标准输出的数据写入到test1.txt

dup2

int dup2(int oldfd, int newfd)
改变数据的流向,使流向newfd的数据全都流向到oldfd中。

重定向前,若newfd已经有打开的文件,则会关闭
重定向后,oldfd和newfd都会操作oldfd所操作的文件

在这里插入图片描述
首先向标准输出输出一个hello(需要\n刷新缓冲区,不刷新的话数据会先写入缓冲区,直到程序结束才写入文件)
然后将标准输出重定向至test.txt文件中
再输出一个world
在这里插入图片描述
这是hello输出到了标准输出中,world输出到了文件中。


文件系统

这里以Linux下的ext2文件系统为例

文件系统就是磁盘上管理文件的系统
在这里插入图片描述
每一个分区都有自己的文件系统。

  • bitmap(位图):一串二进制比特位,用于标记该位置是否存有数据,1为有,0为无。
  • 超级块:描述文件系统,例如有多少磁盘块,有多少使用
  • inode: 文件的元信息,例如大小,属性,所占的磁盘块地址
  • data : 文件的数据
  • 目录项:文件名 + inode节点号

文件存储的流程

通过超级块来找到数据块位图和inode位图,然后通过数据块位图和inode位图来找到空闲的数据块和inode节点,将文件数据存入数据块中,将文件元信息存入inode节点。数据存储完后,还会在当前目录下,记录这个文件名称以及inode节点号(目录项)

文件获取的流程

通过文件名到父目录文件中找到对应的目录项,获取目录项中的inode节点号,通过超级块来找到对应的inode节点,再通过inode节点中文件存储的数据块的地址来访问文件数据


软链接和硬链接

硬链接:

本质上和源文件没什么不同,和源文件共享同一个inode节点,通过这个inode节点来访问文件数据

创建方法:ln [源文件] [链接文件.hard]

软链接(符号链接):

本质上是一个独立的文件,有自己的inode节点,文件数据中保存源文件的路径,通过这个路径来访问源文件

创建方法:ln -s [源文件][链接文件.soft]

在这里插入图片描述
创建test.txt的硬链接和软链接
在这里插入图片描述
可以通过链接文件访问数据。
在这里插入图片描述
删除源文件后硬链接还可以访问源文件数据,软链接失效。

原因:硬链接与源文件共用同一个inode,删除源文件后只是减少了inode的一个链接数,硬链接文件还可以继续访问源文件数据。而软链接是通过源文件路径来访问数据,但是源文件已经删除,所以路径访问不到,无法获取源文件数据。

同时需要注意的是,删除一个文件,文件并不会立即删除,而是先删除了目录项中的文件名信息,并使inode的链接数减一,只有链接数为0时文件才会删除。

跨分区

因为每一个分区都有自己独立的文件系统,也就是说有自己的一套inode节点,所以硬链接是无法跨分区的,因为操作系统会无法识别这个inode到底是哪一个分区的,而使用路径访问的软链接则可以跨分区。

同时,硬链接也无法对目录链接,因为目录本身也是跨分区的。


动态库和静态库

库文件:打包了一堆实现了常用功能的代码文件。
当我们在调用库函数和系统函数的时候,例如stdio.h中的printf,我们并没有定义对函数的实现,那么是在哪里实现的呢?
系统把这些函数的实现放到了/usr/lib下的库文件中,如果没有指定时,编译器会自动到系统默认的库函数搜索路径下面进行查找,然后在链接阶段将库链接进代码中。

函数库分为静态库和动态库两种。

静态库

静态库的加载是在编译链接时将库函数的代码全部加入到可执行文件中,虽然生成的文件比较大,但是运行的使用也不需要库文件了。

生成静态库方法:
gcc -c *****.c -o *****.o 先将.c文件预处理、编译、汇编
ar -cr lib*****.a *****.o (静态库命名:lib**.a)将.o文件封装成库函数**

链接静态库方法:
1.将库文件移动到指定路径下:/usr/lib64 /usr/lib
2.通过环境变量指定库的所在路径:LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

3.通过gcc -L现象指定库所在路径
gcc test.c -o test -L. -l******(库名)

动态库

动态库在编译链接阶段并没有把代码加入到可执行程序中,而是在程序运行的时候链接加载文件库,节约了系统的开销(Linux默认使用动态库)

生成动态库方法:
gcc -c -fPIC *****.c -o *****.o 先将.c文件预处理、编译、汇编
gcc -shared -o *****.o lib*****.so (静态库命名:lib**.so)将.o文件封装成库函数**

链接动态库方法:
1.将库文件移动到指定路径下:/usr/lib64 /usr/lib
2.通过环境变量指定库的所在路径:LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

发布了60 篇原创文章 · 获赞 78 · 访问量 6322

猜你喜欢

转载自blog.csdn.net/qq_35423154/article/details/105366245