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
可监听写就绪。