基础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
文章版权归作者所有,未经允许请勿转载。

THE END
打赏
海报
基础IO——文件系统与接口、文件描述符、静/动态库、重定向
本文主要介绍了LInux操作系统中关于文件系统与文件接口、静态库与动态库、重定向以及文件描述符的相关内容。 目录 C文件接口 (1)打开文件:fopen (2)写文……
<<上一篇
下一篇>>