【Linux基础I/O】文件描述符及重定向


在Linux下,一切皆文件, 现在我们来深刻感受一下什么叫一切皆文件。我们知道一个进程可以打开多个文件,多个进程就可以打开多个文件。打开后的文件,会被加载进内存中。那么操作系统就要对打开的文件做管理。那么也是需要 先描述,再组织。研究文件的操作也就是研究 进程和打开文件的关系。

在讲解之前我们来回顾一下C语言中是如何操作文件的。

一,C语言的文件接口

我们先来看一段代码:

向文件里写:

#include <stdio.h>
#include <string.h>
int main()
{
	FILE *fp = fopen("myfile", "w");
	if(!fp){
		printf("fopen error!\n");
	}
	const char *msg = "hello bit!\n";
	int count = 5;
	while(count--){
		fwrite(msg, strlen(msg), 1, fp);
	}
	fclose(fp);
	return 0;
}

读文件:

#include <stdio.h>
#include <string.h>
int main()
{
	FILE *fp = fopen("myfile", "r");
	if(!fp){
		printf("fopen error!\n");
	}
	char buf[1024];
	const char *msg = "hello bit!\n";
	while(1){
	//
		ssize_t s = fread(buf, 1, strlen(msg), fp);
		if(s > 0){
			buf[s] = 0;
			printf("%s", buf);
		}
		if(feof(fp)){
			break;
		}
	}
	fclose(fp);
	return 0;
}

这里的文件操作的返回值都是一个FILE* 的类型,打开文件时都要传入对应的参数表示打开方式。

这里我们先来说一个概念:

C语言默认会打开三个输入输出流,分别是stdin, stdout, stderr。而且这三个流的类型也是FILE*,也就是文件指针

我们在打开或者关闭文件的时候,打开的都是磁盘上的文件,磁盘属于外设,所以文件操作的接口底层一定封装了系统调用,我们直接来看系统调用。

二,文件操作的相关系统调用接口

下面我们来一一介绍系统调用接口:

2.1 open

在这里插入图片描述

这里可以看到,open接口的返回值为int,我们后面再讨论。而这里的参数pathname:表示要打开或创建的目标文件,而flags表示打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags:(相当于C语言打开文件时传入的打开方式)

在这里插入图片描述

在这里插入图片描述

int fp = open("test2.txt",O_RDONLY|O_CREAT,0666);

2.2 close

在这里插入图片描述
close可以关闭指定的文件,这个参数fd表示文件描述符

close(fd);

2.3 write

在这里插入图片描述
write可以向文件描述符为fd的文件中写入nbyte字节的buf

char* buffer = "abcdef";
int fd = open("/home/ljh/test.txt",O_WRONLY);
write(fd,buffer,sizeof(buffer));

这段代码意思是以只写的方式打开/home/ljh/下的test.txt,向其中写入buffer

2.4 read

在这里插入图片描述

read接口可以打开文件描述符为fd的文件,并且把文件的内容读到buf中,读nbyte个字节。

char buffer[1024];
int fd = open("/home/ljh/test.txt",O_WRONLY);
ssize_t n = read(fd,buffer,sizeof(buffer));
if(n > 0)
{
	buffer[n] = '\0';//将字符串变成C语言风格,以\0结尾
}

三,文件描述符

我们在使用系统调用时都会看到对应的接口都有一个返回值,这个返回值类型为int。这个返回值其实就是文件描述符,那么什么是文件描述符呢?我们现在来看看打开一个文件,底层都做了哪些工作。

3.1 文件描述符的本质

当一个进程打开一个文件时,其实操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体表示一个已经打开的文件对象

在这里插入图片描述

而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct。这个表也叫文件描述符表。该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针。

而文件描述符其实就是该数组的下标,只要拿着文件描述符,就可以找到对应的文件

从上面的图中我们可以清楚看到,每个进程的文件描述符表中前三个默认就是stdin,stdout,stderr.对应的文件描述符为0,1,2。也就是说每个进程都会打开三个文件,分别是键盘,显示器,显示器。

3.2 理解一切皆文件

Linux下一切皆文件其实就是操作系统为显示器,键盘等都创建一个结构体,结构体中包含该硬件的读写方法的函数指针,可以通过read系统调用接口来调用硬件的读写方法,可以忽视底层的差异。

其实也就是多态
在这里插入图片描述

3.3 文件描述符分配规则

从上面我们可以知道,文件描述符表的前三个都默认给了标准输入,标准输出,标准错误。那么其余的文件描述符表示的是什么呢?

看下面的代码:

#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");
		return 1;
	}
	printf("fd: %d\n", fd);
	close(fd);
	return 0;
}

输出后

fd=3

关闭0号文件后观察结果:

#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

从这个例子我们可以得到:
文件描述符的分配规则就是,在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符

在这里插入图片描述

四,重定向的本质

4.1 重定向的本质

知道了文件描述符的分配规则,从上面的例子我们可以看到,当关闭了0号文件后,这个0号下标的文件从键盘改到了log.txt中。
所以再次运行程序后,我们就不能从键盘中来获取输入,而是从log.txt中获取。这种现象我们叫重定向

在这里插入图片描述
如果想让把打在显示器中的内容打在文件中,我们可以关掉1号下标的文件,也就是显示器,进而我们printf的内容都会被写在我们打开的文件中。

4.2 重定向的接口

在这里插入图片描述
这里也有专门进行重定向的系统调用接口,dup2就可以将文件描述符为oldfd的文件覆盖到newfd位置上。

int fd = open("log.txt",O_WRONLY | O_CREAT);
dup2(3,1);

这样我们就可以将原本打在显示器上的内容打在log.txt中了。

五,总结

这篇文章只讲解了Linux的I/O的一半的内容,后面我们还要了解Linux的文件系统和动静态库和缓冲区的问题,希望大家可以继续关注。

  • 38
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值