Linux:如何操作、理解磁盘中的文件和相关文件知识(文件系统、软/硬链接)

1.标准库的IO接口

要对磁盘上的文件进行操作,我们首先要学习标准库IO接口是怎么使用的。下面简单介绍一些常用的接口。
首先知道-》标准库IO操作句柄:FILE* —文件流指针,一个进程要使用一个文件就要向这个文件分配一个文件流指针,通过这个文件流指针对其进行操作。

FILE *fopen(const char *path, const char *mode);
函数 fopen 打开文件名为 path 指向的字符串的文件,将一个流与它关联。
FILE *fopen(const char *path, const char *mode);
mode参数指向一个字符串,设置打开方式。
打开方式:

   r      打开文本文件,用于读。流被定位于文件的开始。

   r+     打开文本文件,用于读写。流被定位于文件的开始。

   w      将文件长度截断为零,或者创建文本文件,用于写。流被定位于文件的开始。

   w+     打开文件,用于读写。如果文件不存在就创建它,否则将截断它。流被定位于文件的开始。

   a      打开文件,用于追加 (在文件尾写)。如果文件不存在就创建它。流被定位于文件的末尾。

   a+     打开文件,用于追加(在文件尾写)。如果文件不存在就创建它。读文件的初始位置是文件的开始,但是输出总是被追加到文件的末尾。

int fclose(FILE *stream);
函数 fclose 将名为 stream 的流与它底层关联的文件或功能集合断开。如果流曾用作输出,任何缓冲的数据都将首先被写入到标准输出文件,使用fflush 。

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
函数 fread()从stream的流位置开始,读取nmemb块数据,每块size大小,存储到ptr指向的位置。

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
函数fwrite() 从ptr获取数据,向stream流位置开始写入nmemb块数据元素,每块大小为size。

int fseek(FILE *stream, long offset, int whence);
函数fseek()为文件流stream设置指示器位置。
获得的位置是whence偏移offset字节的位置。
whence设置有三个
SEEK_SET:文件起始位置
SEEK_CUR :当前文件流指针位置
SEEK_END:文件末尾

char *fgets(char *s, int size, FILE *stream);
函数fgets至多获取一个小于size长度的字符串,或遇到EOF和\0结尾结束获取。

printf fprintf sprintf 这些函数用于格式化输出数据到文件。
scanf fsanf sscanf这些函数用于格式输入数据到文件。

2.系统调用接口

1中提到的标准库函数都是封装以下系统调用函数来实现的,如果想要了解其原理,那么系统调用接口也是必须要了解的。
#include<fcntl.h>
int open(const char *pathname, int flags)
int open(const char *pathname, int flags, mode_t mode)
pathname:文件路径
flags:文件打开方式选项标志
必选选项:
O_RDONLY:只读方式打开
O_WRONLY:只写方式打开
O_RDWR:可读可写方式打开
可选选项:
O_CREATE:文件不存在则创建
O_EXCL:和O_CREAT一起用,文件存在则报错
O_TRUNC:打开文件同时将文件长度截断为0
O_APPEND:写追加方式
mode:若创建文件时,设置文件权限(四位八进制数字)
文件权限=mode&(~umask)
返回值:文件描述符——一个正整数 错误返回 -1

ssize_t write(int fd,const void* buf, size_t count)
fd:文件描述符
buf:向文件写入的数据
count:向文件写入的数据长度
返回值:成功返回写入的数据长度,失败返回-1

ssize_t read(int fd, void *buf, size_t count)
fd:文件描述符
buf:读取的数据存放缓冲区
count:要读取的数据长度
返回值:成功返回读取的数据长度,失败返回 -1

int close(int fd)
fd:文件描述符
返回值:成功返回0,失败返回 -1
off_t lseek(int fd, off_t offset, int whence);
offset:偏移量
whence:偏移位置
SEEK_SET:文件起始位置
SEEK_CUR:文件位置指针当前位置
SEEK_END:文件末尾位置

3.文件描述符和文件流指针的关系

标准库接口使用文件流指针FILE*
系统调用接口使用文件描述符int
标准库调用系统接口
文件流指针是一个结构体指针,其中包含文件描述符。
我们常见的三个文件流指针分别是标准输入,标准输出,标准错误,下面代码证明上面的结论
在这里插入图片描述在这里插入图片描述
执行结果:
在这里插入图片描述

4.什么是缓冲区?

举个例子感受一下
在这里插入图片描述运行这个程序,结果:
在这里插入图片描述当第一条数据输出后,等待了3s后剩下两条数据才输出。
而是巧合吗?当然不是,第一个函数是系统调用接口,剩下两个函数都是库函数接口。
这三个函数都是对文件描述符为1的标准输出文件进行操作。
其实:我们所说的缓冲区实际上就是文件流指针为每个文件所维护的一个缓冲区,是一个用户态缓冲区。
在这里插入图片描述在FILE的定义中也能找到描述缓冲区信息的指针。
因此,我们在使用库函数进行文件操作的时候,并不是执行一条操作就立刻修改文件,而是在文件缓冲区中存储,当文件缓冲区的大小达到了一定的大小,才会进行IO操作,这样大大减少了IO次数,提高了效率。

