目录
1 文件IO
UNIX/Linux系统把所有的服务、设备都抽象成文件看待,并提供了一套简单而统一的系统接口,这部分系统接口就是所谓的系统文件读写,简称系统IO
如果使用的是标准c库中的文件读写函数,简称为标准IO
1.1 文件的分类
可用ls -l查看 权限前xxx xxx xxx的之前那位就可以判断文件类型
文件类型 | 标志符 | 说明 |
普通文件 | - | 包括纯文本文件、二进制文件、压缩文件等 |
目录文件 | d | 类比于window的文件夹注意必须有x执行权限才能进入目录 |
块设备文件 | b | 用于存储大数据的设备,例如硬盘 |
字符设备文件 | c | 例如键盘、鼠标 |
管道文件 | p | 有名管道为主 |
链接文件 | l | 类似于Windows的快捷方式 |
Socket文件 | s | 通常用于网络设备之间数据的连接交互 |
1.2 文件操作相关的系统调用
1.2.1 头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
1.2.2 系统调用函数
int open(const char *pathname, int flags);
功能:打开文件
pathname:文件路径
flags:文件打开方式
O_RDONLY | 只读 | O_WRONLY | 只写 | O_RDWR | 读写 |
O_CREAT | 文件不存在则创建 | O_TRUNC | 文件存在则清空打开 | O_APPEND | 追加 |
O_EXCL | 文件存在,配合O_CREAT则失败 |
int open(const char *pathname, int flags, mode_t mode);
功能:创建文件,
flags:必须为O_CREAT,mode参数需要给值
mode:
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 | 其他用户执行权限 |
注意:可以直接使用八进制数表示三组权限,例如:0644 0664 0775
返回值:文件描述符,成功返回一个非负整数,类似FILE*,代表了打开后的文件
int creat(const char *pathname, mode_t mode);
功能:创建文件
mode:同上
ssize_t write(int fd, const void *buf, size_t count);
功能:把内存中的数据写入文件中
fd:文件描述符,open的返回值
buf:待写入内存的首地址
count:要写入的字节数
返回值:成功写入的字节数
ssize_t read(int fd, void *buf, size_t count);
功能:把文件中的内容读取到内存中
fd:文件描述符,open的返回值
buf:待读入的内存的首地址
count:要读取的字节数
返回值:实际读取到的字节数
int close(int fd)
功能:关闭文件
fd:文件描述符,open的返回值
返回值:成功0,失败-1
注意:
read和write都是fwrite和fread的底层调用,正常情况下:标准IO比系统IO的速度更快
原因:
标准IO中有缓冲区机制,在写数据时,不是每次都调用系统IO,而是把写入的数据存储在临时的缓冲区中,当缓冲区满时,才会调用一次系统IO写入数据到文件中
直接使用系统IO时,会频繁切换内核态和用户态,非常耗时
如果给系统IO人为增加所谓的缓冲区,那么它的速度一定会比标准IO快
2 随机读写
系统IO下,每个打开的文件都有一个读写的位置指针,记录了从哪个字节开始进行读写文件,并且第文件进行读写操作时,它会自动往后移动,当想要在任意位置进行读写文件时,可以通过改变该位置指针的位置来进行随机读写
标准IO:fessk
系统IO:
off_t lseek(int fd,off_t offset,int whence);
offset:偏移值 字节为单位
whence:基础位置
SEEK_SET 文件开头
SEEK_CUR 文件末尾
SEEK_END 文件末尾
返回值:调整后位置指针所在文件的第几个字节
注意:当文件位置指针越过末尾后再写入数据时,越过的地方会形成"黑洞",该段"黑洞"会计算入文件大小中,但是不占用磁盘空间
3 系统IO读写文本文件
没有提供类似于fprintf和fscanf放入函数,因此不能直接读写文本文件
3.1 写文本文件
数据通过sprintf转换成字符串,然后系统IO写入
3.2 读文本文件
把数据读出到字符串中,然后sscanf解析转换成对应的数据再使用
3.3 文件描述符
① 非负整数,代表了打开的文件
② 由系统调用完成后返回,要被内核空间使用
③ 它代表了一个内核对象(类似文件指针),因为内核不能暴露它的地址,因此不能像文件指针返回一个对象地址
④ 内核中有一张表格记录了所有打开的文件对象,使用它们所在位置的下标作为该对象的文件描述符,相当于访问文件的凭证,是当前时间内唯一的
内核中有三个默认打开的文件描述符:
0 标准输入STDIN_FILENO stdin (FILE*)
1 标准输出STDOUT_FILENO stdout
2 标准错误STDERR_FILENO stderr
3.3.1 相关函数
int dup(int oldfd);
功能:复制一个打开的文件描述符
返回值:返回一个没有使用过的最小的文件描述符,失败返回-1
int dup2(int oldfd, int newfd);
功能:复制一个打开的文件描述符,复制成指定的值
注意:
① 如果newfd复制前已经打开,会先关闭,后复制
② 如果复制成功,相当于两个值不同的文件描述符对应同一个文件
3.4 文件同步
在写入数据时,内存与磁盘之间有一块缓冲区,好处是降低了磁盘读写次数,提高了读写的效率
但是这种机制带来的后果是磁盘中的数据有可能与实际写入的数据不匹配,系统提供了系统函数可以让缓冲区中的数据立即写入到磁盘
void sync(void);
功能:把缓冲区中的数据立即同步到磁盘
注意:并不会等待所有数据同步完成后才返回,而是发出同步指令后立即返回
int fsync(int fd);
功能:把指定文件的内容从缓冲区同步到磁盘
注意:会等待同步结束后才返回
int fdatasync(int fd);
功能:把指定文件的内容从缓冲区同步到磁盘,只同步文件的内容,不会同步文件属性
注意:会等待同步结束后才返回
3.5 文件属性
int stat(const char *pathname, struct stat *buf);
功能:根据文件路径获取文件的属性
buf:存储文件属性的结构体指针,是输出型参数
int fstat(int fd, struct stat *buf);
功能:根据文件描述符获取文件的属性
int lstat(const char *pathname, struct stat *buf);
功能:获取软链接文件的文件属性
struct stat:
dev_t st_dev | 设备ID | dev_t st_rdev | 总字节数 |
ino_t st_ino | inode节点号 | off_t st_size | IO块字节数 |
mode_t st_mode | 文件的类型和权限 | blksize_t st_blksize | 占用512字节的块数 |
nlink_t st_nlink | 硬链接数 | blkcnt_t st_blocks | 最后访问时间 |
uid_t st_uid | 用户ID | struct timespec st_atim | 最后修改时间 |
gid_t st_gid | 组ID | struct timespec st_mtim | 属性最后修改时间 |
struct timespec st_ctim | 特殊设备ID |
st_mode:
S_IFMT | 0170000 | 识别文件类型的掩码 |
S_IFSOCK | 0140000 | socket文件 |
S_IFLNK | 0120000 | 链接文件 |
S_IFREG | 0100000 | 普通文件 |
S_IFBLK | 0060000 | 块设备文件 |
S_IFDIR | 0040000 | 目录文件 |
S_IFCHR | 0020000 | 字符设备文件 |
S_IFIFO | 0010000 | 管道文件 |
注意:可以使用 st_mode&S_IFMT 的结果对比是哪个文件
另外:还可以使用提供的宏函数识别st_mode是哪个文件
S_ISREG(st_mode) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO?
S_ISLNK(m) symbolic link?
S_ISSOCK(m) socket?
3.6 文件权限
int access(const char *pathname, int mode);
功能;检查当前用户对文件的权限有哪些
pathname:待测试的文件
mode:想要测试的权限
R_OK 是否有读权限
W_OK 是否有写权限
X_OK 是否有执行权限
F_OK 是否存在该文件
返回值:有该权限返回0 没有返回-1
int chmod(const char *pathname, mode_t mode);
功能:根据路径修改文件权限
mode:由3位八进制数组成的权限码
0644 普通文件
0755 可执行文件
int fchmod(int fd, mode_t mode);
功能:根据文件描述符修改文件权限
3.6.1 权限屏蔽码
如果我们有一些权限不想用户在创建文件的时候具备,可以通过设置权限屏蔽码来屏蔽文件创建是具备某些权限
命令:
umask 查看当前终端的权限屏蔽码
umask 0xxx 临时修改当前终端的权限屏蔽码
函数:
mode_t umask(mode_t mask)
功能:修改当前进程权限屏蔽码
mask:新的屏蔽码
返回值:原来的屏蔽码
注意:
① 权限屏蔽码不能影响 命令chmod 函数chmod,只会影响创建文件函数open/creat
② 修改权限屏蔽码只是临时修改,如果关闭终端后,会还原,想要永久修改需要修改配置文件
3.6.2 修改文件大小
int truncate(const char *path, off_t length);
功能:修改文件大小
length:想要修改后的总字节数
注意:length<原总字节数 从末尾抹除多余内容
int ftruncate(int fd, off_t length);
功能:修改文件大小
3.7 删除和重命名
int remove(const char *pathname);
功能:C标准库提供删除文件函数,底层调用unlink\rmdir
int unlink(const char *pathname);
功能:系统调用函数,删除文件
int rename(const char *oldpath, const char *newpath);
功能:重命名文件
3.8 链接文件
Linux的文件系统主要有两个分区部分:
inode:128Byte,主要记录文件权限、文件大小、所有者、时间等内容
block数据块区:默认4k,记录了文件名和文件中真正的数据内容
每个文件都有一个唯一的inode和若干个block,当要读取文件时,要先借助目录文件的block中记录的目录中文件的文件名和inode号来找到该文件的inode,从而读取到对应的block数据块
3.8.1 软、硬链接文件
硬链接文件:硬链接文件没有专门创新的独属于自己的inode和block,只是在不同于被链接文件的目录中复制了一份被链接文件的inode,通过该inode也可以访问被链接文件的block。
软链接文件:软链接文件会建立属于自己的新的inode和block,但是block中存储的是被链接文件的inode号和文件名。
区别:
① 删除被链接文件时,只是删除了该文件的inode信息块的内容,而不会删除block,所以源文件删除会影响软链接,软链接会无法找到源文件;源文件删除不影响硬链接,让可以访问到源文件的block
② 当硬链接数为0时,系统才认为该文件被删除
③ 如果修改硬链接文件的内容,被链接文件也会随之改变
④ 硬链接文件不能链接目录,软链接可以
⑤ 可以跨文件系统创建软链接,对不存在的文件创建软连接
3.8.2 什么是软、硬链接文件
硬链接文件:硬链接文件没有专门创新的独属于自己的inode和block,只是在不同于被链接文件的目录中复制了一份被链接文件的inode,通过该inode也可以访问被链接文件的block。
软链接文件:软链接文件会建立属于自己的新的inode和block,但是block中存储的是被链接文件的inode号和文件名。
区别:
① 删除被链接文件时,只是删除了该文件的inode信息块的内容,而不会删除block,所以源文件删除会影响软链接,软链接会无法找到源文件;源文件删除不影响硬链接,让可以访问到源文件的block
② 当硬链接数为0时,系统才认为该文件被删除
③ 如果修改硬链接文件的内容,被链接文件也会随之改变
④ 硬链接文件不能链接目录,软链接可以
⑤ 可以跨文件系统创建软链接,对不存在的文件创建软连接
3.8.3 创建链接文件函数
int link(const char *oldpath,const char *newpath);
功能:创建硬链接文件
注意:硬链接文件的类型与被链接文件相同
int symlink(const char *target, const char *linkpath);
功能:创建软链接文件
注意:文件类型为l
3.10 目录文件操作
int mkdir(const char *pathname, mode_t mode);
功能:创建一个空目录
mode:目录的权限,要有执行权限才能进入目录
int rmdir(const char *pathname);
功能:删除空目录
int chdir(const char *path);
功能:在当前进程中更改当前工作路径为path,相当于cd
int fchdir(int fd);
功能:更改当前工作路径为fd目录文件描述符所在目录
char *getcwd(char *buf, size_t size);
功能:获取当前的工作路径,相当于pwd
DIR *opendir(const char *name);
功能:打开一个目录
返回值:该目录的目录流,目录流中记录了该目录中所有文件的信息
DIR *fdopendir(int fd);
功能:通过目录的文件描述符,打开一个目录
返回值:该目录的目录流,目录流中记录了该目录中所有文件的信息
struct dirent *readdir(DIR *dirp);
功能:从目录流中读取一条文件记录
返回值:存储一个文件信息的结构体指针
struct dirent
ino_t d_ino | inode号 |
off_t d_off | 下一条记录的偏移量 |
unsigned short d_reclen | 当前记录的长度 |
unsigned char d_type | 文件类型 |
char d_name[256] | 文件名 |