Linux:带你理解文件描述符和重定向


文件描述符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数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

注意:

  1. 为什么打开一个文件,如果不操作了一定要关闭,释放资源?

文件描述符实际是有限的,若不关闭文件,文件描述符被用光了,就打不开新文件了

  1. printf打印数据到标准输出,close(1),就是把标准输出关闭了;打开新的文件后,printf并没有把数据打印出来,而是在刷新缓冲区之后,将数据写入到了文件中!
  • printf并非真的一定要把数据写入到标准输出文件,而是因为printf函数中操作文件的时候描述符是1
  • 原本向1中写入数据,就是向标准输出写入,然后当1指向了新的文件后,这时候printf就会将数据写入到指定新的文件中
  1. 在写入文件的时候:
  • 向文件写入数据,并不会直接写入文件,而是先写入缓冲区中,刷新缓冲区的时候才会写入文件
  • 系统调用接口是直接将数据写入文件的,系统调用接口是没有缓冲区,只有库函数才存在这个缓冲区
  • 缓冲区实际上是文件流指针结构体中的缓冲区

文件流指针和文件描述符的关系

  1. 文件描述符:是一个非负整数
    文件流指针:FILE结构体 - - typedef struct _IO_FILE FILE

  2. 文件流指针是库函数IO接口的操作句柄
    文件描述符是系统调用IO接口的操作句柄

  3. 文件流指针这个结构体中封装了文件描述符

  4. 通过文件流指针进行最终文件操作的时候,依然还要能够找到文件对应的文件描述符才可以

  5. 文件流指针是一个结构体,结构体中有很多成员变量,其中就有一个叫 _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 这两个重定向交换位置的意义不同!

相关习题

  1. 一个进程如何从用户态切换到内核态运行?

答案:系统调用接口,异常,中断


看到这里啦,动动小手留个赞呐!

猜你喜欢

转载自blog.csdn.net/AngelDg/article/details/106783098