目录
一、重定向
1、重定向的本质
2、使用 dup2 系统调用
(1)输出重定向
(2)追加重定向
(3) 输入重定向
二、缓冲区
1.理解缓冲区
2.缓冲区刷新问题
3.为什么要有缓冲区?
4.这个缓冲区在哪里?
一、重定向
什么是重定向,是本来写到显示器上的内容写入到文件当中。
其中, fd = 1 。这种现象叫做输出重定向。常见的重定向有:>,>>,<
1、重定向的本质
重定向的本质其实就是在 OS 内部更改 fd 对应的内容指向。也就是本该写入到1号文件描述符的内容都写到了log.txt中。
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6
7 int main()
8 {
9 close(0);
10 int fd =open("log.txt",O_RDONLY);
11 if(fd<0)
12 {
13 perror("open");
14 return 1;
15 }
16 printf("fd:%d\n",fd);
17 char buffer[64];
18 fgets(buffer,sizeof buffer,stdin);
19 printf("%s",buffer);
20 return 0;
21 }
本应该从键盘读取的内容,现在从 log.txt 中读取。这就叫作输出重定向。
本应该从显示器上读取,现在从log.txt中读取,这是输入重定向。
这是追加重定向。
2、使用 dup2 系统调用
这个oldfd和newfd我们该怎么区分呢?
很明显依靠函数原型,我们就能认为 dup2(1, fd),因为 1 是先打开的,而 fd 是后打开的.可实际上并不是这样的,文档中说 newfd 是 oldfd 的一份拷贝,这里拷贝的是文件描述符对应数组下标的内容,所以数组内容最终应该和 oldfd 一致。
换而言之,这里就是想把让 1 不要指向显示器了,而指向 log.txt,fd 也指向 log.txt。所以这里的 oldfd 对应 fd,newfd 对应 1,所以应该是 dup2(fd, 1)。
我们最后的文件描述符要跟oldfd一致。
dup(3,1)是把三号文件描述符的内容拷贝到一号文件描述符里。
(1)输出重定向
- < 就是 dup2(fd, 0),且 open 文件的方式是 O_RDONLY;
(2)追加重定向
(3) 输入重定向
无非是在追加重定向基础上更改
二、缓冲区
1.理解缓冲区
先看一段代码
- 当我们去掉换行,关闭1号文件描述符时,显示器和重定向到文件都显示不了内容。
- 只有write的时候,也关闭了1号文件描述符,显示器和重定向到文件却能显示。
这是为什么呢?
这是因为存在缓冲区,C库函数接口没带"\n",没有打印到显示器上,先写到了缓冲区上 。
这个缓冲区一定不在操作系统内部!不是系统级别的缓冲区!
write系统调用接口,使用系统内核的缓冲区,进程结束的时候直接刷新到磁盘上。
而printf/fprintf/fwrite...等C库函数,并没有把数据写入到系统级别的缓冲区中,C语言会提供一个缓冲区(这叫应用层缓冲区,也是用户级缓冲区),会先把数据写入到用户级缓冲区里。再调用write系统调用接口,在合适的时候把用户级缓冲区的内容写入到系统缓冲区里。
可这段代码中,执行到close(1)时,还没有调用write讲数据写入到1号文件描述符,就把对应的显示器文件描述符关闭了,此时的数据还在用户级缓冲区,刷新时显示不出来。
我们再来看一段代码
结果如下:
同样一个程序,向显示器打印输出 4 行文本,而向普通文件(磁盘)上打印输出 7 行文本。其中,printf 和 fwrite (库函数)都输出了 2 次,而 write 只输出了一次(系统调用),为什么呢?
2.缓冲区刷新问题
我们先给大家讲解缓冲区刷新
- 当进程退出的时候,直接刷新。
- 无缓冲区——直接刷新。
- 行缓冲区——不刷新,直到遇到了\n才刷新。
- 全缓冲区——直到缓冲区满了才刷新。
- 我们的显示器文件的刷新方案就是行刷新,所以在printf函数遇到\n会直接刷新到显示器中。
- 讲数据写入到普通文件是全缓冲区刷新,数据写满缓冲区才刷新。或者写完了数据,进程退出了也会刷新。
我们再重回刚刚的问题:
在向显示器打印输出是是行刷新,遇到了\n就刷新,所以我们在上面写入的4个接口的内容能直接打印到显示器上。
而写入到文件时全刷新,当缓冲区满了或者进程结束了才刷新。
这段代码依次往下执行,printf/fprintf/fwrite这三个是C库函数写入到C缓冲区中,等待合适的时候调用write系统调用接口写入系统缓冲区中,执行到write时,因为write是直接写道系统缓冲区,所以被直接刷新到显示器上,所以优先打印出hello write
再往下执行到fork时,此时数据还在C缓冲区中,但是 fork 的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。 从而导致C库函数被打印了两次。
3.为什么要有缓冲区?
- 解决了效率问题,在printf/fwrite把数据放入缓冲区中直接返回了。
- 语言配合的格式化(输入、输出的转换),我们写在显示器上,123,这个123是字符1字符2字符3。我们写int a=10; printf("hello%d\n",a); 打印的是整数。这个时候就需要缓冲区进行类型的转化。
4.这个缓冲区在哪里?
我们之前说文件操作系统离不开FILE struct,FILE结构体里面还有对应打开文件的缓冲区字段和维护信息。
每一个文件都对应一个语言缓冲区。
我们打开10个文件,有10个文件描述符,也就有10个对应的语言缓冲区。
FILE对象是属于用户还是操作系统呢?
属于用户,因为语言都属于用户,FILE内有语言缓冲区。