基础IO
I/O:input & output,是一切实现的基础。
- stdio标准IO
- sysio系统调用IO,又叫文件IO
1.C文件接口及标准输入、输出和错误
fopen、fclose:
FILE* fopen(const char * filename, const char * mode)
作用:以mode方式打开一个名为filename的文件
参数:filename文件名;mode文件的打开方式
返回值:文件流指针
int fclose(FILE* stream);
作用:关闭一个打开的文件
参数:要关闭的文件的流指针
返回值:成功返回0,失败返回-1
读、写文件:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
作用:读取文件中的内容
参数:ptr读出的内容;size一次读取的长度;nmemb读取的次数;stream读取的文件流指针
返回值:成功读取的字节数,如读取失败返回0
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
作用:写入文件中的内容
参数:ptr写入的内容;size一次写入的长度;nmemb写入的次数;stream写入的文件流指针
返回值:成功写入的字节数,如读取失败返回0
标准输入、标准输出和标准错误:
- C默认会打开三个输入输出流,分别是stdin, stdout, stderr
- 仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针
2.系统调用IO
open:
open()函数——打开或创建一个文件
#include <sys/types.h>
#include <sys/stat.h>
#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_CREAT:若此文件不存在则创建它
- O_TRUNC:如果文件已存在,若文件存在,则长度被截为0,属性不变
- O_APPEND:如果文件已有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容。
- 必选项,以下三个中必须选定一个,且仅允许指定一个
- mode:设置文件的访问权限,可以直接给八进制的文件权限位(一般给0644)
- 返回值:
- 成功,返回新分配的文件描述符
- 失败,返回-1
由open返回的文件描述符一定是该进程尚未使用的最小描述符。
write:
write()——向打开的设备或文件中写数据
#include <unistd.h>
ssize_t write(int fd,const void* buf,size_t count);
作用:向文件中写入内容
参数:fd文件描述符;buf写入的字符串;count写入的大小
返回值:成功返回写入成功的字节数,失败返回-1
read:
read()函数——从打开的设备或文件中读取数据
ssize_t read(int fd, void *buf, size_t count);
作用:从文件中读取内容
参数:fd文件描述符;buf输出型参数,保存读出的内容;count读取的大小
返回值:成功返回读取成功的字节数,失败返回-1
close:
close()函数——关闭一个已打开的文件
#include <unistd.h>
int close(int fd);
参数:要关闭的文件的文件描述符
返回值:成功返回0,失败返回-1
3.文件描述符
Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2。(stdin、stdout、stderr)
文件描述符就是从0开始的正整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,于是就有了file结构体,用来表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个结构体指针files, 指向一个files_struct结构体,该结构体最重要的部分就是包含一个指针数组,每个元素都是struct file结构体一个指向打开文件的指针。所以本质上,文件描述符就是该数组的下标。只要拿着文件描述符,就可以找到对应的文件 。
文件描述符的分配规则:在file_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
4.dup2系统接口重定向
什么是重定向?
重定向将一个文件描述符所对应fd_array数组当中的元素所指的struct file*的内容更改为另外一个文件信息,意味着当前文件描述符所对应的struct file指向的文件被另外一个文件替代了。
-
1. > 清空重定向,清空文件之间的内容,在重定向到文件中
-
2. >> 追加重定向,追加在文件之后
-
3. < 输出重定向
缓冲区
我们的缓冲区是由语言提供的,由FILE 结构体进行维护。
- 一般C库函数写入文件是全缓冲的,而写入显示器是行缓冲。
- printf fwrite 库函数是用户级的缓冲区,当发生重定向到普通文件的时候,数据的缓冲方式就由行缓冲变成了全缓冲。
C标准库提供的缓冲区叫用户级缓冲区,它的作用是将缓冲区的内容刷新到系统中,系统在调用write函数刷新到文件或者标准输出。
dup2系统接口
#include <unistd.h>
int dup2(int oldfd,int newfd);
newfd是oldfd的一份拷贝。newfd复制参数oldfd所指的文件描述符,并将它返回。此新的文件描述符和参数oldfd所指的是同一个文件,共享所有的锁定、读写位置和各项权限。
5.文件系统
我们使用l -i的时候看到的除了看到文件名,还看到了文件元数据
每行包含7列:
- 模式 2. 硬链接数 3. 文件所有者 4. 组 5. 大小 6. 最后修改时间 7. 文件名
其实这个信息除了通过这种方式来读取,还有一个stat命令能够看到更多信息
这图中引入一个inode。
超级块:包含文件系统的信息(inode总个数、块总个数、每个块的inode个数、每个块组的块个数)
块组描述符:包含文件系统中各个块组的状态(块组中空闲块和inode的数目)
Block Bitmap(块位图): 描述Data blocks区域中哪一个块是空闲的,哪一个是使用的
inode Bitmap(inode位图):描述inode结点在inode Table当中哪里是空闲的,哪里被使用
inode列表:描述文件在Data blocks区域当中都占用哪些块
数据区(Data blocks):存放文件内容
-
如何存储文件数据?
- 将文件分成不同的block块,从Block Bitmap当中查找,Data blocks区域中空闲的块,将 文件存储在不同的空闲块中
- 需要对文件进行描述,从inode Table中查找空闲的inode结点,从inode Table中获取inode结点,使用inode结点去描述文件存储信息
- 文件名称+inode结点号作为目录的目录项存储起来
-
如何获取文件数据?
- 在目录当中根据文件名称和inode结点号,找到inode结点
- 根据inode信息,查找到文件在Data blocks当中对应的块,将块组织起来,就获取到了文件信息。
6.软/硬链接
软链接:
- 创建:ln -s [源文件] [创建出来软链接的名称]
- 软链接相当于重新创建了一个文件,这个文件有独立的inode结点,但是文件内容是另外一个文件的路径。
- 修改软链接文件或者源文件是一样的。
- 但是删除软链接对源文件没有任何影响(类似windows中的快捷方式)
硬链接:
- 创建:ln [源文件] [创建出来硬链接的名称]
- 硬链接文件和源文件具有相同的inode结点信息,不是一个独立的文件(类似重命名)