概念
- 文件IO是没有缓冲区的,只有标准IO有缓冲区
- 文件IO函数是由操作系统提供的,与操作系统绑定,又称之为系统调用
- 文件IO函数是通过文件描述符来维护一个文件
文件描述符
-
当我们要去操作一个文件的时候首先要打开一个文件。尝试打开一个文件的时候,系统会自动给这个文件分配一个编号,这个编号就是文件描述符,用文件描述符来维护描述这个文件
-
标准IO是对文件IO的二次封装,最终标准IO依然会去调用文件IO,所以FILE结构体中会有文件描述符成员
_fileno
- 在文件IO的基础上,封装了一个缓冲区,同时将文件描述符也一起封装到了FILE结构体中
-
文件描述符的本质
-
文件描述符的本质是数组下标,该数组的容量默认为1024,范围是[0,1023]
-
文件描述符是有上限的,所以在不使用的情况下,需要关闭
-
获取文件描述符的规则是,从小向大,依次获取,直到找到一个没有被使用的元素,返回该元素的下标。
-
其中有三个特殊的文件描述符,分别为0,1,2
特殊的流指针(FILE*) 特殊文件描述符 结构体位置 FILE *stdin 0 stdin->_fileno FILE *stdout 1 stdout->_fileno FILE *stderr 2 stderr->_fileno
-
-
getdtablesize(void)
获取一个进程最多能打开几个文件描述符
文件IO函数
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); int open(const char *pathname, int flags, mode_t mode);
-
-
参数:
-
char *pathname
:指定要打开的文件路径以及名字 -
int flags
:打开方式-
参数 功能 O_RDONLY 只读 O_WRONLY 只写 O_RDWR 读写 -
以上三种,必须且只能包含一个
参数 功能 O_APPEND 追加的方式 O_CREAT 如果文件不存在则创建 O_TRUNC 如果文件存在,则清空 -
如果需要多个选项,可以用按位或连接
-
-
mode_t mode
:在文件创建时,指定文件的权限使用 当flags中指定了O_CREAT或者O_TMPFILE的时候,mode参数必须填写
当flags没有指定上述两种选项,则mode参数会被忽略
-
-
返回值:
- 成功:返回新的文件描述符
- 失败:返回-1,更新errno
-
fopen的打开方式,在open中组合
-
fopen open r O_RDONLY w O_WRONLY |O_CREAT|O_TRUNC a O_WRONLY|O_CREAT|O_APPEND r+ O_RDWR w+ O_RDWR|O_CREAT|O_TRUNC a+ O_RDWR|O_CREAT|O_ARREND
-
close
-
功能:关闭文件,释放文件描述符对应的该空间
-
原型
-
#include <unistd.h> int close(int fd);
-
-
参数:
int fd
:指定要关闭的文件
-
返回值:
- 成功:0
- 失败:-1,更新errno
write
-
功能:将数据写入文件中
-
原型:
-
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
-
-
参数
int fd
:指定要写入哪个文件中,填对应的文件描述符void *buf
:指定要输出的数据首地址,可以输出任意类型的数据size_t count
:指定要输出的数据大小,以字节为单位
-
返回值:
- 成功:返回成功输出的字节数
- 失败:返回-1,更新errno
read
-
功能:从指定的文件中读取数据
-
原型:
-
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
-
-
参数
int fd
:指定要读取哪个文件,填对应的文件描述符void *buf
:存储读取到的数据size_t count
:指定要读取多少个字节
-
返回值
- >0:成功,返回成功读取到的字节数
- =0:读取到文件结尾
- -1:失败,返回-1,更新errno
-
read不会自动补充\0字符
-
read函数遇到\n不会停止读取
lseek
-
功能:修改文件偏移量
-
原型:
-
#include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t offset, int whence);
-
-
参数:
int fd
:指定要修改哪个文件偏移量,填上对应的文件描述符off_t offset
:偏移量,新的位置为whence加上offset参数- 往结尾偏移,填正数
- 往开头偏移,填负数
int whence
:SEEK_SET
,文件开头位置SEEK_CUR
,文件当前位置SEEK_END
,文件结尾位置
-
返回值
- 成功:返回文件偏移后的当前位置,距离文件开头的偏移量
- 失败,返回
(off_t)-1
,更新errno
-
lseek(fd, 0, SEEK_END);
,返回值为文件的大小
文件属性相关函数
stat
-
功能:获取文件所有属性
-
原型:
-
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *pathname, struct stat *statbuf);
-
-
参数
-
char *pathname
:指定要获取哪个文件的属性,填上对应的文件路径以及文件名 -
struct stat *statbuf
:存储获取到的文件属性
-
//结构体类型
struct stat {
dev_t st_dev; /* 设备信息 */
ino_t st_ino; /* inode号 */
mode_t st_mode; /* 文件类型以及文件权限 */
nlink_t st_nlink; /* 硬链接数 */
uid_t st_uid; /* 文件所属用户的id号 */
gid_t st_gid; /* 文件所属组用户的id号gid */
dev_t st_rdev; /* 设备ID */
off_t st_size; /* 文件大小,单位字节 */
blksize_t st_blksize; /* 一个IO块大小 */
blkcnt_t st_blocks; /* 块个数 */
struct timespec st_atim; /* 最后一次被访问的时间 */
struct timespec st_mtim; /* 最后一次被修改的时间 */
struct timespec st_ctim; /* 最后一次被改变状态的时间 */
};
-
返回值:
- 成功:返回0
- 失败:返回-1
-
stat获取文件权限方法
需要使用 st_mode & 0777 --->得到文件权限
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 当前用户的读、写、可执行
S_IRUSR 00400 当前用户读权限
S_IWUSR 00200 当前用户写权限
S_IXUSR 00100 当前用户可执行权限
S_IRWXG 00070 当前组的读、写、可执行
S_IRGRP 00040 当前组读权限
S_IWGRP 00020 当前组写权限
S_IXGRP 00010 当前组可执行权限
S_IRWXO 00007 其他用户的读、写、可执行
S_IROTH 00004 其他用户的读权限
S_IWOTH 00002 其他用户的写权限
S_IXOTH 00001 其他用户的可执行权限
- stat获取文件类型方法
需要使用 st_mode & S_IFMT --->得到文件权限
S_IFMT 0170000 bit mask for the file type bit field
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
操作目录相关的函数
opendir
-
功能:打开目录
-
原型:
-
#include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name);
-
-
参数
char *name
:指定要打开的目录路径以及名字
-
返回值
- 成功:返回目录流指针,DIR*
- 失败:返回NULL
closedir
-
功能:关闭目录
-
原型
-
#include <sys/types.h> #include <dirent.h> int closedir(DIR *dirp);
-
readdir
-
功能:读取目录,可以获取到该目录下所有文件名字
-
原型:
-
#include <dirent.h> struct dirent *readdir(DIR *dirp);
-
-
返回值
- 成功:返回结构体指针
- 读取到文件结尾,返回NULL,不更新errno
- 失败,返回NULL,更新errno
-
结构体原型
-
struct dirent { ino_t d_ino; /* inode号 */ off_t d_off; /* 偏移量 */ unsigned short d_reclen; /* 结构体长度 */ unsigned char d_type; /* 文件类型 */ char d_name[256]; /* 文件名 */ };
-
Linux中的库
- 库在系统中,是一个二进制文件,它是由源文件(不包含main函数)编译而来的,其他程序可以引入该文件中的库,并使用该文件中的相关函数
静态库
- 一个
XXX.c
的源文件,编译生成一个libXXX.a
的二进制文件,当你需要使用这个源文件中的函数时,只需要将对应的库连接到自己的可执行程序即可。- 静态体现在,使用
gcc
编译时,会将该库封装进可执行程序,当使用该库中的函数时,可执行程序直接使用即可,因为该库已经该可执行程序的一部分 - 缺点:库会占用可执行文件的大小
- 优点:调用方便,效率高
- 静态体现在,使用
编译生成静态库
gcc -c add.c -o add.o --> 只编译不连接,生成二进制文件
ar -crs libadd.a add.o --> 基于add.o生成一个静态库
ar:生成静态库的指令
-c:创建
-r:将文件插入或者替换静态库中同名的文件
-s:重置静态库的索引
静态库的使用
gcc 主程序.c -L 库的路径 -l库名 -I 头文件路径
-L:指定库所在的路径
-l:指定库名
-I:指定头文件所在路径
动态库
- 一个
XXX.c
的源文件,编译生成一个libXXX.a
的二进制文件,当你需要使用这个源文件中的函数时,只需要将对应的库连接到自己的可执行程序即可。- 动态体现在:在使用
gcc
编译的时候,会将该库对应的函数表封装在可执行程序中,当使用该库中的函数时,可执行程序需要通过该表找到库所在的位置,并且调用该库中的函数。 - 缺点:调用时需要去找到库所在的位置,并调用 ,效率较低
- 优点:库会不会占用可执行文件太多的空间,多个可执行程序可以共同连接到一个库中,所以,动态库也称为共享库
- 动态体现在:在使用
生成动态库
gcc -fPIC -c add.c -o add.o //-fPIC 忽略文件位置进行编译,将add.c编译程式add.o
gcc -shared add.o -o libadd.so //依赖于二进制文件生成一个动态库
上面两个指令可以把合成一个指令
gcc -fPIC -shared add.c -o libadd.so
使用动态库
gcc 主程序.c -L 库的路径 -l库名 -I 头文件路径
-L:指定库所在的路径
-l:指定库名
-I:指定头文件所在路径
动态库使用异常
解决方式一
1、在命令行中输入定义当前库的路径
export LD_LIBRARY_PATH = 库的路径
方法二
2、将该库直接放在/lib目录夏,或者/usr/lib目录下
sudo mv libadd.so /lib
sudo mv libadd.so /usr/lib
方法三
3、直接更改库的配置文件
sudo vi /etc/ld.so.conf.d/libc.conf