【Linux学习】第六弹:基础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:作为目录的目录项保存下来


 持续制作更新中, 创作不易求点赞,欢迎大家交流讨论!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值