Linux: 基础IO , 重定向 , 静态库与动态库
2021/11/11 7:10:09
本文主要是介绍Linux: 基础IO , 重定向 , 静态库与动态库,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
基础IO:
库函数IO接口:
FILE * fopen(char *pathname,char*mode);
pathname:文件路径名 , mode:文件的打开方式 . 返回值:打开文件成功则返回一个FILE*文件流指针作为文件的操作句柄,失败返回NULL
r 只读方式打开文件,文件必须存在 r+ 读写方式打开文件,文件必须存在
w 只写方式打开文件,文件不存在则自动创建,存在则清空内容
w+ 读写方式打开文件,文件不存在则自动创建,存在则清空内容
a 追加写方式打开文件,文件不存在则自动创建,存在则写入数据总是写入文件末尾
a+ 读+追加写方式打开文件,文件不存在则自动创建,存在则写入数据总是写入文件末尾
b二进制方式打开文件.
在系统调用IO接口中并不区分文本和二进制数据,统一按照二进制进行处理,
但是库函数不一样,区分文本和二进制,默认文本操作,但是有的文本一个字符占据多个字节
例如:一个文件中有10个字节数据,但是读取的时候读取完毕后读取大小是9,这个9表示的不是9个字节而是9个字符
size_t fwite(const void* data,size_t bsize,size_t nmem,FILE* fp);
data:要写入文件的数据所在空间首地址;
bsize:要写入的块大小;nmem:要写入的块个数;bsize*nmem 就是实际要写入文件的大小
fp: fopen返回的文件流指针,标识要操作哪一个文件
返回值:返回实际完整操作的块个数
注意:r+/w/w+方式文件打开之后,文件的默认读写位置在文件起始,如果文件中本身就有数据,就会覆盖,并且读写位置会随着数据的写入向后偏移;a/a+方式打开文件之后,读默认是从文件起始读的,但是如果写会使读写位置跳转到文件末尾,将数据追加到未尾
size_t fread(void *buf,size_t bsize,size_t nmem,FILE*fp)
buf:一块空间的首地址,用于存放从文件中读取的数据
bsize:块大小;nmem:块个数; bsize*nmem 就是实际要读取的数据大小 ,
fp:fopen返回的文件流指针
返回值:成功返回实际操作的完整块个数,出错了返回0
注意:这个函数返回值存在多义性,返回0的时候无法直接确定是出错还是读到文件末尾了
例如一个文件100个字节,我现在读取数据块大小200,块个数1,因为没有读取完整的一块,并且读取到了文件末尾因此返回了—个0。这时候这个0就无法确定是出错了还是正常的。
因此,建议fread读取数据的时候,块大小设置为1,想要读取的长度设置为块个数,这样返回值就能告诉我们读取了多少个字节的数据
int fseek(FILE *fp,int offset,int whence);
改变文件读写位置,跳转到哪里就从哪里开始读写
fp:文件流指针; offset:偏移量;
whence:偏移起始位置SEEK_SET-起始/SEEK_CUR-当前/SEEK_END-末尾返回值:成功返回0;失败返回-1
比如我们使用a+追加写入文件后想读取文件信息这时候我们是读不到东西的,因为读写位置已经到文件末尾了,这时我们就需要先来一个 fseek(fp,0,SEEK_SET) 使读写位置从新回到开头,再读取
int fclose(FILE*fp);
每次文件使用结束关闭文件释放资源,否则造成资源泄漏
系统调用IO接口:
int open(char *pathname, int flag,mode_t mode);
pathname:要打开的文件路径名
flag:文件打开标志--决定了文件的打开方式
返回值:成功返回一个非负整数-文件描述符-是文件的操作句柄 这个下面会讲到; 失败返回-1;
mode:文件的权限,通常采用八进制数字设定0777或0664,如果使用了O_CREAT就一定要有第三个参数,第一个数字0是不可少的. 文件的创建权限是受到到umask掩码影响的。实际文件得到的权限mode&(~umask).
mode_t umask(mode_t mask);通常在程序起始阶段调用umask(0)将当前进程的创建掩码设置为0,这样文件权限就和open的mode参数一样了,零取反得777与上mode得mode自身.
必选标志: O_RDONLY-只读/O_WRONLY-只写/O_RDWR-读写,只能选择其中一个,不能同时使用
可选标志: O_APPEND-追加写│O_CREAT-文件不存在则创建|O_TRUNC-文件存在则截断为0|O_EXCL-文件存在则报错
比如fopen中w+: 读写+创建+截断O_RDWR |O_CREAT |O_TRUNC
fopen中a+: 读+追加写,创建O_RDWR |O_APPEND |O_CREAT
ssize_t read(int fd,void *buf, int len);
fd : open返回的文件描述符-操作句柄-表示操作哪个文件
buf:一块空间地址,用于存放读取到的数据
len:要读取的数据长度
返回值:成功返回实际读取到的数据长度;失败返回-1;
ssize_t write(int fd, void *data, int len)
fd:文件描述符-操作句柄; data:要写入文件的数据的空间首地址; len:想要写入文件的数据长度返回追:成功返回实际写入文件的数据长度;失败返回-1;
off_t lseek(int fd,off_t offset,int whence);
fd:文件描述符; offset:偏移量; whence:偏移起始位置SEEK_SET / SEEK_CUR / SEEK_END
返回值:成功返回跳转后的位置相对于文件起始位置的长度(额外用法:跳转到文件末尾则返回文件长度)﹔失败返回-1int
和fseek一样:比如我们使用a+追加写入文件后想读取文件信息这时候我们是读不到东西的,因为读写位置已经到文件末尾了,这时我们就需要先来一个 lseek(fd,0,SEEK_SET) 使读写位置从新回到开头,再读取
close(int fd);关闭文件释放资源
文件描述符与文件流指针:
文件描述符就是open的返回值fd , 文件流指针就是fopen的返回值fp
系统调用接口封装在库函数中,所以文件描述符也就封装在文件流指针中了.
通过下面老师画的图理解文件描述符:
文件描述符本质上就是内核中,进程打开的文件IO信息指针数组的一个下标
当我们通过描述符操作文件的时候,在pcb中找到files _struct结构体指针,进而找到结构体,找到结构体中的数组,以描述符作为下标获取到数组中存放的文件描述信息的地址,通过描述信息操作文件
为什么第一个打开的文件的文件描述符是3呢?因为一个进程运行之后默认会打开三个文件:stdin标准输入,stdout标准输出,stderr标准错误 . 分别为键盘,显示器,显示器, 他们的文件描述信息分别占据了0,1,2数组下标的位置.所以0,1,2号文件描述符分别指向键盘,显示器,显示器. 再打开文件会优先放在数组下标最小的位置, 所以就成3了.
再比如下面可以通过先close(1)断开1号文件描述符指向显示器, 再打开一个文件, 之后这个文件就变成1号文件描述符指向的文件, 之后操作的东西如果原本输出到显示器的就会写入到文件里, 而不再输出到显示器了,这也是重定向的原理:改变了文件描述符对应的文件描述信息, 就改变了操作了的文件,但文件描述符没有变.
重定向:
1.如下图, 1号文件描述符是标准输出,所以write(1,str, )写入的str只会显示在屏幕(显示器)上,不会写进某个文件.
2.和上面所说,关闭1号后再打开文件会优先匹配最小的数组下标所以打开的test.txt文件的文件描述符就成了1.再write(1, ,)就会写进重定向的文件中而不会再显示在屏幕上
3.重定向的接口:dup2(int fd1,int fd2),使文件描述符fd2对应的文件描述信息改为fd1对应的文件描述信息,成功则关闭fd2原先指向的文件描述信息,这时fd1和fd2都操作fd1对应的文件
files_struct结构体中的_fileno成员就是文件描述符, 所以也可以用下面这句stdout->_fileno=fd改变标准输出的文件描述符1 改为想给的fd.(屏幕显示器的文件流指针就是stdout).
4.文件流指针指向的结构体中不止封装有有文件描述符,还有缓冲区.
所以fwrite是有文件缓冲区的,而write就没有了.所以下面的代码运行会直接打印write,程序退出前刷新文件缓冲区才打印出缓冲区里的hello bit和fwrite
这也是系统调用接口_exit()没有缓冲区,库函数exit()有的原因.不了解缓冲区的朋友可以去看我前面写的linux进程概念与控制中文字概念中有详细介绍
标准输出重定向:
>>是我们的标准输出重定向符号,一个 '>' 是清空重定向就像O_TRUNC, 两个''>>''是追加重定向就像O_APPEND.
比如 ls >> test.txt 就是将原本打印出来到屏幕上的当前目录下的文件信息 不再打印出来 而是重定向到指定的test.txt中. 下面我们通过minishell理解标准输出重定向的过程!
思路是分析接收到键盘指令的cmd, 若含 '>' 符号就使该符号位置设为'\0', '\0' 前面的字符串就是对应的指令.计算 '>' 的个数使后面在程序替换执行该执行的指令前决定重定向是追加还是清空, 设置为'\0'再往后看, >>后第一个非空格非'\0'的字符就是重定向指定文件的文件名首地址设置为filename,则位置到'\0'之间的字符串就是要写入信息的文件名.
动态库与静态库生成与使用:
库的命名:一定为lib作前缀+中间自定义库名称+.os后缀(动态库)/.a后缀(静态库)
库:将很多写好的功能性代码打包成的一个文件(库中不能有main函数)
前面写的linux常用指令和常用工具gcc介绍部分中写有静态链接和动态链接的区别.
生成:嘿嘿
使用:
1. 使用-l来指定要链接的库名称(前提是库文件需要放到指定路径下:/usr/lib64 )
2.如果库文件不在/usr/llib64底下也不希望把库文件拷贝过去, 也可以通过改变环境变量指令: export LIBRARY_PATH=$(LIBRARY_PATH):./ 设置环境变量,将库文件所在目录加入到环境变量路径中, 再使用 gcc main.c -o main -lchild 生成链接了child库的可执行程序main
运行可执行程序的时候使用: (仅限于动态库--因为只有链接动态库,运行程序的时候才需要加载)
静态库使用时是把库中用到的代码直接放入到可执行程序,动态库使用是只记录函数符号信息,因此运行时依赖 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH):./ 设置环境变量中的库文件加载路径.这两句export必须配对一起使用,不然没有下面这个export的话./main执行时程序没法执行
3.(常用)使用gcc -L选项指定库文件的所在路径(常用于链接静态库--因为静态库没有依赖,运行时不需要加载) gcc main.c -o main -L./ -lchild 使用main.c和自定义的库名称生成一个main可执行程序
要是没有-L./指定库文件所在路径为./(当前目录下)就需要把库文件拷贝到/usr/lib64底下
要是./下有同名的child动态静态库文件(libchild.so,libchild.a)默认链接动态库.
这篇关于Linux: 基础IO , 重定向 , 静态库与动态库的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法