目录
文件内核对象
文件描述符的分配规则
重定向
重定向的概念
dup2系统调用
输出重定向
追加重定向
输入重定向
stderr解析
重定向到同一个文件中
分离常规输出和错输出
文件内核对象
上篇文章中我们介绍到了操作系统中的文件,操作系统为了方便管理进程打开的每个文件都会给每个文件创建一个内核对象(struct file)。但是每个进程可能打开多个进程,因此操作系统会形成一个数组,数组中的每个元素为文件内核对象指针指向当前进程打开的每个文件;数组的下标就代表一个打开的文件;对文件进行操作就是对数组的下标进行操作。打开的文件会加载在内存中,每个文件也都会有一段内存空间(文件缓冲区);打开文件会对文件进行操作,一般就是读操作和写操作;因此每个文件内核对象中一定含有这三种信息:
- 打开文件的属性
- 进行文件操作的方法集
- 文件的内存空间(文件缓冲区)
总结:
- 不能对磁盘中的内存进行操作,只能先将文件加载到内存中。
- 对文件进行读数据和写数据都先要将文件加载到内存中(当文件不在内存中时会造成缺页中断,操作系统自动会将文件加载到内存中)。
- 数据的读写本质是将内核缓冲区中的数据进行来回拷贝。
文件描述符的分配规则
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7 #define FILE_NAME "log.txt"
8 int main()
9 {
10
11 char buffer[1024];
12 ssize_t s = read(0,buffer,1024);
13 if(s>0)
14 {
15 buffer[s-1]=0;
16 printf("echo# %s\n",buffer);
17 write(1,buffer,strlen(buffer));
18 }
26 return 0;
27 }
可以直接使用0和1文件描述符进行键盘和显示器的读写。
#include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7 #define FILE_NAME "log.txt"
8 int main()
9 {
10 int fd = open(FILE_NAME,O_CREAT|O_WRONLY|O_TRUNC,0666);
11 if(fd<0)
12 {
13 perror("open");
14 return 0;
15 }
16 printf("fd:%d\n",fd);
close(fd);
17 return 0;
18 }
上篇文章我们说过进程会默认打开三个流(stdin、stdout、stderr),分别为0,1,2。
文件描述符的分配规则:寻找最小的,没有被使用的数据的位置分配给指定的打开文件。
重定向
重定向的概念
- 改变原来系统命令的默认执行方式
- Linux重定向是指修改原来默认的一些东西,对原来系统命令的默认执行方式进行改变,比如说简单的我不想看到在显示器的输出而是希望输出到某一文件中就可以通过Linux重定向来进行这项工作。
先执行一段代码,看现象:
#include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7 #define FILE_NAME "log.txt"
8 int main()
9 {
10
11 close(1);
12 int fd = open(FILE_NAME,O_CREAT|O_WRONLY|O_TRUNC,0666);
13 if(fd<0)
14 {
15 perror("open");
16 return 0;
17 }
18 printf("fd : %d \n",fd);
19 printf("stdout->fd : %d \n",stdout->_fileno);
fflush(stdout);
close(fd)
return 0;
}
当我们不使用fflush刷新stdout时,显示器和新打开的文件都不会输出我们打印的内容,现象对应我们的第一张图片。使用fflush刷新stdout时,显示器不会输出我们的内容,但是新打开的文件中含有我们输出的内容。
现象的解释:进程运行是会打开我们的stdin、stdout、stderr,对应的文件描述符为0,1,2。当我我们关闭1,即关闭显示器。当新打开文件时根据文件描述符的分配规则文件描述符为1,但是printf、fprintf底层只认文件描述符1,但是此时的文件描述符已经不是stdout,而是新打开的文件,因此输出到新打开的文件中。
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7 #define FILE_NAME "log.txt"
8 int main()
9 {
10
11 close(0);
12 char buffer[1024];
13 int fd = open(FILE_NAME,O_RDONLY);
14 if(fd<0)
15 {
16 perror("open");
17 return 0;
18 }
19 fread(buffer,1,sizeof(buffer),stdin);
20 printf("%s\n",buffer);
close(fd)
return 0;
}
根据上面的原理我们可以关闭0,即关闭从键盘输入;打开新文件,此时新文件的文件描述符为0,从新打开的文件中读取数据输入。
dup2系统调用
通过上面的代码我们可以实现输入、输出重定向;但是需要我们手动关闭键盘或者显示器非常的麻烦,Linux中提供了系统调用,方便我们进行重定向。
- 当调用dup函数时,内核在进程中创建一个新的文件描述符,此描述符是当前可用文件描述符的最小数值,这个文件描述符指向oldfd所拥有的文件表项。
- dup2和dup的区别就是可以用newfd参数指定新描述符的数值,如果newfd已经打开,则先将其关闭。如果newfd等于oldfd,则dup2返回newfd, 而不关闭它。dup2函数返回的新文件描述符同样与参数oldfd共享同一文件表项。
输出重定向
#include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7 #define FILE_NAME "log.txt"
8 int main()
9 {
10 int fd = open(FILE_NAME,O_CREAT|O_WRONLY|O_TRUNC,0666);
11 if(fd<0)
12 {
13 perror("open");
14 return 0;
15 }
16 dup2(fd,1);
17 printf("fd : %d \n",fd);
18 printf("stdout->fd : %d\n ",stdout->_fileno);
19 printf("hello Linux\n");
20 fprintf(stdout,"hello world\n");
close(fd);
return 0;
}
使用系统调用进行重定向时,并不会像我们一样关闭键盘或者显示器而是创建新的文件描述符,然后让键盘或者显示器的文件描述符和键盘、显示器断开;和我们新打开的文件建立联系,此时这个文件就含有两个文件描述符,使用引用计数进行关闭文件。
追加重定向
#include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7 #define FILE_NAME "log.txt"
8 int main()
9 {
10 int fd = open(FILE_NAME,O_CREAT|O_WRONLY|O_APPEND,0666);
11 if(fd<0)
12 {
13 perror("open");
14 return 0;
15 }
16 dup2(fd,1);
17 printf("fd : %d \n",fd);
18 printf("stdout->fd : %d\n ",stdout->_fileno);
19 printf("hello Linux\n");
20 fprintf(stdout,"hello world\n");
close(fd);
return 0;
}
输入重定向
#include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7 #define FILE_NAME "log.txt"
8 int main()
9 {
10 // int fd = open(FILE_NAME,O_CREAT|O_WRONLY|O_APPEND,0666);
11 int fd = open(FILE_NAME,O_RDONLY);
12 if(fd<0)
13 {
14 perror("open");
15 return 0;
16 }
17 dup2(fd,0);
18 // printf("fd : %d \n",fd);
19 // printf("stdout->fd : %d\n ",stdout->_fileno);
20 // printf("hello Linux\n");
21 // fprintf(stdout,"hello world\n");
22 char buffer[1024];
23 fread(buffer , 1,1024,stdin);
24 printf("%s",buffer);
close(fd);
return 0;
}
stderr解析
上篇文章我们提到stdout和stderr,都代表显示器流即往显示器文件中打印。
#include<stdio.h>
int main()
{
fprintf(stdout,"hello stdout\n");
fprintf(stderr,"hello stderr\n");
return 0;
}
但是我们进行重定向时只会将stdout进行重定向到文件中 ;stderr会在进程中保留。
因为fprintf底层只认文件描述符1,因此stderr不会被重定向。
重定向到同一个文件中
分离常规输出和错输出
进程运行时难免发生错误,输出错误信息;为了和常规信息进行区别,错误信息和常规信息分别独占一个显示器文件夹,我们只需要查看错误信息的显示器文件夹即可发现程序的报错信息。
今天对Linux下文件重定向的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!