Linux 文件IO操作
1.打开和关闭文件
1.1 open
#include<unistd.h> #include<fcntl.h> int open(const char *pathname, int flags) /* pathname: 打开文件的路径名(相对、绝对) flags: 打开文件的方式(O_RDONLY | O_WRONLY | O_RDWR) --- <fcntl.h> return: 返回一个文件描述符或-1 */
int open(const char* pathname, int flags, mode_t mode) /* pathname: 打开文件的相对路径 flags: 打开文件的方式(O_RDONLY|O_WRONLY|O_RDWR) 还有很多标签可以在man里查看 mode_t: 参数2如果指定了 O_CREAT, 则参数三传 8 进制数来描述文件权限 例如 0664表示 rw-rw-r-- */
注意,文件创建后的最终权限受到 open() 函数传入的参数影响,同时也受到 umask 屏蔽掩码共同影响
最终创建出的文件权限 = mode & (~umask)
1.2 close
#include <unistd.h> int close(int fd) /* fd: open的返回值(文件描述符) return: 成功0, 失败-1, errno */
2.文件读取和写入
read和write都是按字节操作的函数
2.1 read
ssize_t read(int fd, void *buf, size_t count) /* fd: 打开的文件描述符 buf: 读取数据到缓冲区 count: 缓冲区大小 size_t: 无符号整数 ssize_t 有符号整数 return: 成功: 读到文件末尾0, 未读到文件末尾>0 失败: -1 errno = EAGAIN(或EWOULDBLOCK): 非阻塞状态读(网络、设备)文件没读到数据 errno != EAGAIN(或EWOULDBLOCK): 错误 */
2.2 write
ssize_t write(int fd, void *buf, size_t count) /* fd: 打开的文件描述符 buf: 储存写出数据的缓冲区地址 count: 缓冲区大小 size_t: 无符号整数 ssize_t 有符号整数 return: 成功: >0 写入的数据大小, =0 没有写入数据 失败: -1, errno */
2.3 与fgetc和fputc比较
read 和 write 是系统调用,fgetc 和 fputc 是 c 语言提供的函数。下面两个函数举例,同样复制一个txt文件,每次复制一个字节,c 语言提供的函数速度会远远快于系统调用,因为 c 语言封装的 read 和write 有一个缓冲区,分别是预读入和缓输出,虽然 fgetc 函数读取一个字节,但实际上它一次性会缓存4096字节在内存中,同样 fputc 虽然会写入一个字节,但它会先写入内存4096字节再一次性写入文件,这种机制避免了频繁IO降低性能
2.4 文件描述符
PCB进程控制块里有一个成员是文件描述符表,open 函数返回的 fd 是一个下标,返回表中未被使用的最小的文件描述符(0、1、2分别是标准输入STDIN_FILENO、标准输出STDOUT_FILENO、标准出错STDERR_FILENO)
3. 错误代码处理
3.1 strerror
char* strerror(EINTR); // 头文件<string.h>,引入errno.h头文件(错误号), 该函数会打印错误号对应的信息(EINTR其实是4)
3.2 perror
int fd = open(filepath, O_RDWR|O_CREAT|O_TRUNC, 0644); if(fd == -1) { perror("open dst err"); // 打印该字符串以及errno错误号对应的错误信息 exit(1); // 程序非正常退出 }
4.改变已经打开文件的访问属性
4.1 fcntl (f control)
这个函数功能非常多,这只是一个例子
#include <fcntl.h> int fcntl(int fd, int cmd, ...); // 获取文件权限 int flag = fcntl(fd, F_GETFL); // 修改文件权限 flag |= O_NONBLOCK; // 例如添加非阻塞权限 // 设置文件权限 fcntl(fd, F_SETFL, flag);
5.改变文件指针
5.1 lseek
off_t lseek(int fd, off_t offset, int whence); /* fd: 文件描述符 offset: 偏移量 whence: 起始偏移位置 SEEK_SET起始位置 / SEEK_CUR当前位置 / SEEK_END结束位置 return: 成功返回起始位置的偏移, 失败返回-1 */
- 文件的“读”、“写”使用相同偏移位置,所以先使用 write 写入10个字节后,read 时指针也是在第10个字节开始的,这可能就需要 lseek 调整指针位置
- 可以通过 int len = lseek(fd, 0, SEEK_END); 获取当前文件的大小,从结束位置偏移 0 个字节返回
- 可以通过 int len = lseek(fd, 10, SEEK_END); 拓展文件大小,但必须引起IO操作才能拓展,write(fd, "a", strlen("a")); 正常应该用 truncate() 函数扩展文件大小