hello,大家好,这里是bang___bang_ ,今天和大家谈谈Linux中的基础IO,包含内容有对应的系统文件I/O接口,文件描述符,理解重定向。
目录
1️⃣初识文件
✦文件=内容+属性(属性也是数据)
✦文件的所有操作:a.对内容 b.对属性
✦文件在磁盘(硬件)上放着,我们访问文件,先写代码->编译->exe->运行->访问文件。
本质上是 进程 在访问文件。
Linux下一切皆文件。曾经我们理解的文件就是磁盘上的那些普通文件能read,write。
在C语言中,我们对显示器有方法:printf->这是write(output);键盘:scanf->这是read(input)。也就是说我们的显示器和键盘也是文件。
站在系统的角度来说:我们的程序要加载到内存,键盘相当于把我们的数据输交给内存(input),而内存把读取到的数据刷新到文件或者显示器当中(output)。
系统角度:能够被input读取,或者能够被output写出的设备就叫做文件!
2️⃣ 系统文件I/O接口
在C语言中,文件操作接口有fopen,fwrite,fprintf,fclose等等,实际上这些接口都是封装了系统I/O接口。
🍙open
pathname: 要打开或创建的目标文件flags: 打开文件时,可以传入多个参数选项参数 :O_RDONLY: 只读打开O_WRONLY: 只写打开O_RDWR : 读,写打开O_RDONLY O_WRONLY O_RDWR 这三个常量,必须指定一个且只能指定一个O_CREAT : 若文件不存在,则创建它。需要使用 mode 选项,来指明新文件的访问权限O_APPEND: 追加写O_TRUNC: 清空文件mode: 为新建文件设置权限
问题:如何实现flags传入多个参数选项呢?
答:采用位图的思想,flags的参数实际上是一个int类型的数,参数之间进行|运算,就能实现传入多个参数的效果。
![](https://i-blog.csdnimg.cn/blog_migrate/5d57d206a659b627736ff37941344968.png)
🌰O_WRONLY | O_CREAT
![](https://i-blog.csdnimg.cn/blog_migrate/c6ab655bfb133683360f7734f9f90572.png)
检测是否有当前状态,只需要使用flag&状态的值。
0110 0101 & 0000 0001 = 0000 0001 有O_WRONLY状态
0110 0101 & 0110 0100 = 0110 0100 有O_CREAT状态
open函数图中我框选了返回值为一个文件描述符,在下面讲解。
🌰简单使用open
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>
int main()
{
int fd=open("log.txt",O_WRONLY | O_CREAT,0666);
assert(fd!=-1);
printf("open sucess! fd:%d\n",fd);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/61d3a128574aec3471e4beed80c4e37f.png)
🍙write
功能:把缓冲区buf的前count字节写入与文件描述符fd关联的文件中。它返回实际写入的字节数。
🌰测试write
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>
#include<string.h>
int main()
{
int fd=open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
assert(fd!=-1);
printf("open sucess! fd:%d\n",fd);
const char* buf="This is a test\n";
write(fd,buf,strlen(buf));
return 0;
}
🍙read
功能:从与文件描述符fd相关联的文件中读取count个字节的数据。并把它放入到数据区buf中。
🌰测试read,读取上面write写的内容
int main()
{
int fd=open("log.txt",O_RDONLY);
assert(fd!=-1);
printf("open sucess! fd:%d\n",fd);
char res[64];
read(fd,res,sizeof(res));
printf("res:%s",res);
return 0;
}
🍙close
功能:将文件描述符fd相关联的文件关闭。
🌰close操作演示
int main()
{
int fd=open("log.txt",O_RDONLY);
assert(fd!=-1);
printf("open sucess! fd:%d\n",fd);
close(fd);
return 0;
}
3️⃣文件描述符
🍙0&1&2
🌰fd的显示问题:
int main()
{
int fd1=open("log1.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
int fd2=open("log2.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
int fd3=open("log3.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
int fd4=open("log4.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
assert(fd1!=-1);
assert(fd2!=-1);
assert(fd3!=-1);
assert(fd4!=-1);
printf("open sucess! fd1:%d\n",fd1);
printf("open sucess! fd2:%d\n",fd2);
printf("open sucess! fd3:%d\n",fd3);
printf("open sucess! fd4:%d\n",fd4);
close(fd1);
close(fd2);
close(fd3);
close(fd4);
return 0;
}
问题:为什么fd是从3开始?
答:实际上C/C++程序默认会打开三个文件流:标准输入stdin(0),标准输出stdout(1),标准错误stderr(2)
0,1,2一般对应的物理设备是:键盘,显示器,显示器。
🍙内核中文件描述符的探究
在上面我们可以发现文件描述符fd就是一个小整数,那么在内核中文件描述符究竟是什么呢?
![](https://i-blog.csdnimg.cn/blog_migrate/48487477c0446e48f2cec3104b9393b4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/77296044aa33c2a58ec8f95d41858132.png)
🍙分配规则
🌰验证分配规则,关闭文件0,进行分配。
int main()
{
close(0);
int fd=open("log1.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
assert(fd!=-1);
printf("open sucess! fd:%d\n",fd);
close(fd);
return 0;
}
问题:如果关闭文件描述符1,为什么没有输出显示。
答:文件描述符1对应显示器,关闭后自然就不会有内容显示到显示器了,但是也没有显示到文件log.txt中。
4️⃣重定向
🍙重定向现象
问题:上面例子中内容没有显示到显示器和文件log.txt中,那么内容实际上在哪个地方?
答:在文件的缓冲区中。
那是不是意味着我们刷新缓冲区就可以将内容显示出来?
int main()
{
close(1);
int fd=open("log.txt",O_WRONLY | O_CREAT| O_TRUNC,0666);
assert(fd!=-1);
printf("open sucess! fd:%d\n",fd);
fflush(stdout);//刷新缓冲区
close(fd);
return 0;
}
刷新后成功输出,内容本来应该输出到显示屏,但是现在输出到了log.txt这实际上就是输出重定向!!!(O_CREAT创建文件,O_TRUNC刷新文件,实现C语言中的w模式;O_APPEND追加内容,实现C语言中的a模式)
🍙重定向的本质
重定向的本质,其实是在OS内部,更改fd对应的内容的指向!!
🍙dup2系统调用
在重定向中,我们通常不使用close关闭fd,再重定向,而是直接使用系统调用dup2来重定向。
将oldfd的内容给newfd,即newfd对应内容指向oldfd
🌰dup2系统调用测试
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>
#include<unistd.h>
int main()
{
int fd=open("log.txt",O_WRONLY | O_CREAT| O_TRUNC,0666);
assert(fd!=-1);
dup2(fd,1);
printf("open sucess! fd:%d\n",fd);
close(fd);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/54634f3adbe57a1b2bc6def60191a917.png)
文末结语,本文讲解Linux的基础IO,包含内容:初识文件,系统文件I/O接口open、read、write、close,文件描述符的详细讲解以及文件描述符的分配规则,重定向现象的演示及重定向的本质讲解,实现重定向的系统调用dup2。如有需要,希望能有所帮助!!!