一、文件描述符(fd)
1.文件描述符其实就是一个非负的小整数。是文件指针数组的下标。
2.让我们看一看0,1,2,…代表什么?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main()
{
char buf[1024];
printf("fd0\n");
ssize_t s=read(0,buf,sizeof(buf));
if(s>0)
{
buf[s]=0;
printf("fd1\n");
write(1,buf,strlen(buf));
printf("fd2\n");
write(2,buf,strlen(buf));
printf("fd3\n");
write(3,buf,strlen(buf));
}
return 0;
}
从运行程序来看,文件描述符0,1,2是Linux默认情况下三个缺省打开的。3不是缺省的。
0,1,2对应的物理设备分别是键盘、显示器、显示器。
//标准输入、输出
执行一个shell命令行时通常会自动打开三个标准文件,
即标准输入文件(stdin),通常对应终端的键盘;
标准输出文件(stdout)和标准错误输出文件(stderr),这两个文件都对应终端的屏幕。
3.文件描述符如何做到找到对应文件的?
每个进程都有一个PCB,每一个PCB中都包含一个file指针,它指向一个文件结构体,结构体中包含了一个文件指针数组,每个元素都包含了一个指向打开文件的指针!,所以文件描述符就是该文件指针数组的下标,因此,我们只需要知道文件描述符,就可以通过文件描述符去找到指针指向的文件。
4.文件描述符的分配规则
在file_struct结构体中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
//文件描述符也是一种资源,这种分配规则可以很好的利用资源。
1)当没有关闭默认文件描述符时,新文件的文件描述符等于3。
2)当关闭了0时,新文件的文件描述符是0。
3)关闭使用close(0)
5.重定向
概念:将各种请求重新定个方向转到其它位置
分类:重定向有输出重定向(>)、输入重定向(<)、追加重定向(>>)。
⑴先看一个小程序,关闭文件描述符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);
return 0;
}
⑵根据分配规则,新打开的文件接收到的文件描述符是1,但结果是什么呢?看下图↓
我们发现,fd:1本来是往屏幕输出的,可是它并没有,而是输出到了打开的文件中,我们把这种现象称为输出重定向。
重定向有输出重定向(>)、输入重定向(<)、追加重定向(>>)。
那它到底发生了什么?看下图↓
⑶printf是C库函数,本应该往stdout中输出,但此时fd:1的下标存放的是新文件的地址。也就是说标准输出被占用了,无法往显示器输出,这叫输出重定向。
二、FILE
1.FILE是在stdio.h定义的保存文件流信息的一个结构体类型。
2.本质上访问文件都是通过fd访问的,库函数封装了fd,所以,FILE结构体内部肯定封装了fd。
3.fork缓冲问题。
//行缓冲:比如写入显示器中,输入的数先进入行缓冲区,当满足条件时,在进一步输出到屏幕。
//全缓冲;比如写入文件中。
//无缓冲
①使用三种操作方式(库函数、系统调用)往显示器打印数据
int main()
{
const char *msg0="printf\n";
const char *msg1="fwrite\n";
const char *msg2="write\n";
printf("%s", msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1, msg2, strlen(msg2));
fork();
return 0;
}
②对进程进行输出重定向
为什么会出现这种现象呢?不应该都是出现两次吗?
答:当进程输出重定向到新文件时时,数据的缓冲方式由行缓冲转换为全缓冲,printf与fwrite(库函数自带缓冲区)先进入了全缓冲区,不会立即被刷新;write是系统调用,没有缓冲区,直接输出到了屏幕,当下一步执行fork()函数时,子进程的执行起点为fork之后,但此时父子数据会发生写时拷贝,所以当父进程全缓冲区准备刷新的时候,子进程也有了同样的一份数据,随机产生两份数据。那为什么子没有write数据呢?因为写时拷贝,只有在写入时,数据才被复制。