ubuntu学习day4
4 Linux 文件操作
4.1 基于文件指针的文件操作
- Linux 中对目录和设备的操作都是文件操作,文件分为普通文件,目录文件,链接文件和设备文件。
- 普通文件:也称磁盘文件,并且能够进行随机的数据存储(能够自由 seek 定位到某一个位置)。
- 管道:是一个从一端发送数据,另一端接收数据的数据通道。
- 目录:也称为目录文件,它包含了保存在目录中文件列表的简单文件。
- 设备:该类型的文件提供了大多数物理设备的接口。它又分为两种类型:字符型设备和块设备。字符型设备一次只能读出和写入一个字节的数据,包括调制解调器、终端、打印机、声卡以及鼠标;块设备必须以一定大小的块来读出或者写入数据,块设备包括 CD-ROM、RAM 驱动器和磁盘驱动器等。一般而言,字符设备用于传输数据,块设备用于存储数据。
- 链接:类似于 Windows 的快捷方式,指包含到达另一个文件路径的文件。
- 基于文件指针的文件操作函数是 ANSI 标准函数库的一部分。
4.1.2 文件的创建,打开与关闭
使用文件指针来访问文件的方法是由标准 C 规定的,相关函数的原型为:
#include <stdio.h> //头文件包含
FILE* fopen(const char* path, const char* mode);//文件名 模式
int fclose(FILE* stream)
-  fopen 以 mode 的方式打开或创建文件,如果成功,将返回一个文件指针,失败则返回 NULL。fopen 创建的文件的访问权限将以 0666 与当前的 umask 结合来确定。 
-  mode 的可选模式列表,如下所示: 
  
