目录
一、重定向
文件描述符的分配规则:查看自己的文件描述表,分配最小的没有被使用的fd
重定向的本质:是在内核中改变文件描述符表
特定下标的内容,和上层无关
重定向的本质:是在内核中改变文件描述符表特定下标的内容
至于,你这个下标指向什么位置,和我重定向无关,我只认这个下标
所以,事实上,要做重定向,是两个文件描述符指向的文件的改变
所以,需要操作系统提供一个专门用于文件描述符改向的系统调用
这个系统调用就叫做dup2
#include <unistd.h>
int dup2(int oldfd, int newfd);
参数
oldfd:原始文件描述符,表示要复制的文件描述符。
newfd:目标文件描述符,表示将 oldfd 复制到的文件描述符。返回值
成功时,dup2 返回 newfd,这是复制操作后新的文件描述符。
失败时,返回 -1,并将 errno 设置为相应的错误代码。把oldfd复制到newfd
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd;
// 打开文件,如果文件不存在则创建,权限为0644
fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
return 1;
}
// 将标准输出重定向到fd
int ret = dup2(fd, STDOUT_FILENO);
if (ret < 0) {
perror("dup2");
return 1;
}
// 关闭原文件描述符
close(fd);
return 0;
}
>:将命令的标准输出重定向到文件,会覆盖文件内容。
示例:
ls > file.txt 将 ls 命令的输出写入 file.txt 文件中,覆盖原有内容。
<:将文件内容作为命令的标准输入。
示例:
sort < input.txt 将 input.txt 文件的内容作为 sort 命令的输入进行排序
>>:将命令的标准输出重定向到文件,追加到文件末尾。
示例:
echo "Hello" >> greetings.txt 将 "Hello" 追加到 greetings.txt 文件的末尾。
二、缓冲区
1、缓冲区现象
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
close(1);
int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
//fflush(stdout);
close(fd);
exit(0);
}
当运行上述代码时,
在没有fflush函数之前,即使对一个文件写入了内容,也没有显示
但是使用了fflush函数之后,就显示了
这是为什么?
原因:
struct FILE 结构体内还有一个语言级别的文件缓冲区
你使用的printf、fprintf函数等
写入的数据,都是写到了语言级别的文件缓冲区
而不是在内存的缓冲区
所以,fflush函数所作的工作,其实就是把语言级别的缓冲区,
刷新到内存中
如果在文件关闭之前,没有进行刷新,
那么,就无法把语言级的缓冲区刷新到内存
上述的代码中,把1文件(标准输出,显示器)关闭后,再新建一个myfile文件,他的fd会被分配到1,因为1是空着的。此时,我把信息写入到myfile文件内,即fd为1的文件,此时,只是写到了myfile语言级缓存内,还没有刷新到内存中。可是,我们的操作系统还没有来得及刷新,代码就跑完了,直接close掉文件,我们就无法进行刷新。
2、理解缓冲区
缓冲区区分:用户级缓冲区 和 内核级缓冲区
其缓冲区的作用是:提高效率(提高使用者的效率)
例如说,当我们使用c语言的函数接口时
数据会存在语言级的缓冲区内
而不会立马刷新到内存,而是在合适的时机再刷新到内存
在用户的角度来看,速度就快了
同时,调用系统调用是有成本的
所以,尽量少调用,以提高效率
那么怎么办呢?
其采取的策略是,
你调用了多次c语言函数接口
此时,就会积攒数据在语言级的缓冲区内
然后,再调用一次系统调用接口
将所有数据全部刷新到内存中(文件内级的缓冲区)
这样,就提高了整体的效率
提高了使用者的效率,也提高了硬件IO的效率
这就好比顺丰快递
快递不是一件一件的寄送,因为发送快递是有成本的
所以要积攒到一定数量,一车一车的送
所以,什么是缓冲区?
就是一段内存空间
为什么会有缓冲区?
存在的目的就是提高效率
给上层提供良好的IO体验
间接提高整体的IO效率
缓冲区如何提高效率?
(1)刷新策略
立即刷新(可以认为是无缓冲),如fflush(stdout)和 fsync(int fd)函数,都是立即刷新到外设
行刷新:显示器(照顾人的查看习惯,人习惯一行一行的看文字)
全缓冲:缓冲区写满才刷新,普通文件
(2)特殊情况
进程退出,系统会自动刷新缓冲区
强制刷新
3、stderr是什么?
为什么要默认打开0,1,2?
我们写的程序,本质都是对数据的处理(计算、存储...)
那么数据从哪里来?
数据去到哪里?
所以,为什么有0 和 1?
为了解决用户更好的动态获取数据的来源、去处、状态等信息
所以,默认把0 和 1打开
可是,为什么有2呢?
我们打印的信息,会有错误的信息和正确的信息
我们就可以用重定向把正确的和错误的信息分开
方便查看错误
常用的perror这个函数
就是往2文件内打印,而不是1
完整的重定向语法:把1的信息输入到ok.log,2的信息输入到err.log
./a.out 1>ok.log 2>err.log
4、相关知识
系统调用是直接刷新到文件内核的缓冲区
注意,并不是直接刷新到硬件磁盘上
缓冲区在哪?
在FILE结构体内部
每一个文件都有自己的FILE*结构体,每一个文件都有自己的缓冲区
c语言为什么要在FILE结构体提供用户级缓冲区?
为了减少底层调用系统调用的次数,让使用C语言的IO函数(printf、fprintf等)效率更高