基础IO 和 Linux 的文件系统

基础IO: 标准库IO接口 / Linux下系统调用IO接口 / 文件描述符 / 重定向 / 文件系统 / 库文件

标准库的IO接口

标准库的IO接口: fopen / fwrite / fread / fseek / fclose

  • FILE *fopen(char *filename,char *mode);(文件名,打开方式)
    打开方式: r – 只读,r+ – 读写,w – 只写,w+ --读写,a – 追加写,a+ --追加读写,b – 二进制操作
    追加写: 每次写入数据总是写入到文件末尾

    r+ 的读写和 w+ 的读写区别: r+ 读写打开文件,若文件不存在则报错 / w+ 读写打开文件,若文件不存在则创建,若存在清空原有内容

    a 不仅仅是追加写,若文件不存在还会创建新的文件

    返回值: 返回一个FILE* 的文件流指针作为文件的操作句柄;失败则返回NULL

  • size_t fread(char *buf,size_t block_size,size_t block_count,FILE *fp);(缓冲区,块大小,块个数,文件流指针)

  • size_t fwrite(char *data,size_t block_size,size_t block_count,FILE *fp);(数据首地址,块大小,块个数,文件流指针)
    注意: fread / fwrite操作的数据实际大小是块大小 * 块个数;块大小 = 10,块个数 = 2;意味着写入 / 读取 20个字节的数据
    返回值: 返回实际操作的块个数 例如:读取一个文件size = 10,count = 2;如果文件大小足够则返回 2 / 但是若文件大小只有 16字节,则会返回 1,因为第二块没有读满。fread 如果读到了文件末尾会返回 0,若读取 1000 个字节,块个数为 1,文件大小只有 512 字节,虽然读取了 512 的数据,但是也会返回 0.

  • int fseek(FILE *fp,long offset,int whence);(将文件的读写指针从 whence 位置偏移 offset 个字节) — 跳转文件读写位置

  • int fclose(FILE *fp);关闭文件流指针,释放资源。

fread / fwrite 比较推荐块带下为1,块个数是想要操作的数据长度
feek: 文件没有数据也可以跳转读写位置
对文件数据进行字符串操作的时候要注意文件数据中的 \0 这种数据

系统调用接口

标准库中的 IO — 都是库函数 — 库函数就是对系统调用接口的一种封装
系统调用接口: open / read / write / lseek / close

  • int open(char *filename,int flag,mode_t mode);
    filename:要打开的文件名称
    flag:选项参数 – 文件的打开方式 : 必选项 / 分选项
    必选项(只能选其一):O_RDONLY – 只读 O_WRONLY – 只写 O_RDWR – 读写
    可选择:O_CREAT – 文件存在则打开,不存在则创建;O_EXCL – 与O_CREAT同时使用,文件存在则报错,不存在则创建;O_TRUNC – 打开文件的同时清空原有内容;O_APPEND – 追加写,总是将数据卸载文件末尾
    mode:权限 – 如果使用了 O_CREAT有可能创建新文件,就一定要指定文件权限,八进制数字形式
    返回值:一个非负整数 – 文件描述符 – 文件的操作句柄;失败返回 -1;

  • ssize_t write(int fd,char *buf,size_t count);
    fd:open返回的文件描述符 – 文件操作句柄
    buf:要写入文件的数据空间首地址
    count:要写入文件的数据大小
    返回值:返回实际写入文件的数据字节长度;失败返回 -1;

  • ssize_t read(int fd,char *buf,size_t len);
    fd:open返回的文件描述符
    buf:从文件中读取数据放到哪块缓冲区中的首地址
    len:想要读取的数据长度,注意这个len不能大于缓冲区的大小
    返回值:返回的是十几度渠道的数据自己长度,错误返回 -1

  • off_t lseek(int fd,off_t offset,int whence);
    fd:open返回的文件描述符
    offset:偏移量
    whence:从哪里开始偏移 SEEK_SET – 文件起始位置 SEEK_CUR – 文件当前读写位置 SEEK_END – 文件末尾
    返回值:成功返回当前位置相对于起始位置的偏移量;失败返回 -1

  • int close(int fd); – 通过文件描述符关闭文件,释放资源

文件描述符

文件描述符: 是一个非负整数 – 操作文件的句柄
文件描述符其实就是内核中一个进程打开的文件描述信息的下标 – 通过这个下标可以在内核中找到相应的文件描述信息,通过这个文件描述信息,可以实现文件的操作,实际上是一个数字,从 3 开始,因为 0 1 2被比标准输入标准输出和标准错误占了
为什么打开一个文件,如果不操作了一定要关闭,释放资源;文件描述符实际是有限的,文件描述符用光,则进程中就打不开新文件了
一个程序运行起来,进程中默认会打开三个文件:标准输入 - 0 -stdin / 标准输入 - 1 -stdout / 标准错误 - 2 -stderr
printf 是打印数据到标准输出, close(1),就是把标准输出关闭了,打开新文件后,printf并没有直接把数据打印出来,而是在刷新缓冲区之后,将数据写到文件中
printf 并非真的一定要把数据写入标准输出文件,而是因为printf函数中操作文件的时候的描述符是 1
原本向 1 中写入数据,就是向标准输出写入,然后当 1指向新文件后,这时候printf就会将数据写入到指定新的文件中