5.什么是文件描述符?

简单地说每一个进程都是一个PCB,如果一个进程要对文件进行操作,那么在这个PCB中肯定要保留文件的描述信息而不是把文件整个直接拷贝进PCB,这样内存也不可能放下。在PCB中有一个files_struct的结构体,这个结构体下有一个fd_array[ ]数组,这里面就保存了所有文件的描述信息。示意图如下。
而文件描述符,其实就是这个数组对应的下标,前三个在每个PCB创建的时候就已经被使用。
在这里插入图片描述
在/usr/src/kernels/3.10.0-1062.4.3.el7.x86_64/include/linux/sched.h 1300行可以找到task_struct的定义,也就是PCB,在第1499行找到了描述文件结构体的定义
在这里插入图片描述
在/usr/src/kernels/3.10.0-693.el7.x86_64/include/linux/fdtable.h找到了files_struct的定义,其中fd_array这个数组中就保存了每个文件的描述信息。
在这里插入图片描述

6.重定向

举个例子,我们关闭了文件描述符为1的文件,然后打开了一个新的文件名为tmp.txt,再输出一条信息:这是一条数据。
在这里插入图片描述执行,发现没有任何输出。
在这里插入图片描述
打开tmp.txt,发现数据打印到了文件里。
在这里插入图片描述
实际上原因是我们关闭了1,也就是标准输出文件描述符,然后新打开的文件被被赋予的文件描述符是1,输出语句默认将输出结果写入到文件描述符为1的文件中,所以写入到了tmp.txt中。这也和文件描述符的最小分配原则有关,当打开一个进程中没有的新文件的时候,会为其分配文件描述符,将从0开始,找到第一个未使用的文件描述符赋给它。
这个例子也很好的说明了重定向,就是改变数据的流向,更换数据要流向的文件。
函数dup2直接可以做到
int dup2(int oldfd, int newfd);
dup2将newfd指向的文件更改成oldfd指向的文件。
使用dup2会进行如下操作:

  1. 关闭newfd指向的文件。
  2. 拷贝oldfd对应的文件信息。
    注意:使用dup2后,一个文件就会有两个文件描述符同时指向该文件,为了不造成空间的浪费,最好手动关闭一个。

7.文件系统

再Linux中磁盘是这样分区的,swap分区负责暂时保存需要和内存交换的数据,大小一般是内存的二倍。其他磁盘空间是各种文件分区。
磁盘中数据是分块存储的,每一个块大小设置为2的幂次。有一个Inode节点,这个inode节点存储一个文件的各种信息。
在这里插入图片描述存储文件的流程:
1.inode_bitmap在inode table中找到空闲的inode节点。
2.data_bitmap中找到空间的数据块,将数据块信息记录在inode节点中。
3.将文件数据写入数据块中。
4.将文件名和inode节点号写入父目录文件中。

8.软/硬链接

在linux系统中有种文件是链接文件,可以为解决文件的共享而使用。
在这里插入图片描述第一列的是文件类型,d是目录文件,-是普通文件,l就是链接文件,这个链接文件是软链接。
后面有一列数字的,这个是该文件的当前链接数,这个链接数表示的是硬链接数。
硬链接:是通过和源文件共同使用同一个inode节点,inode节点在7.文件系统说过,既然是同一个inode节点,那么它们本质上操作的也是同一块磁盘中的数据。
软链接:创建软链接文件,会创建一个新的inode节点,这个inode节点中保留的数据块中,存储着要链接的文件名。
在这里插入图片描述虽然tmp.soft和tmp_2.soft链接着同一个文件,但是由于给的路径方式不同,他们存储长度也就不相同,存储长度就是路径的字节长度。
图示两种链接的方式:
在这里插入图片描述用途:
软链接可以链接目录文件而硬链接不行。(因为引入了对目录的硬连接就有可能在目录中引入循环,在目录遍历的时候系统就会陷入无限循环当中,这样导致无法定位到访问目录。软链接可以通过文件类型判断终止条件不会产生死循环。)
软链接可以跨文件分区链接硬链接不行。(不同文件系统的inode节点会引起冲突所以硬链接不可以)
硬链接可以防止误删,当当前文件的链接数为1时,删除该文件才是真正的在磁盘上删除,当链接数大于1,删除只会让链接数减1。
对软/硬链接文件操作都能访问到源文件。

发布了35 篇原创文章 · 获赞 13 · 访问量 2113

猜你喜欢

转载自blog.csdn.net/weixin_42458272/article/details/103960550
今日推荐