02-文件操作(文件、错误处理、目录、mmap)
文件操作
5 个基本函数:open、close、read、write、ioctl(把控制信息传递给设备驱动程序)
比较重要的设备文件3个
- /dev/console
- /dev/tty
- /dev/null
文件系统
一个磁盘可以分为一个或多个分区,每个分区有自己的文件系统。
多个目录项可以指向同一个i节点,i节点中有链接计数器。当链接计数减为0时才可以删除文件。这也是删除一个目录项的函数为unlink而非delete的原因。保存在stat结构体的st_nlink成员中。这种链接类型被称为硬链接。
i节点包含了文件有关所有信息:文件类型、文件访问权限、文件长度、指向文件数据块的指针。stat结构大多数数据来自i节点。两项数据在目录项中:文件名、i节点编号。
每个目录的i节点其类型字段表示它是一个目录,链接数最少为2(该目录的父目录项,该目录的.项)。此外,每在父目录中创建一个子目录项都使该父目录项的计数加一。
系统调用
全局变量 errno 指明错误
-
打开
open (const char *__path, int __oflag, ...)
oflags 种类
O_EXCL 与O_CREAT一起使用,若文件已存在,创建失败
创建文件时使用三个参数的open调用以指定权限
S_IRUSR 读权限,文件主 S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH -
写
ssize_t write (int __fd, const void *__buf, size_t __n)
#include <unistd.h> // 此头文件必须先于其他头文件出现 void Write() { if (write(1, "12345", 5) != 5) { // 向标准输出写入长为5的字符串 write(2, "678", 3); // 向标准错误输出写入 } }
-
读
read (int __fd, void *__buf, size_t __nbytes)
#include <unistd.h> void Read() { char buffer[128]; int nread; nread = read(0, buffer, 128); if (nread == -1) write(2, "read error", 10); if ((write(1, buffer, nread)) != nread) write(2, "write error", 11); }
-
创建
int creat (const char *__file, mode_t __mode)
相当于
open("file", O_CREAT|O_WRONLY|O_TRUNC)
-
关闭
int close(int fildes)
-
操作底层
int ioctl(int fildes, int cmd, ...)
-
控制文件指针
off_t lseek(int fildes, off_t offset, int whence)
#include <unistd.h> #include <sys/types.h>
offset 指定位置
whence 可取:
- SEEK_SET 绝对位置
- SEEK_CUR 相对于当前位置的相对位置
- SEEK_END 对于文件尾的相对位置
-
文件描述符信息
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> int fstat (int __fd, struct stat *__buf); int stat (const char *__restrict __file, struct stat *__restrict __buf); int lstat (const char *__restrict __file, struct stat *__restrict __buf);
-
复制
#include <unisted.h> int dup(int fildes); // 复制并返回一个新的文件描述符 int dup2(int fildes, int fildes2); // 通过指明目标,将一个文件描述符复制为另外一个 int dup3 (int __fd, int __fd2, int __flags) __THROW;
-
pread pwrite
-
sync 文件同步
系统内部有高速缓存,内核通常将数据复制到缓冲区,再排入队列,晚些时候写入磁盘。这被称为“延迟写”。可以使用操作同步文件系统和缓冲区内容。
-
fcntl
#include <fcntl.h>
int fcntl(int fildes, int cmd);
int fcntl(int fildes, int cmd, long arg);
// 对于文件进行各种操作,如:复制、获取、设置文件描述符标志。。。
- truncate
将文件截断,如果参数大于文件长度则增加文件的逻辑长度却没有分配真实物理块,即创建了文件空洞。
- 操作符号链接
标准IO
- fopen、fclose
- fread、fwrite
- fflush
- fgetc、getc、getchar
- fputc、putc、putchar
- fgets、gets
gets 可能造成缓冲区溢出 - printf、fprintf、sprintf
- scanf、fscanf、sscanf
- fgetpos、fsetpos、ftell(返回当前文件流偏移位置)
- rewind(重置文件流中读写位置)、freopen、setvbuf(设置文件流的缓冲机制)、remove
- freopen、fwide 用于清除和设置文件是单字节还是宽字节
- setbuf、setvbuf
注意:这两个函数要在流被打开后执行其他操作之前使用。buf为一个长为BUFSIZ的缓冲区,BUFSIZ定义于<stdio.h>
- fileno 获取文件标识符
- 获得唯一的文件名、获得临时文件
- 内存流
高级IO
- 用于创建文件描述符 pipe dup dup2
- 用于读写数据的函数 readv writev sendfile mmap splice tee
// 文件描述符间在内核直接传递数据,零拷贝 extern ssize_t sendfile (int __out_fd, //输出io必须为socket int __in_fd, // 输入io必须是真实的文件 off_t *__offset, // 输入流的偏移位置 size_t __count) __THROW; // 传输的字节数 extern __ssize_t // 两个fd间移动数据,零拷贝 splice (int __fdin, // 输入fd __off64_t *__offin, // 输入流偏移位置 int __fdout, // 输出fd __off64_t *__offout,// 输出流偏移位置 size_t __len, // 移动数据的长度 unsigned int __flags); extern __ssize_t // 复制数据,零拷贝,不消耗数据 tee (int __fdin, int __fdout, size_t __len, unsigned int __flags);
- 用于控制IO行为和属性的函数 fcntl
错误
错误由全局变量 errno 反应,许多函数都可能改变errno的值,只有函数表明失败后立即检查才有意义。
#includ <errno.h>
extern int errno;
通过与 errno.h 里定义的错误代码比较得到正确原因
#include <string.h>
char *strerror(int errnum); // 将错误代码映射为一个字符串
#include <stdio.h>
void perror(const char *s); // 输出字符串与错误提示
perror("program");
// program: Too many open files
文件与目录的维护
- chmod、chown
- unlink、link、symlink
- mkdir、rmdir
- chdir、getcwd
扫描目录
include <dirent.h>
- opendir、closedir
- readdir
- telldir
- seekdir
/proc 文件系统
/proc 目录下文件为进程文件的映射
- /proc/cpuinfo
- /proc/meminfo
- /proc/version
- /proc/net/sockstst
- /proc/sys/fs/file-max 最大打开文件数
mmap
#include <sys/mman.h>
// 文件映射到内存
void *mmap (
void *__addr, // 映射存储区地址。通常为0,由系统选择并返回映射区起始地址
size_t __len,
int __prot, // PROT_READ 可读 PROT_WRITE 可写 PROT_EXEC 可执行 PROT_NONE 不可访问 权限不能超过open模式访问权限
int __flags, // MAP_FIXED 返回值必须等于addr,不建议使用
// MAP_SHARED 指定存储操作修改映射文件,即存储操作等于对文件write。必须指定此标志或下一个,但不能同时指定二者
// MAP_PRIVATE 对映射区的存储操作导致创建该映射文件的一个是由副本,后续引用都使用该副本
int __fd, // 文件描述符,要提前打开
__off_t __offset // 映射字节在文件中偏移量
);
// 内存中修改部分写回文件
int msync (
void *__addr,
size_t __len,
int __flags // MS_ASYNC MS_SYNC 一定要指定一个;MS_INVALIDATE 丢弃与底层存储器没有同步的页
);
// 更改一个现有映射权限
int mprotect (
void *__addr,
size_t __len,
int __prot
);
// 释放内存
int munmap (
void *__addr,
size_t __len
);