重定向

重定向: 将数据不再写入原本的文件,而是写入新的指定文件中 – 实现方式就是替换这个描述符对应的文件描述信息
实际上是描述符的重定向,改变描述符所指向的文件,就改变了数据的流向

  • int dup2(int oldfd,int newfd) – 描述符重定向函数
    让 newfd这个描述符也指向 oldfd所指向的文件,这时候 oldfd和newfd都能操作oldfd所指向的文件

文件系统

文件系统:Linux下的ext2文件系统为例
在这里插入图片描述
存储一个文件需要找到空闲的磁盘块存储文件数据,以及找到一个未被使用的 inode节点存储自己的元信息,针对每一个磁盘块做一个整体的位图,这样就可以快速找到空闲的磁盘块,类似于目录。
文件的权限就是使用位图存储:一个用户有没有权限就是 0 / 1的关系;位图可以节省大量的空间以及二进制的与或非是很快的。
位图: 一连串的二进制比特位,用来做一种数据标记
文件存储流程: 通过超级块找到 inode 位图 / 数据块位图,通过数据块位图快速的找到空闲的磁盘块存储文件数据,通过inode 位图快速找到空闲的 inode节点存储文件的元信息。 – 文件数据存储完毕,每个文件拥有唯一的 inode节点。文件的数据存储完毕之后,还需要在这个文件的所在目录下(目录文件中),记录这个文件名称以及inode节点号(目录项)
文件读取流程: 通过文件名打开一个文件,在所在的目录文件中通过文件名找到这个文件的 inode节点号,在磁盘超级块中找到 inode节点区域,根据inode节点号,快速找到inode节点,得到数据存储的磁盘块号,取出数据。

软链接文件 / 硬链接文件: 给源文件创建一个软链接文件 / 硬链接文件,就可以通过被创建出来的软链接文件 / 硬链接文件来操作源文件
为源文件创建一个硬链接文件:In test.txt test.hard
为源文件创建一个软链接文件:In -s test.txt test.soft
软链接文件和硬链接文件的区别:
硬链接文件:本质上和源文件没有什么不同,都是一个文件名称,与源文件共用同一个 inode节点,通过自己的 inode节点访问源文件数据
软链接文件:本质上是一个独立的文件,有自己的inode节点,文件数据中保存源文件的路径,通过这个路径访问源文件数据
区别:
删除源文件,软链接文件失效,硬链接文件只是链接数减一;连接数: – 一个inode节点对应有几个目录项
删除一个文件,文件并不会被立即删除,而是直接删除了目录项信息,inode中的链接数减一,之后链接数为零时,才会真正删除文件
软链接文件可以跨分区,硬链接文件不可以
软链接文件可以对目录创建,硬链接文件不可以
每个磁盘分区都有自己一套 inode节点,跨分区建立硬链接,与源文件 inode节点相同,这个inode节点号到底是指向哪个分区的inode节点?Linux下目录结构是唯一的,分区可以有多个,可以挂载在不同的目录上,目录本身就是跨分区的,因此硬链接不能对目录进行创建。

库文件

库文件: 打包了一堆实现了常用功能的代码文件

将各个 .c 文件编译汇编成目标文件
例如:gcc -c chile.c -o chile.o
动态库打包:gcc -c -fPIC child.c -o child.o -> gcc shared child.o -o libmychild.so;动态库的命名方式 lib***.so
静态库打包:gcc -c child -o child.o -> ar -cr libmychild.a child.o 静态库命名方式 lib***.a

库的使用
生成可执行程序时链接使用: gcc main.c -o main -lmychild -l指定的库名称

  1. 必须将库文件放到指定的路径下
  2. 设置连接库的搜索路径环境变量 – 将当前库文件所在的路径添加进去: export LD LIBRARY_PATH=$LD_LIBRARY_PATH
  3. 使用gcc -L选项指定连接库的搜索路径:gcc main.c -o main -L./ -mychild
    ldd main 查看main这个可执行程序以来的动态库
    运行可执行程序时加载使用:仅仅针对动态库才会在运行时加载库文件

静态库的链接:

  1. 将静态库放到指定的路径下,然后使用–L指定静态库的位置:gcc main.c -o main -L./lib -lmychild
  2. 对于所有的库都是用静态链接:gcc -static main.c -o main -L./lib -lmychild
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值