> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。
> 目标:了解在Linux下的系统文件IO,知道什么是文件描述符,什么是重定向
> 毒鸡汤:白日莫闲过,青春不再来。
> 专栏选自:Linux初阶
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
🌟前言
最早我们在C语言中学习关于如何用代码来管理文件,比如文件的输入和文件的输出,一些文件的接口,掌握上述的知识只能说是对文件入门而已,在Linux中我们是一切接文件的,如何深入学习文件的知识这是一个难题,今天我们所探讨就是Linux的基础I/O。
⭐主体
学习【Linux】基础IO咱们按照下面的图解:
🌙 回顾C文件接口
💫 C 读写文件
文件操作:
- 首先要打开文件:打开成功,返回文件指针;打开失败,返回NULL
- 最后要关闭文件
代码操作:
FILE *fopen(const char *path, const char *mode); int fclose(FILE *fp);
1.C 写文件
采用方法:
我们可以fputs/fgets以字符串形式读写;也可以fprintf/fscanf格式化读写
代码操作:
int fputs(const char *s, FILE *stream); 向特定文件流写入字符串 int fprintf(FILE *stream, const char *format, ...);
举个栗子:
①如果以"w"模式打开文件,默认是文本读写,且会把原始内容清掉再写。
代码如下:
#include <stdio.h> int main() { FILE *fp = fopen("log.txt","w"); if(fp == NULL) { perror("fopen"); return 1; } // 进行文件操作 fclose(fp); return 0; }
运行结果:
②如果要以追加方式写,则要以"a
" append模式打开文件
代码如下:
#include <stdio.h> #include <unistd.h> #include <string.h> int main() { FILE *fp = fopen("log.txt","a"); // 追加 if(fp == NULL) { perror("fopen"); return 1; } // 进行文件操作 const char* s = "hello world\n"; fwrite(s,strlen(s),1,fp); return 0; }
运行结果:
2.C 读文件
解读:
fgets从特定文件流中按行读取,内容放在缓冲区。读取成功返回字符串起始地址,读失败返回NULL.
代码演示:
char *fgets(char *s, int size, FILE *stream); //size:为缓冲区大小 int fscanf(FILE *stream, const char *format, ...);
举个栗子:
#include <stdio.h> #include <unistd.h> #include <string.h> int main() { FILE *fp = fopen("./log.txt","r"); if(fp == NULL) { perror("fopen"); return 1; } // 进行文件操作 char buffer[64]; while(fgets(buffer,sizeof(buffer),fp)) { printf("%s",buffer);//把我们读到的东西打出来 } return 0; }
运行结果:
💫 关于stdin stdout stderr
概念分析:
C语言默认会打开三个输入输出流:stdin、stdout、stderr,它们的类型都是FILE*,C语言把它们当做文件看待,本质上我们最终都是访问硬件。C++中也有cin、cout、cerr,几乎所有语言都提供标准输入、标准输出、标准错误。
- stdin对应的硬件设备是键盘
- stdout对应显示器
- stderr对应显示器
总结分析:
既然fputs是向文件写入,stdout也是FILE*类型,我们是不是可以向显示器标准输出打印了?这说明显示器被看做文件即:Linux下,一切皆文件。
举个栗子:
问题拓展:
fputs可以向一般文件(磁盘,也是硬件)或者硬件设备写入。这反映着Linux下一切皆文件!
🌙 系统文件I/O
文件操作最终都是访问硬件(显示器、键盘、文件(磁盘))。众所周知,OS是硬件的管理者。所有语言上对“文件”的操作,都必须贯穿操作系统。然而OS不相信任何人,访问操作系统,就必须要通过系统接口!!
其实我们学过的几乎所有的语言中,fopen/fclose,fread/fwrite,fputs/fgets,fgets/fputs 等底层一定需要使用OS提供的系统调用接口,下面咱们就来学习文件的系统调用接口,才能做到万变不离其宗!!
图解:
💫 open & close
采用 man open 指令查看相关资料
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags);//路径 + 选项 int open(const char *pathname, int flags, mode_t mode);
三个参数:
pathname: 要打开或创建的目标文件文件名 flags: 打开方式。传递多个标志位,下面的一个或者多个常量进行“或”运算,构成flags. O_RDONLY: 只读打开 O_WRONLY: 只写打开 O_RDWR : 读写打开 以上这三个常量,必须指定一个且只能指定一个 O_CREAT : 若文件不存在,则创建它。同时需要使用mode选项,来指明新文件的访问权限 O_APPEND: 追加写 mode: 设置默认权限信息
返回值(int):
return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately). 成功: 新打开的文件描述符 失败: -1
采用 man close 指令查看相关资料
#include <unistd.h> int close(int fd);
举个栗子:
代码如下:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() { int fd = open("./log.txt",O_WRONLY | O_CREAT); // int fd = open("./log.txt",O_WRONLY | O_CREAT,0644); if(fd < 0) { printf("open error\n"); // return 1; } close(fd); return 0; }
运行结果:
问题分析:
可以看到权限完全是混乱的!这是因为,没有这个文件,要创建它,系统层面就必须指定权限是多少!我们采用权限设置的八进制方案
代码再次更新:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() { //int fd = open("./log.txt",O_WRONLY | O_CREAT); int fd = open("./log.txt",O_WRONLY | O_CREAT,0644); if(fd < 0) { printf("open error\n"); return 1; } close(fd); return 0; }
运行更新结果:
分析结果:
之前我们在语言层面,创建时就是一个正常权限,我根本就不关心什么只写、创建、权限这些与系统强相关的概念。语言为我们做了封装,我用就好了
fopen("./log.txt", "w"); int fd = open("./log.txt", O_WRONLY | O_CREAT, 0644);
那第二个参数flags(int)为什么要把模式 | 在一起呢?这是一种用户层给内核传递标志位的常用做法。int有32个bit位,一个bit代表一个标志,就可以传递多个标志位且位运算效率较高。这些O_RDONLY、O_WRONLY、O_RDWR 都是只有一个比特位是1的数据,并且相互不重复,这样 |在一起,就能传递多个标志位。
我们可以来打开/usr/include/bits/fcntl-linux.h
这个文件查看
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(下) https://developer.aliyun.com/article/1565672