本次博客来对基础IO这一内容进行一个收尾。
目录
1.文件描述符和文件流指针
在正式开始本次博客内容之前, 我们来引出两个名词--“文件描述符”和“文件流指针。”这是上一篇博客中所遗留的一部分内容。
其中文件描述符就是系统调用接口open打开文件所返回的整形数字,它本质上是内核中文件描述信息数组的下标。进而当需要对文件进行操作时,便以文件描述符作为下标,找到对应的文件描述信息,来对文件进行操作。
而文件流指针就是库函数fopen打开文件所返回的FILE*指针。在Linux中,文件流指针内部便封装了文件描述符。
2.重定向
2.1内容
重定向的作用就是将原本要写入A文件的数据,在不改变程序逻辑的前提下,写入到B文件之中。
- >:清空重定向;
- >>:追加重定向。
2.2原理引入
了解玩重定向的内容之后,我们来简述一下重定向的原理,即重定向是如何实现的。
在我们通过系统调用接口或者库函数来打开文件时,会存在一个sturct file型的结构体来对文件进行一个描述,便于我们后续的操作。在Linux中对于正在运行中的程序会存在pcb来对其进行描述,打开文件这个操作也不例外,必然也存在对应的描述信息。
在讲解进程概念的时候,我们了解过对于pcb的底层实现上也是作为一个task_struct的结构体出现的,该结构体中一个指针指向struct files_struct结构体来对打开文件的信息进行描述,在struct files_struct结构体中存在一个struct file *fd_arry[]数组。
大致了解完文件打开之后的Linux底层后,当我们执行一个程序的时候,系统便会默认打开三个文件:标准输出-0;标准输出-1;标准错误-2。
如何对于文件描述符的分配规则是:最小未使用原则,即struct file *fd_arry[]数组从中下标从小到大进行分配,我们来通过代码对上述内容进行一个演示:
从ccfile.c和其执行结果我们可以看出,打开test.txt所分配到的文件描述符fd = 3,这与我们前文中的描述相符。因为struct file *fd_arry[]数组的前3位已经会被默认打开的3种文件描述符占用。
2.3原理
在了解完Linux中系统描述符的分配规则和存储位置后,我们言归正传来聊一聊重定向的原理。
重定向原理其实也十分简单,它本质上就是将一个描述符所对应位置的文件描述信息地址,替换成另外一个文件的描述。这便实现了在不改变逻辑的情况下,改变了所操作的文件。
3.动态库与静态库的生成和使用
3.1内容
这一部分内容我们来了解5个名词:库文件、动态库、静态库、动态链接和静态链接。
库文件:(库文件中包含着的便是我们常用的库函数),库文件是将已实现的代码进行打包封装,并不是为了生成可执行程序,而是将其作为接口来供我们使用。
动态库:和位置无关代码打包。静态库:单独的文件。
动态链接:生成可执行程序的时候,连接动态库,记录库中的符号表,并且多个程序运行时在内存可以共享同一个动态库。运行时依赖动态库存在,因为程序运行时才将动态库加载到内存当中。
静态链接:生成可执行程序的时候,连接静态库,直接将库中所用到的函数实现,拿到可执行程序之中,不存在运行依赖,效率较高。但是生成的可执行程序较大,并且如果运行了多个用到同一个静态库的程序,则代码内存中可能存在冗余。
3.2生成库
生成库的作用就是将大量已经实现的代码打包起来,便于以后的多次使用。在Linux中对于库的生成也有与之相对应的指令,不同的指令来生成动态库和静态库。
- 先将所有的原码进行编译汇编成各自的二进制指令:gcc test.c -o test.o;
- 将所有的二进制文件给打包到一起。
3.2.1动态库
动态库:gcc -fPIC -c test.c -o test.o(生成动态库的所需要的二进制文件指令),gcc --shared test.c -o libmytest.so(生成.so文件,即动态库文件)。(Linux中动态库命名:以lib作为前缀,以.so作为后缀,中间是库名称)
其中,-fPIC是告诉编译器,在编译生成指令的时候产生和位置无关的代码(变量指令都是相对偏移指令)。当一个动态库被映射到不同进程的虚拟地址时,映射的位置也不尽相同。
因此,main函数对其的调用就会变得的麻烦(动态库具体位置不同,且其是运行时才被加载),所以在所有的main中所调用的库函数都只是记录一个相对偏移量,最后根据实际映射的起始地址开始计算。
3.2.2静态库
静态库:gcc -c test.c -o test.o(先汇编生成二进制指令),ar -cr libmytest.a test.o(生成静态库文件test.o)。(Linux中静态库命名:以lib作为前缀,.a作为后缀)
3.3使用库
使用 -l 来告诉编译器需要使用那一个库,gcc main.c -o main -lmytest。
但是这种直接使用的方式大概率会产生报错,因为编译器默认会在指定目录(/user/lib64)查找对应的库文件,而我们未将其放置在系统指定目录中,所以会出错。
那么解决该问题存在两种方式,一种针对报错情况直接进行解决,即将我们的库文件放置到系统指定目录之中;另一种是通过配置环境变量的方式。
- 将库文件放置到指定目录中:64位操作系统 -- /user/lib64,32位操作系统 -- /user/lib32;
- 设置环境变量:export LIBRARY_PATH=${LIBRARY_PATH}:./ -- 将库文件所在目录添加到环境变量值中, export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:./ -- 添加运行加载库文件的路径。
- 还有一种方式来加载库,不过这种方式只适用于加载静态库与,因为它无法设置运行程序时的库加载路径,所以我们单独来看。gcc -L选项,gcc main.c -o -L./-lmytest。