一:重定向
./stream > log.txt 在这行代码中,我们将运行stream得到的结果重定向到 log.txt 这个文件中,而实际上这行代码的完整写法为./stream 1 > log.txt,即将运行stream得到的结果从标准输出(stdout文件描述符为1)重定向到 log.txt 这个文件中
stream.cc代码:
1 #include<iostream>
2 #include<cstdio>
3
4 int main()
5 {
6 //向标准输出打印,stdout 1
7 std::cout << "hello cout" << std::endl;
8 printf("hello printf\n");
9
10 //向标准错误打印,stderr 2
11 std::cerr << "hello cerr" << std::endl;
12 fprintf(stderr,"hello stderr\n");
13
14 return 0;
15 }
如上面代码所示:我们分别在标准输出(cout)打印hello cout语句并使用printf函数打印hello printf语句和在标准错误(cerr)打印hello cerr语句并使用fprintf函数向标准输出stderr打印hello stderr语句,而两者都是通过显示器打印,结果如下所示:
但如下面两张图所示,我们发现将运行stream得到的结果重定向到 log.txt 这个文件中时只打印了hello cerr和hello stderr这两条语句,而打印log.txt文件中的内容时只打印了hello cout和hello printf这两条语句。这是为什么呢?这是因为执行./stream > log.txt语句时是将stream执行的结果从标准输出(stdout)重定向到log.txt文件中,(即在log.txt文件中写入hello cout和hello printf语句)而另外两条语句是在标准错误文件中,因此执行./stream > log.txt语句时会将还存在显示器上的hello erro和stderr语句打印出来,即执行./stream > log.txt语句只做了标准输出的重定向而没有做标准错误的重定向
1-1 :那如果我们想做标准错误的重定向呢?
向指定文件进行标准错误的重定向:
./stream 1>log.normal 2>log.error
如上图所示:在显示器上输入上面语句,即将执行stream文件的结果并将标准输出(1)重定向到log.normal文件中;将标准错误(2)重定向到 log.error文件中 。这样就可以将标准错误重定向到log.error这个文件中了
把标准输出和标准错误重定向到同一个文件:
方法1:
./stream 1>log.normal 2>>log.normal
如上图所示:我们将执行stream文件的结果并将标准输出(1)重定向到log.normal文件中;并且将标准错误(2)追加到 log.normal文件中。这样就可以将标准输出和标准错误重定向到同一个文件中了
方法2:
./stream 1>log.txt 2>&1
如上图所示:我们将执行stream文件的结果并将标准输出(1)重定向到log.txt文件中;并且将标准错误重定向到与标准输出相同目标的文件中(即log.txt)
二:缓冲区
stream.cc代码:
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<unistd.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8
9 int main()
10 {
11 close(1);
12 int fd = open("log.txt",O_CREAT | O_WRONLY | O_APPEND,0666);
13 //C语言标准库
14 printf("fd:%d\n",fd);
15 printf("hello world\n");
16 printf("hello world\n");
17 printf("hello world\n");
18
19 //系统调用
20 const char* msg = "hello write\n";
21 write(fd,msg,strlen(msg));
22
23 //close(fd);
24 return 0;
25 }
如上面代码所示:我们先关闭标准输出文件,然后以追加方式创建一个权限位为0666的文件log.txt,由所学可知,fd的值为1,因此将标准输出重定向到log.txt文件中,接着打印log.txt的文件描述符,打印三行hello world语句,接着调用write函数向log.txt文件中写入长度为msg的字符串msg,先不关闭文件log.txt, 因此打印log.txt所有内容都显示
在这里我们将关闭文件log.txt, 这时我们将拿不到fd的值,因此C语言标准库想通过fd系统调用write函数时将会失败,因此C语言缓冲区中的内容将不能刷新到文件内核缓冲区,只有系统调用函数write中的内容在文件内核缓冲区内,所以最后return的时候只能打印hello write这条语句
缓冲区刷新条件:
- 强制刷新(调用fflush)
- 刷新条件满足(行刷新)
- 进程退出(return语句)
缓冲区刷新流程:如果是C/C++等标准库函数的刷新,例如printf函数的刷新,会在C语言标准库中建一个语言层的缓冲区,将需要刷新的语句放在这个缓冲区中,当条件满足时C标准库就会通过fd和系统调用write将语言层的缓冲区的内容拷贝给操作系统,然后再拷贝到文件内的缓冲区。即条件满足就会将语言标准库中的内容刷新到文件内核缓冲区,当最后将文件内核缓冲区内的内容刷新到硬件中(如显示器);而系统调用函数如write函数的内容本身就刷新到文件内核缓冲区
例子1:stream.cc代码:
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<unistd.h>
5
6 int main()
7 {
8 //库函数
9 printf("hello world\n");
10 fprintf(stdout,"hello fprintf\n");
11 const char* s = "hello fwrite\n";
12 fwrite(s,strlen(s),1,stdout);
13
14 //系统调用
15 const char* ss = "hello write\n";
16 write(1,ss,strlen(ss));
17
18
19 return 0;
20 }
如上面代码所示:我们使用printf函数打印hello world语句,fprintf函数向标准输出打印hello fprintf
fwrite函数向标准输出写入长度为s的字符串s,最后调用系统函数write向标准输出打印hello write语句,结果如下图所示:
将标准输出中的内容重定向到log.txt文件中,并且打印log.txt文件中的内容如下如所示:
例子2:stream.cc代码:
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<unistd.h>
5
6 int main()
7 {
8 //库函数
9 printf("hello world\n");
10 fprintf(stdout,"hello fprintf\n");
11 const char* s = "hello fwrite\n";
12 fwrite(s,strlen(s),1,stdout);
13
14 //系统调用
15 const char* ss = "hello write\n";
16 write(1,ss,strlen(ss));
17
18 fork();
19 return 0;
20 }
如上面代码所示:我们在调用write函数后创建一个子进程,执行结果和上面代码一样,如下图所示:
但是我们发现打印log.txt文件中的内容时和上一个例子中的运行结果不一样,除了hello write语句之外的另外三条语句都打印了两次,这是因为hello world、hello fprintf和hello fwrite这三条语句都是存储在C语言标准库的缓冲区中,调用fork进程时会创建子进程因此子进程会从C语言标准库的缓冲区中拿到这三条语句并向C语言标准库的缓冲区中写入(即父进程和子进程都会执行者三条语句),而hello write这条语句没有输出两次的原因是hello write这条语句存储在文件内核缓冲区中执行该语句之后就会在显示器上显示这条语句,最后执行return语句时就会将C语言标准库的缓冲区中的内容向文件内核缓冲区中拷贝并刷新,最后在显示器打印
三:自定义glib.c
mystdio.h代码:
1 #pragma once
2 #include<stdio.h>
3
4 #define MAX 1024
5
6 #define NONE_FLUSH (1 << 0)
7 #define LINE_FLUSH (1 << 1)
8 #define FULL_FLUSH (1 << 2)
9
10 typedef struct IO_FILE
11 {
12 int fileno;
13 int flag;
14 char outbuffer[MAX];
15 int bufferlen;
16 int flush_method;
17 }MyFile;
18
19 MyFile* MyFopen(const char* path,const char* mode);
20 void MyFclose(MyFile*);
21 int MyFwrite(MyFile*,void* str,int len);
22 void MyFFlush(MyFile*);
23
mystdio.c代码:
1 #include"mystdio.h"
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<string.h>
5 #include<fcntl.h>
6 #include<stdlib.h>
7 #include<unistd.h>
8
9 static MyFile* BuyFile(int fd,int flag)
10 {
11 MyFile* f = (MyFile*)malloc(sizeof(MyFile));
12 if(f == NULL)
13 return NULL;
14
15 f->bufferlen = 0;
16 f->fileno = fd;
17 f->flag = flag;
18 f->flush_method = LINE_FLUSH;
19 memset(f->outbuffer,0,sizeof(f->outbuffer));
20 return f;
21 }
22
23 MyFile* MyFopen(const char* path,const char* mode)
24 {
25 int fd = -1;
26 int flag = 0;
27
28 if(strcmp(mode,"w") == 0)
29 {
30 flag = O_CREAT | O_WRONLY | O_TRUNC;
31 fd = open(path,flag,0666);
32 }
33 else if(strcmp(mode,"a") == 0)
34 {
35 flag = O_CREAT | O_WRONLY | O_APPEND;
36 fd = open(path,flag,0666);
37 }
38 else if(strcmp(mode,"r") == 0)
39 {
40 flag = O_RDONLY;
41 fd = open(path,flag);
42 }
43 else
44 {
45 }
46
47 if(fd < 0)
48 return NULL;
49
50 //创建文件
51 return BuyFile(fd,flag);
52 }
53
54 void MyFclose(MyFile* file)
55 {
56 if(file->fileno < 0)
57 return;
58 MyFFlush(file);
59 close(file->fileno);
60 free(file);
61 }
62
63 int MyFwrite(MyFile* file,void* str,int len)
64 {
65 //拷贝
66 memcpy(file->outbuffer + file->bufferlen,str,len);
67 file->bufferlen += len;
68
69 //刷新
70 if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen - 1] == '\n')
71 {
72 MyFFlush(file);
73 }
74 return 0;
75 }
76
77 void MyFFlush(MyFile* file)
78 {
79 int n = write(file->fileno,file->outbuffer,file->bufferlen);
80 file->bufferlen = 0;
81 }