基础IO——文件系统与接口、文件描述符、静/动态库、重定向
本文主要介绍了LInux操作系统中关于文件系统与文件接口、静态库与动态库、重定向以及文件描述符的相关内容。
目录
C文件接口
(1)打开文件:fopen
(2)写文件:fwrite
(3)读文件:fread
(4)移动文件流指针位置:fseek
(5)fclose:
系统调用文件接口
(1)open
(2)write
(3)read
(4)lseek
(5)close
文件描述符
(1)文件描述符概念:
(2)查看文件描述符分配规则
(3)从task_struct的角度理解文件描述符
(4)文件描述符和文件流指针的区别
重定向
(1)符号
(2)从内核理解重定向
(3)重定向接口
静态库&动态库
(1)库的概念
(2)库的优点
(3)动态库
(4)找到动态库的方式
(5)静态库
软硬链接
(1)软连接
(2)硬链接
简单的文件系统
(1)文件在磁盘当中是如何进行存储的(ext2)
(2)创建新文件的4个操作
C文件接口
(1)打开文件:fopen
FILE *fopen(const cahr *path,const cahr *mode);
示例:
FILE* fp = fopen("目标文件名","mode");
函数参数:
path:打开的文件(带路径)
mode:以何种方式打开
(1)r:只读,文件流指向文件头部
(2)r+:读写,文件流指向文件头部
(3)w:只写,如果文件存在则清空文件开始写
如果文件不存在则创建文件
(4)w+:读写,如果文件存在则清空文件开始写
如果文件不存在则创建文件
(5)a:追加写,如果文件不存在,则创建文件,从文件末尾开始写
(6)a+:可读、追加写,如果文件不存在,则创建文件,从文件末尾开始写
返回值:
成功:返回文件流指针
失败:返回NULL
(2)写文件:fwrite
size_t fwrite(const void *ptr,size_t size,size_t nmemb,FILE *stream);
示例:
ssize_t w_size = fwrite(str,1,strlen(str),fp);
//定义块大小为1个字节,通过strlen()获取字符串长度,返回值为块数,也是字节数(块大小为一个字节)
函数参数:
ptr:想往文件当中些什么内容
size:定义往文件当中写的时候一个块(block:由程序员自己定义)是多大,单位字节(通常情况为一个字节)
nmemb:期望写多少块
stream:文件流指针
补充:总共往文件当中写的字的大小 = 块的大小 × 期望写多少块
返回值:
返回成功写入到文件中块的个数
(3)读文件:fread
size_t frean(void *ptr,size_t size,size_t nmemb,FILE *stream);
示例:
char buf[1024] = {0};
ssize_t r_size = fread(buf,1,sizeof(buf)-1,fp);
sizeof(buf)-1的原因:
1. 期望读sizeof(buf)-1个字节的数据,真实读到的字节数量取决于文件内容
2. 预留“\0”的位置,防止后续访问时,为读到“\0”而越界访问,造成程序奔溃
函数参数:
ptr:想从文件当中读到什么内容保存到ptr指向的内存空间中
(注意:空间需要程序员提前准备)
size:定义往文件当中写的时候一个块(block:由程序员自己定义)是多大,单位字节(通常情况为一个字节)
nmemb:期望写多少块
stream:文件流指针
返回值:
返回成功读入的文件块的个数
(4)移动文件流指针位置:fseek
int fseek(FILE*stream,long offset,int whence);
函数参数:
stream:文件流指针
offset:偏移量
whence:将文件流指针偏移到什么位置
SEEK_SET:文件头部
SEEK_CUR:当前文件流指针的位置
SEEK_END:文件末尾
返回值:
成功:0
失败:-1
(5)fclose:
int fclose(FILE *fp);
作用:
打开文件之后,一定记得关闭文件,否则就会造成文件句柄泄露(内存泄漏)
系统调用文件接口
(1)open
#include<fcntl.h>
int open(const char *pathname,int flags,mode_t mode);
参数:
pathname:要打开或创建的目标文件
flags:打开文件时,可以传入多个参数选项,用下面的一个或多个常量进行“或(|)”运算,构成flags。
必选项(必须选择一个且只能选择一个)
O_RDONLY:只读打开
O_ERONLY:只写打开
O_RDWR:读,写打开
可选项
O_CREAT:若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND:追加写
mode:当新创建一个文件的时候,指定新创建文件的权限;传递一个8进制的数字(就是权限,例如0664)
返回值:
成功:新打开的文件描述符
失败:-1
扩展:
文件描述符的数值:
0 标准输入(stdin)
1 标准输出(stdout)
2 标准错误(stdor)
(2)write
ssize_t write(int fd,const void *buf,size_t count);
参数:
fd:文件描述符
buf:将buf指向的内容写到文件当中去
count:期望写多少字节
返回值:
返回写入的字节数量
(3)read
ssize_t read(int fd,void *buf,size_count);
参数:
fd:文件描述符
buf:将从文件当中读到的内容写到buf指向的空间当中去
count:期望读多少字节
返回值:
返回读到的字节数量
(4)lseek
off_t lseek(int fd,off_t offset,int whence);
参数:
fd:文件描述符
offset:偏移量,单位字节
whence:偏移的位置
SEEK_SET:文件头部
SEEK_CUR:当前文件流指针的位置
SEEK_END:文件末尾
返回值:
成功:返回偏移位置,单位字节
失败:-1
(5)close
int close(int fd);
关闭文件描述符
文件描述符
(1)文件描述符概念:
数值:文件描述符是一个小整数
文件描述符可被打开的数量是有限的:
软限制:在操作系统资源允许内,可以使用ulimit修改数量:
ulimit -n [数字] root用户修改
硬限制:操作系统的资源(打开文件描述符需要耗费内存资源)
(2)查看文件描述符分配规则
查看文件描述信息:
/proc/[pid] //通过进程号查看文件信息
文件描述符分配规则:最小未使用原则
(3)从task_struct的角度理解文件描述符
进程描述打开文件的信息的结构体指针:
struct file_struct *files
该结构体指针指向一个结构体,叫做:
struct file_struct{...}
该结构体包括一个数组和若干个结构体指针,叫做:
struct file* fd_array[NR_OPEN_DEFAULT]; 数组
struct file* 结构体指针
每一个指针都指向一个结构体,叫做:
struct file{...}
这个结构体用于描述文件信息,包括:
文件名称、文件大小、文件权限、文件属性、文件所有者、文件在磁盘中的存储位置
其中文件描述符是数组fd_array[]的下标
(4)文件描述符和文件流指针的区别
源码角度理解文件流指针(struct_IO_FILE)是什么
在程序源码中 “struct_IO_FILE” 被重定义为结构体 “FILE” ,可以通过以下指令查询
vim /usr/include/libio.h
重定向
(1)符号
>:清空重定向>>:追加重定向
(2)从内核理解重定向
重定向就是将struct file*这个结构体指针的指向改变成为另一个struct file结构体(3)重定向接口
int dup2(int oldfd,int newfd);
作用:将newfd的值重定向为oldfd,即newfd拷贝oldfd参数:oldfd/newfd均为文件描述符
成功:1. 关闭newfd 2. 让newfd指向oldfd对用的struct file*结构体
失败:1. 如果oldfd是一个非法/无效的文件描述符,则重定向失败;newfd没有变化
2. 如果oldfd和newfd的值相等,则什么事情都不干
静态库&动态库
(1)库的概念
静态库和动态库都是程序代码的集合。一般为了方便程序提供给第三方使用,就将程序编写成库文件提供给第三方(用户)使用
(2)库的优点
不会泄漏源码(源码为公司的核心)
调用者不必担心库内部实现,只需要关注如何让使用(调用)即可
(3)动态库
特征:win:没有前缀,后缀为.dll
Linux:前缀为lib,后缀为.so
生成:
使用gcc/g++编译器,增加两个命令行参数
-fPLC
-shared
生成动态库的代码中不需要包含main函数(程序入口函数)
使用:
编译可执行程序时需要依赖动态库
-L [path]:指定动态库所在的路径
-l[动态库的名称(去掉前缀(lib)和后缀(.so)之后的名称)]:指定编译可执行程序时,依赖的动态库是哪个
4)找到动态库的方式
配置LD_LIBRARY_PATH(动态库的环境变量:LD_LIBRARY_PATH)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:[库文件路径]
将动态库放到可执行程序的路径下(不推荐)
放到系统库的路径下:/lib64(极力不推荐)
(5)静态库
特征:win:没有前缀,后缀为.lib
Linux:前缀为lib,后缀为.a
生成:
第一阶段,使用gcc/g++将源代码编译成目标程序(.o)
第二阶段,使用ar -rc命令编译目标程序称为静态库
注意:如果直接用源代码编译是不行的
软硬链接
(1)软连接
软连接文件:目标文件的快捷方式生成:
ln -s 源文件 软连接文件
注意事项:1. 修改软链接文件,源文件也会被修改
2. 原文件如果被删除,软链接文件还在的,修改软链接文件,会重现建立源文件,重新建立链接关系(慎重),一定要在删除源文件的时候,也将软链接文件也删除掉(注意)
3. 软链接文件和原文件的inode节点不一致
(2)硬链接
硬链接文件:目标文件的提升生成:
ln 源文件 硬链接文件
注意事项:1. 硬链接文件和原文件的inode节点一致
简单的文件系统
(1)文件在磁盘当中是如何进行存储的(ext2)
一个磁盘可以被划分成多个分区,每个分区都可以有自己的文件系统Block Group:ext2文件系统会根据分区的大小划分为数个Block Group,每个Block Group都有着相同的结构组成。
超级块(Super Block):存放文件系统本身的结构信息,有block和inode的总量,未使用block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间和等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了
GDT(Group Descriptor Table):块组描述符,描述块组属性信息
i节点表(inodeTable):存放文件属性,如文件大小、所有者、最近修改时间等
数据区(Data blocks):存放文件内容
1. 存储文件的规则并不是将文件进行连续存储的
2. 将文件内容进行离散存储(离散存储课避免产生大量内存碎片)
3. 已使用的区域标记为0,未标记的区域标记为1
块位图(Block Bitmap):记录数据区(Data blocks)中哪个数据块是否被占用
inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用
(2)创建新文件的4个操作
存储属性内核先找到一个空闲的i节点(假设为123123),内核将文件信息记录到其中
存储数据
根据文件需要存储的大小,在内核中找对应数量的空闲块,然后将内核缓冲区的数据依次复制进去
记录分配情况
文件内容按顺序存放到空闲块内,,内核在inode上的磁盘分布区记录上述块列表
添加文件名到目录
假设新文件名为abc,内核将入口(1231231,abc)添加到目录文件。文件名和inode之间的 对应关系将文件名和文件的内容及属性连接在一起。
总结:文件名称+inode:作为目录的目录项保存下来
————————————————版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/w2583467558/article/details/130319452
版权声明:
作者:SE_Yang
链接:https://www.cnesa.cn/2120.html
来源:CNESA
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论