文章目录
- fd-文件描述符
- 如何深度理解"一切皆文件"
- **我们使用OS的本质:**
- FILE
- `FILE`是什么?谁提供的?和我们刚刚讲的内核的struct有关系吗
- `FILE`是一个结构体.该结构体内部一定要有以下字段:
- `FILE`是C语言标准库提供的.
- `FILE`和我们刚刚讲的内核的struct没有关系,最多就是上下层的关系
- 做实验->重定向的本质
- 第一个实验->文件描述符的分配规则
- 第二个实验->输出重定向
- 重定向的原理
- 第三个实验->输入重定向
- 第四个实验->追加重定向
- 结论
- 需求:把常规消息放一个文件,错误消息放在另一个文件
- 更好的写法
- 让我们自己的程序支持重定向:
fd-文件描述符
任何一个进程,在启动的时候,默认会打开当前进程的三个文件
标准输入 | 标准输出 | 标准错误 | 本质都是文件 |
---|---|---|---|
stdin | stdout | stderr | 文件在语言层的表现 |
cin | cout | cerr | 同上,但是他是一个类 |
0 | 1 | 2 | <-fd ,数组下标 |
文件描述符,即open对应的返回值,本质就是:数组下标
标准输出和标准错误都会向显示器打印,但是其实是不一样的
类型 | 设备文件 |
---|---|
标准输入 | 键盘文件 |
标准输出 | 显示器文件 |
标准错误 | 显示器文件 |
#include<iostream>
#include<cstdio>int main()
{//因为linux一切皆文件,所以,向显示器打印,本质就是向文件中写入printf("hello printf->stdout\n"); fprintf(stdout,"hello fprintf->stdout\n");fprintf(stderr,"hello fprintf->stderr\n");std::cout << "hello cout -> cout" << std::endl;std::cerr << "hello cerr -> cerr" << std::endl;
}
int fd1 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//3
int fd2 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//4
int fd3 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//5
int fd4 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//6
int fd5 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//7
int fd6 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//8
如何深度理解"一切皆文件"
我们使用OS的本质:
都是通过进程的方式进行操作系统的访问,在进程的角度,只能看到文件对象,而看不到底层的设备的区别,所以我们才说"Linux下一切皆文件".
FILE
操作系统层面,我们必须使用fd才能找到文件!
任何语言层面访问外设或者文件,都必须经历OS
FILE
是什么?谁提供的?和我们刚刚讲的内核的struct有关系吗
#include<stdio.h>
FILE* fopen(const char *path,const char* mode);
答案:
FILE
是一个结构体.该结构体内部一定要有以下字段:
fd
证明:
int main()
{printf("%d\n",stdin->_fileno);printf("%d\n",stdout->_fileno);printf("%d\n",stderr->_fileno);FILE* fp = fopen(LOG,"w");printf("%d\n",fp->_fileno);
}
FILE
是C语言标准库提供的.
我们平时安装VS2019,是在安装IDE环境以及对应语言的库和头文件
FILE
和我们刚刚讲的内核的struct没有关系,最多就是上下层的关系
做实验->重定向的本质
第一个实验->文件描述符的分配规则
把fd为3的文件关闭以后,新的文件fd应该是什么
int main()
{close(0);//fclose(stdin)close(2);//fclose(stderr)int fd1 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);int fd2 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);int fd3 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);int fd4 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);int fd5 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);int fd6 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);printf("%d\n",fd1);0printf("%d\n",fd2);2printf("%d\n",fd3);3printf("%d\n",fd4);4printf("%d\n",fd5);5return 0;
}
进程中,文件描述符的分配规则:
最小的,没有被使用的数组元素,分配给新文件
第二个实验->输出重定向
int main()
{fclose(1);int fd = open(LOG, O_WRONLY | O_CREAT | O_TRUC, 0666);//此时log.txt的fd是'1'//但是上层结构不知道这个变化,他只知道要写进fd=1的文件中printf("you can see me!\n");//本来是指向stdout -> 1的,但是stdout变成了log.txtprintf("you can see me!\n");printf("you can see me!\n");printf("you can see me!\n");printf("you can see me!\n");return 0;
}
结果:打印不到屏幕,但是打印到了log.txt
printf("",);
不是认stdout,而是认fd==1的文件描述符
重定向的原理
在上层无法感知的情况下,在OS内部,更改进程对应的文件描述符表中,特定下标的指向!!
第三个实验->输入重定向
现在log.txt中写入:
123 456
int main()
{fclose(0);int fd = open(LOG, O_RDONLY | O_CREAT | O_TRUC, 0666);//fd=0int a,b;scanf("%d %d",&a,&b);printf("a=%d , b=%d\n",a,b);return 0;
}
结果: cat log.txt
:
a=123 , b=456
第四个实验->追加重定向
int main()
{close(1);//标准输出int fd = open(LOG, O_RDONLY | O_CREAT | O_APPEND, 0666);printf("you can see me!\n");//从屏幕(stdout)上追加到fd中printf("you can see me!\n");printf("you can see me!\n");printf("you can see me!\n");
}
结果: cat log.txt
:
a=123 , b=456
you can see me!
you can see me!
you can see me!
you can see me!
结论
所以stdout cout->1,他们都是向1号文件描述符对应的文件打印
stderr cerr ->2 ,他们都是向2号文件描述符对应的文件打印
输出重定向,只改的是1号对应的指向,对2号不影响
需求:把常规消息放一个文件,错误消息放在另一个文件
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#define LOG "log.txt"
#define LOG_NORMAL "logNormal.txt"
#define LOG_ERROR "logError.txt"int main()
{close(1);int fd = open(LOG_NORMAL, O_WRONLY | O_CREAT | O_APPEND, 0666);close(2);int fd = open(LOG_ERROR, O_WRONLY | O_CREAT | O_APPEND, 0666);printf("hello printf->stdout\n"); fprintf(stdout,"hello fprintf->stdout\n");fprintf(stderr,"hello fprintf->stderr\n");}
所以为什么要默认把1和2打开:
就是为了把常规消息和错误消息分类开来,便于后面的调试!
bash中重定向操作
a.out > log.txt 2 > &1
或者
a.out 1>log.txt 2>err.txt
2 > &1
把1里面的内容,写到2下标的内容里
更好的写法
int dup2(int oldfd, int newfd)
是对数组对应下标的内容进行拷贝
new要成为old的拷贝
所以最终只有oldfd的内容了
而我们最后正确重定向肯定是剩下3啊
所以oldfd 是3
newfd 是1
所以代码
dup2(fd,1)
重定向写法:
int main ()
{int fd = open(LOG_NORMAL, O_WRONLY | O_CREAT | O_APPEND, 0666);if(fd < 0){perrer("open");return 1;}dup2(fd,1);printf("hello world,hello lx\n");close(fd);
}
就是打开文件,之后使用dup2
就行
让我们自己的程序支持重定向:
enum redir{REDIR_INPUT = 0,REDIR_OUTPUT,REDIR_APPEND,REDIR_NONE
};char* checkdir(char commandstr[],redir &redir_type);
{//1.监测是否有 > < >> //2.如果有,要根据> < >> 设置redir_type = ?//3.将符号改成\0,分成两部分//保存文件名,并返回//如果不满足,直接返回
}int main()
{while(1){redir redir_type = REDIR_NONE//...char* filename = NULL;char* filename = checkdir(commandstr,&redir_type);if(*filename){//1.存在文件//2.获取redir_type}//遍历,看是否有> < >>,这三个字符//前半部分执行后续//把这三个字符变成\0,将后面的字符串打开文件//dup2(fd,1);//...if(id == 0){if(redir_typr != REDIR_NONE){dup2();}}}
}
未完待续…