-  在 Linux 系统中,mode 里面的’b’(二进制)可以去掉,但是为了保持与其他系统的兼容性,建议不要去掉。 
-  ab 和 ab+为追加模式,在此两种模式下,在一开始的时候读取文件内容是从文件起始处开始读取的,而无论文件读写点定位到何处,在写数据时都将是在文件末尾添加(写完以后读写点就移动到文件末尾了),所以比较适合于多进程写同一个文件的情况下保证数据的完整性。 
4.1.3 读写文件
基于文件指针的数据读写函数较多,可分为如下几组:
块读写
#include <stdio.h>
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从- stream读取- nmemb个元素到- ptr,每元素- size字节。
- fwrite从- ptr写入- nmemb个元素到- stream,每元素- size字节。
- 操作从当前读写点开始,完成后自动移动 size * nmemb字节。
格式化读写
#include <stdio.h>
int printf(const char *format, ...);
int scanf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
- fprintf写入- stream;- sprintf写入字符串;- fscanf,- sscanf对应读取。
单字符读写
#include <stdio.h>
int fgetc(FILE *stream);
int fputc(int c, FILE *stream);
int getc(FILE *stream);
int putc(int c, FILE *stream);
int getchar(void);
int putchar(int c);
- getchar,- putchar对应- stdin/- stdout。
字符串读写
char *fgets(char *s, int size, FILE *stream);
int fputs(const char *s, FILE *stream);
int puts(const char *s);
char *gets(char *s);
- fgets从- stream读取一行,保留- \n;- fputs写一行,不自动加- \n。
- gets不安全,建议使用- fgets。
文件定位
#include <stdio.h>
int feof(FILE *stream);
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
void rewind(FILE *stream);
- fseek(stream, offset, whence)定位:- SEEK_SET,- SEEK_CUR,- SEEK_END。
- ftell返回当前偏移;- rewind移至开头;- feof判断 EOF。
4.1.4 文件的权限
#include <sys/stat.h>
int chmod(const char* path, mode_t mode);
- mode如- 0777(八进制),修改文件访问权限。
4.2 目录操作
4.2.1 获取、改变当前目录
#include <unistd.h>
char *getcwd(char *buf, size_t size);
int chdir(const char *path);
- getcwd(NULL,0)自动- malloc空间,需- free。
- chdir(path)等同- cd命令。
4.2.2 创建和删除目录
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int mkdir(const char *pathname, mode_t mode);
int rmdir(const char *pathname);
- mkdir创建目录,- mode权限;- rmdir删除空目录。
- 环境变量:echo $PATH;export PATH=$PATH:dir。
4.2.3 目录的存储原理
- 文件系统用 inode 管理文件:存放位置信息、类型、权限、时间等。
- 目录文件固定大小,存放多个 dirent节点(链式结构)。
struct dirent {ino_t d_ino;off_t d_off;unsigned short d_reclen;unsigned char d_type;char d_name[256];
};
4.2.4 目录相关操作
#include <dirent.h>
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dir);
void rewinddir(DIR *dir);
void seekdir(DIR *dir, off_t offset);
off_t telldir(DIR *dir);
int closedir(DIR *dir);
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
- 打开目录后,用 readdir迭代,最后closedir。
- stat获取文件状态。
4.3 基于文件描述符的文件操作
4.3.1 文件描述符
- POSIX 提供无缓冲 I/O,文件由整数描述符表示。文件描述符索引内核中文件表。
4.3.2 打开、创建和关闭文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
int close(int fd);
- open返回- fd,失败返回- -1。
- 常用 flags:O_RDONLY,O_WRONLY,O_RDWR,O_CREAT,O_EXCL,O_TRUNC,O_APPEND,O_NONBLOCK,O_SYNC。
- creat(path, mode)等价- open(path, O_CREAT|O_TRUNC|O_WRONLY, mode)。
- close(fd)释放锁并递减引用计数。
4.3.3 读写文件
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
- 失败返回 -1,读完返回0,否则返回字节数。
4.3.4 改变文件大小
#include <unistd.h>
int ftruncate(int fd, off_t length);
- 将文件截断或扩展到 length,成功返回0。
- 常与 mmap配合使用。
4.3.5 文件映射
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off);
- 将文件映射到内存,无需 read/write。
- 需与 ftruncate配合调整文件大小。
4.3.6 文件定位
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
- lseek返回新的偏移位置,可创建文件空洞。
4.3.7 获取文件信息
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *file_name, struct stat *buf);
int fstat(int fd, struct stat *buf);
- struct stat包含多种属性:- st_mode,- st_size,- st_atime,- st_mtime,- st_ctime等。
- 宏判断类型:S_ISREG,S_ISDIR,S_ISLNK, etc。
4.3.8 文件描述符的复制
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
- dup自动分配新描述符;- dup2指定新描述符,新描述符若已打开则先关闭。
- 应用:输出重定向。
4.3.9 文件描述符和文件指针
- fopen本质调用- open,可用- fileno(fp)获取底层- fd。
- fdopen(fd, mode)为- fd创建缓冲流。
4.3.10 标准输入输出文件描述符
- STDIN_FILENO=0, STDOUT_FILENO=1, STDERR_FILENO=2对应- stdin,- stdout,- stderr。
4.3.11 管道
-  无名管道:半双工通信,文件类型 p。
-  Shell: mkfifo name cat name # 读 echo "..." > name # 写
-  C 程序分别 open(..., O_RDONLY)/O_WRONLY。
4.4 I/O 多路转接模型
4.4.1 读取阻塞
- 默认阻塞模式,read无数据则阻塞。
- 双管道可实现全双工。
4.4.2 select
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
- FD_ZERO,- FD_SET,- FD_CLR,- FD_ISSET操作- fd_set。
- timeout- NULL永久等待。
4.4.3 select 的退出机制
- 写端关闭后,读端 read返回0,select标记可读。
- 应检测 read返回0并退出。
4.4.4 超时处理
- 每次调用前需重置 struct timeval。
4.4.5 写集合
- 写阻塞:管道满时写端阻塞;select可监听写就绪。
