1、标准库IO接口
stdout/stdin/stderr
FILE *fopen/fwrite/fread/fseek/fclose
fprintf/fgets
文件流指针(FILE * fp)
stdin(标准输入)
stdout(标准输出)
stderr(标准错误)
打开文件
FILE *fopen(const char* filename,const char*mode)
mode:“r/r+/w/w+/a/a+”
r:只读
r+:可读可写
w:只写/文件不存在则创建文件,文件存在则清空文件内容
w+:可读可写
a:以追加的方式写入(追加到文件末尾),文件不存在则创建文件(打开文件直接显示在文件末尾)
a+:以读和追加写的方式,打开文件显示在文件头,当开始写数据时,跳转到文件末尾
注意:
1>w/w+每次都会清空文件原有的内容后写入数据
2>fread/fwrite返回值都是块个数,而并非实际读取/写入的数据字节长度,因此使用时通常块的大小为1,而块的个数时数据长度,这样才能返回时机读取/写入的数据长度
3>对文件内容进行处理的时候需要注意文件中有可能会出现空字符,因此进行字符串匹配处理的时候需要注意
2、学习系统调用IO接口
常用接口:open/read/write/lseek/close
(1)open
int open(const char *pathname,int flags,mode_t mode);
参数信息:
- pathname:带路径的文件名
- flags:选项参数
- mode:若有可能创建新文件则需要指定文件权限(例如输入权限为可读可写可执行时,需要输入0777,不能输入777)
mode_t umask(mode_t mask);//设置文件的创建权限掩码,决定了文件的默认权限用法:给定权限 & ~掩码
注:如果有O_CREAT(创建)一定要使用mode,设置权限
例:
umask(0);
int fd = open("./test.txt",O_RDWR | O_CREAT, 0777);
必选项:
O_RDONLY/O_WRONLY/O_RDWR 只读/只写/可读可写-----三种只能选择其一
可选项:
O_CREAT 文件不存在则创建新文件,存在则打开
O_EXCL 跟O_CREAT同时使用,若文件不存在则创建,已经存在则报错O_TRUNC 打开文件的同时截断文件
O_APPEND 写入数据的时候总是追加在文件末尾
w+:O_RDWR | O_CREAT | o_TRUNC
a+:O_RDWR | O_APPEND | O_CREAT
返回值:文件的操作句柄----文件描述符就是操作句柄–正整数(出现错误则返回-1)
(2)write
ssize_t write(int fd, const void *buf, size_t count);
fd:文件描述符
buf:要写入文件的数据缓冲区首地址
count:要写入的数据长度
返回值:实际写入文件中的数据字节长度(不一定等于count),失败返回-1
(3)read
ssize_t read(int fd, void *buf, size_t count);
fd:文件描述符
buf:缓冲区首地址,放入读取到的数据
count:要读取的数据长度,大小不能大于缓冲区大小
返回值:返回读取到的数据字节长度(不一定等于count),返回0表示读到文件末尾,失败返回-1
mode_t umask(mode_t mask);
设置当前调用进程的文件创建权限掩码(仅对当前进程)
(4)lseek
off_t lseek(int fd, off_t offset, int whence);
第一个参数是文件描述符
第二个参数是偏移量
第三个参数是跳转到的位置
(5)close
int close(int fd);
关闭文件描述符,不关闭文件描述符会导致资源泄漏
标准库IO接口的操作句柄是文件流指针
系统调用IO接口的操作句柄是文件描述符
3、文件描述符(fd)
文件描述符就是内核中struct file* fd arry[]数组的下标,进程可以通过这个下标找到文件的描述信息,进而操作文件
文件描述符的分配规则:最小未使用
一个进程运行起来,默认会打开三个文件,分别是:
标准输入(0)
标准输出(1)
标准错误(2)
所以文件描述符默认从3开始
4、文件流指针和文件描述符的关系
文件流指针:stdin、stdout、stderr
文件描述符:标准输入(0),标准输出(1),标准错误(2)
文件流指针是库函数IO接口的操作句柄
文件描述符是系统调用IO接口的操作句柄
文件流指针是一个结构体封装了文件描述符
5、重定向的实现原理
重定向:针对文件描述符所对应的文件描述信息的重定向,此时操作的文件描述符/ 文件流指针并没有发生改变,但是实际操作的文件已经发生改变
常见的重定向:>、>>、<
ls 1 >> a.txt:将标准输出重定向到a.txt文件
ls 2 >> &1:将标准错误重定向到标准输出
使用dup2系统调用函数
#include <unistd.h>
int dup2(int oldfd, int newfd);
在使用dup2函数时,首先newfd指向的文件与之解除关系,然后指向oldfd所指向的文件,此时newfd与oldfd都指向同一个文件
如何在minishell中实现重定向
ls -l >> a.txt
在命令字符串中若有重定向符号
追加重定向 >>
清空重定向 >
则从符号起始位置进行截断,前边是命令,后边是重定向文件
解析之后,得到文件名,以及重定向类型
在子进程中以不同重定向的方式打开文件,进行重定向即可 dup2(fd,1)
6、文件系统==磁盘文件管理
(1)存储文件的流程:通过databitmap快速找到空闲磁盘块,存储文件数据,通过inodebitmap快速找到空闲inode节点,保存文件元信息,将文件名和inode节点号作为目录项存储文件所在目录的文件中
(2)获取文件流程:通过文件名在所在目录文件的目录项找到文件名对应的目录项,从目录项中获取inode节点号,通过inode节点号,在文件系统的inodetable中获取到inode节点,进而获取到文件数据存储在磁盘的位置,进而获取到文件数据。
7、软链接文件与硬链接文件的区别
不管是软链接文件还是硬链接文件,目的都是为了通过这些文件访问到源文件
软链接文件是一个独立的文件,有自己的inode结点,通过保存源文件的路径进而访问源文件
硬链接文件跟源文件用有相同的inode节点,类似于源文件的一个别名(其实跟源文件已经没有什么区别,都只是一个文件的名称),通过自己的inode结点访问源文件
区别
删除源文件,软链接文件失效,而硬链接文件只是链接数-1
软链接可以针对目录创建,硬链接不可以
软链接可以跨分区建立,硬链接不可以
ls -i 查看文件的inode结点号
对于小文件进行打包存储可以节省inode结点的使用
8、动态库与静态库的生成与使用
生成: 将每个.c文件进行预处理、编译、汇编生成自己的目标代码文件
动态库:gcc -fPIC -c child.c -o child.o
gcc --share -o libmychild.so child.o
-fPIC的作用:产生一个位置无关代码
静态库:gcc -c child.c -o child.o
ar -cr libmychild.a child.o
使用:
在生成可执行程序的时候链接使用
在运行可执行程序的时候加载使用:仅针对动态链接生成的可执行程序
**生成可执行程序时链接时使用库:**库文件的搜索路径是默认的路径 /lib64
(1)将库文件放置到指定路径下
(2)设置环境变量LIBRARY_PATH,将库文件所在目录的路径添加到这个环境变量的值中
(3)使用gcc -L 选项,指定gcc库文件的搜索路径,gcc main.c -o main -L ./ -lmychild(mchild是库名称,不需要前缀和后缀)
运行时使用:运行时加载库文件,也会去默认的搜索路径下去找库文件 /lib64
(1)将库文件放置到指定的路径下
(2)设置环境变量 LD_LIBRARY_PATH export LD_LIBRARY_PATH = ${LD_LIBRARY_PATH}:./
通常我们在链接静态库的时候,并不是使用-static去指定gcc从进行静态链接,因为-static的功能是在生成可执行程序的时候,所有的依赖库都是静态库,而不仅仅是指定的那个库。
通常在链接一个静态库的时候,都是将这个静态库放到指定路径下,就会直接链接这个静态库,而其他的依赖库依然会使用动态库链接