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