文件描述符fd
文件描述符就是内核中 struct file* fd_array[]
数组的下标,进程可以通过这个下标找到文件的描述信息,进而操作文件。
文件描述符(0 & 1 & 2)
- Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0(stdin), 标准输出1(stdout), 标准错误2(stderr)
- 0,1,2对应的物理设备一般是:键盘,显示器,显示器。所以输入输出还可以采用如下方式:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main() {
char buf[1024];
ssize_t s = read(0, buf, sizeof(buf));
if(s > 0){
buf[s] = 0;
write(1, buf, strlen(buf));
write(2, buf, strlen(buf));
}
return 0;
}
pcb ->files_struct ->fd_arry[]
而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件
文件描述符的分配规则
最小未使用
代码示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open err");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
输出发现是 fd: 3
关闭0或者2,在看
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
close(0);
//close(2);
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
发现是结果是: fd: 0 或者 fd: 2
文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
注意:
- 为什么打开一个文件,如果不操作了一定要关闭,释放资源?
文件描述符实际是有限的,若不关闭文件,文件描述符被用光了,就打不开新文件了
- printf打印数据到标准输出,close(1),就是把标准输出关闭了;打开新的文件后,printf并没有把数据打印出来,而是在刷新缓冲区之后,将数据写入到了文件中!
- printf并非真的一定要把数据写入到标准输出文件,而是因为printf函数中操作文件的时候描述符是1
- 原本向1中写入数据,就是向标准输出写入,然后当1指向了新的文件后,这时候printf就会将数据写入到指定新的文件中
- 在写入文件的时候:
- 向文件写入数据,并不会直接写入文件,而是先写入缓冲区中,刷新缓冲区的时候才会写入文件
- 系统调用接口是直接将数据写入文件的,系统调用接口是没有缓冲区,只有库函数才存在这个缓冲区
- 缓冲区实际上是文件流指针结构体中的缓冲区
文件流指针和文件描述符的关系
-
文件描述符:是一个非负整数
文件流指针:FILE结构体 - - typedef struct _IO_FILE FILE -
文件流指针是库函数IO接口的操作句柄
文件描述符是系统调用IO接口的操作句柄 -
文件流指针这个结构体中封装了文件描述符
-
通过文件流指针进行最终文件操作的时候,依然还要能够找到文件对应的文件描述符才可以
-
文件流指针是一个结构体,结构体中有很多成员变量,其中就有一个叫 _fileno (这是文件描述符)
重定向
那如果关闭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 err");
return 1;
}
printf("fd: %d\n", fd);
fflush(stdout);
close(fd);
exit(0);
}
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile
当中,其中,fd=1
。这种现象叫做输出重定向。
常见的输出重定向
有:> (清空重定向);>> (追加重定向)
符号 | 作用 |
---|---|
命令 > 文件 | 将标准输出重定向到文件中(清除原有文件中的数据) |
命令 >> 文件 | 将标准输出重定向到文件中(在原有的内容后追加) |
重定向的本质
针对文件描述符所对应的文件描述信息的重定向:
-
操作的文件描述符/文件流指针并没有改变,但是实际操作的文件却改变了
-
实现方式就是替换这个描述符对应的文件描述信息
-
实际是描述符的重定向,改变描述符所指向的文件,就改变了数据的流向
使用 dup2 系统调用
int dup2(int oldfd, int newfd); - 描述符重定向函数
- 让newfd这个描述符也指向oldfd所指向的文件,这时候oldfd和newfd都能够操作oldfd所指向的文件,两个描述符操作的都是oldfd指向的信息
- 因为newfd本身有可能已经打开了文件,但是现在要让它保存oldfd指向的信息,因此就需要先释放掉newfd当前的信息
第一行 >/dev/null , 2>&1 这两个重定向交换位置的意义不同!
相关习题
- 一个进程如何从用户态切换到内核态运行?
答案:系统调用接口,异常,中断
看到这里啦,动动小手留个赞呐!