标准库的IO接口
文件的打开方式
r:只读
r+:读写
w:只写
w+:读写
a:追加写(每次写入数据总是写入到文件末尾)
a+:追加读写
b:二进制操作
- r+ 和 w+ 的区别:r+读写打开文件时若文件不存在则报错;w+读写打开文件时若不存在则创建,若存在则清空原有内容
- a不仅仅是追加写,并且文件不存在还会创建新文件
- 如果不指定b则默认文件是文本操作,加上b则认为是二进制操作;区别在于有时一个特殊字符占据了两个字节的内存(读取一个100字节大小的文件,文本操作最终读取出来的数据可能不到100个字节)
IO接口
fopen
FILE* fopen(char* filename,char* mode); //filename--文件名称,mode--打开方式
- 返回值:返回一个FILE*的文件流指针作为文件的操作句柄;失败则返回NULL
- 打开文件之后 一定不能忘了close关闭文件
fread、fwrite
// buf--缓冲区,block_size--块大小,block_count--块个数,fp--文件流指针
size_t fread(char* buf,size_t block_size,size_t block_count,FILE* fp);
// data--数据首地址,block_size--块大小,block_count--块个数,fp--文件流指针
size_t fwrite(char* data,size_t block_size,size_t block_count,FILE* fp);
- fread和fwrite操作的数据实际大小是块大小*块个数
- 返回值:返回实际操作的块个数
- 例:读取一个文件size=10,count=2;若文件大小足够则返回2,如文件大小只有16字节则返回1,因为第二块没读满
- fread如果读到了文件末尾会返回0;
- 若读取1000个字节,块个数为1,文件大小只有512字节,虽然读取了512字节的数据但也会返回0
- fread/fwrite比较推荐块大小为1,因为块大小为1时块个数就是要操作的数据长度
fseek
int fseek(FILE* fp,long offset,int whence);
- 将文件的读写指针从whence位置偏移offset个字节–跳转文件读写位置
- 可偏移负数个字节(向前偏移),文件没有数据也可以跳转读写位置
fclose
int fclose(FILE* fp);
- 关闭文件流指针,释放资源
Linux下系统调用IO接口
open
打开指定文件
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有可能创建新文件,就一定要指定文件权限(八进制数字形式);若要操作文件,最好程序初始化时调用umask(0);将当前进程的umask值设置为0,这样在设置权限时就不需要再进行计算
- 返回值:一个非负整数----文件描述符(文件在代码中的操作句柄),失败返回-1
write
向指定文件中写入指定长度的数据
ssize_t write(int fd,char* data,size_t len);
- fd:open返回的文件描述符(文件操作句柄),通过这个fd指定要往哪个文件写入数据
- data:要写入文件的数据的空间首地址
- len:要写入的数据大小
- 返回值:返回实际写入文件的数据字节长度;失败返回-1
read
从文件中读取指定长度的数据,放到buf中
ssize_t read(int fd,char* buf,size_t len);
- fd:open返回的文件描述符
- buf:从文件中读取数据放到哪块缓冲区中的首地址
- len:想要读取的数据长度,这个len不能大于缓冲区大小
- 返回值:返回实际读取到的数据字节长度;失败返回-1
lseek
跳转读写位置
off_t lseek(int fd,off_t offset,int whence);
/*例*/ lseek(fd,0,SEEK_END); //返回的刚好为文件大小
- fd:open返回的文件描述符
- offset:偏移量
- whence:从哪里开始偏移。SEEK_SET:从文件起始位置,SEEK_CUR:从文件当前读写位置,SEEK_END:文件末尾
- 返回值:成功返回当前位置相对于起始位置的偏移量;失败返回-1
close
通过文件描述符关闭文件,释放资源
int close(int fd);
- fd:文件描述符
文件描述符
概念:文件描述符其实是内核中一个文件描述信息数组的下标,通过这个下标可以在内核中找到相应的文件描述信息,通过描述信息可以实现文件操作
- 文件描述符(操作文件的句柄)是一个非负整数
- 文件描述符的分配规则:最小未使用
- 一个进程中默认会打开三个文件:标准输入:0,标准输出:1,标准错误:2
- 为什么一个文件在不操作后一定要关闭?
释放资源,文件描述符是有限的。若不关闭文件,描述符用光,进程中就无法打开新文件了
文件描述符与文件流指针的关系
- 文件描述符:文件描述信息数组的下标,是一个非负整数,是系统调用的IO接口
- 文件流指针FILE是一个结构体(typedef struct _IO_FILE FILE),有一个成员就是文件描述符,是库函数IO接口的操作句柄
- 我们通常说的缓冲区实际上是文件流指针结构体中的缓冲区,这个缓冲区通常被称之为用户态缓冲区
缓冲区
- 向文件写入数据,并不会直接写入文件,而是写入缓冲区,刷新缓冲区的时候才会写入文件
- 系统调用接口是直接将数据写入文件的,系统调用接口没有缓冲区,只有库函数才有缓冲区
- exit退出会刷新缓冲区/_exit退出时不会刷新缓冲区
重定向
改变描述符所指向的文件描述信息,改变当前描述符所操作的文件,最终改变数据的流向。实际是描述符的重定向,将数据不再写入原本的文件,而是写入新的指定文件中
int dup2(int oldfd,int newfd) //描述符重定向函数
- 让newfd这个描述符也指向oldfd所指向的文件,这时候oldfd和newfd都能操作oldfd所指向的文件
文件系统
文件系统就是磁盘上管理文件的系统。linux下ext2文件系统将磁盘分为五个区域:超级块、inode bitmap1、data bitmap、inode、data
文件的存储:通过超级块找到inode位图/数据块位图,通过数据块位图快速找到空闲的磁盘块存储文件数据,通过inode位图快速找到空闲的inode节点,存储文件的元信息
- 目录项:文件名+inod节点号。Linux下文件的数据和文件的名称是分离的,文件的数据以及inode信息存储完毕之后,将目录项存储到父目录文件中
文件的获取:通过文件名到父目录文件中找到文件对应的目录项,得到文件的inode节点号,在磁盘超级块中找到inode节点区域,根据inode节点号,快速找到inode节点,得到数据存储的磁盘块号(数据块位置),进而获取到文件数据
软链接文件、硬链接文件
软链接文件/硬链接文件:给一个源文件创建一个软链接文件/硬链接文件,就可以通过被创建出来的软链接/硬链接文件来操作源文件
ln test.txt test.hard // 为源文件创建一个硬链接文件
ln -s test.txt test.soft // 为源文件创建一个软链接文件
软连接文件和硬链接文件的区别:
软连接文件 | 硬链接文件 |
---|---|
软链接文件是独立的文件,有自己的inode号,里面保存着源文件的路径,通过路径访问源文件数据 | 硬链接文件是一个文件的目录项(只是源文件的别名),和源文件共用一个inode号,通过inode节点访问源文件数据 |
删除源文件,软链接文件失效 | 删除源文件, 硬链接文件只是链接数-12 3,依然可以访问源文件数据 |
软链接文件可以对目录创建 | 硬链接文件不可以对目录创建 |
软链接文件可以跨分区创建 | 硬链接文件不可以跨分区创建 |
库的打包与使用
库文件:打包了一堆实现了常用功能的代码文件
打包流程
- 将各个高级语言代码(.c文件等)编译汇编成为机器指令。例:
gcc -c child.c -o child.o
;生成动态库时最好加上-fPIC产生位置无关的代码:gcc -c -fPIC child.c -o child.o
- 将所有的
.o
文件以及库文件打包生成自己的库文件
动态库的打包:gcc -shared child.o -o libmychild.so
;动态库的命名方式:lib***.so
静态库的打包:ar -cr libmychild.a child.o
;静态库命名方式:lib***.a
库的使用
生成可执行程序时链接使用:
- 将库文件放到指定路径下:/usr/lib64或/usr/lib
- 设置链接库的搜索路径环境变量,将当前库文件所在路径添加进去:export LIBRARY_PATH=$LIBRARY_PATH:.
- 使用gcc -L选项指定链接库的搜索路径:gcc main.c -o main -L. -lmychild
运行可执行程序时加载使用:仅仅针对动态库(只有动态库才会在运行时加载库文件)
- 必须将库文件放到指定路径下:/usr/lib64或/usr/lib
- 设置链接库的加载路径环境变量,将当前库文件所在路径添加进去:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.