1.文件描述符
通过对open函数的学习,我们知道了文件描述符就是一个小整数
1.1 0 & 1 & 2
Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2。
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;
}
运行后显示要求输入值:
标准输入流读取后,在标准输出流和标准错误流中输出,也就是从键盘读取后输出到屏幕上。
而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件
1.2文件描述符分配规则
#include <unistd.h>
#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或者2,在看:
#include <unistd.h>
#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数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
2.重定向
如果关闭文件描述符1呢?
#include<unistd.h>
#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);
exit(0);
}
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>, <
重定向的本质就是改变这些文件描述符所指向的位置,使其指向用户指定的文件或其他资源。
3.dup2系统调用
3.1 dup2函数基本信息
dup2是Unix/Linux系统中一个重要的系统调用函数,用于复制文件描述符,并将其重定向到指定的新文件描述符上。
函数原型:
int dup2(int oldfd, int newfd);
参数:
oldfd:待复制的文件描述符。
newfd:指定的新文件描述符。
返回值:成功时返回newfd,出错时返回-1。
man dup查看详细信息
3.2工作原理
检查oldfd和newfd是否相等:如果两者相等,则不进行任何操作,直接返回newfd。
检查newfd的合法性:如果newfd已经打开,则先关闭它。
复制oldfd的文件表项到newfd:使得newfd和oldfd指向同一个文件表项,共享相同的文件偏移量和文件状态标志。
返回newfd:此时,newfd和oldfd都指向同一个文件,但可以通过不同的文件描述符进行操作。
3.3常见用途
重定向标准输入、输出和错误输出:通过dup2函数,可以将文件描述符与标准输入(stdin,文件描述符0)、标准输出(stdout,文件描述符1)和错误输出(stderr,文件描述符2)进行关联,实现输入输出的重定向。例如,将标准输出重定向到文件中,以便将程序的输出结果保存到文件中。
管道通信:在进程间通信中,管道是一种常用的通信方式。通过dup2函数,可以将管道的读端或写端复制到标准输入或标准输出,从而实现进程间的数据传输和信息交换。
文件描述符的复制:有时候需要复制一个文件描述符,以便在不同的上下文中使用。通过dup2函数,可以创建一个新的文件描述符,并复制旧的文件描述符的状态,使得两者指向同一个文件表项。这样做的好处是在不改变原来文件描述符设置的情况下,可以在新的文件描述符上进行操作。
3.4示例
使用dup2函数将标准输出重定向到文件:
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
int main() {
int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return -1;
}
// 将标准输出重定向到文件
if (dup2(fd, STDOUT_FILENO) == -1) {
perror("dup2");
close(fd);
return -1;
}
// 此时,标准输出已经重定向到文件,接下来的printf将输出到文件
printf("Hello, World!\n");
// 关闭原始的文件描述符(可选,因为进程结束时会自动关闭)
close(fd);
return 0;
}