<Linux>基础IO_输出重定向&&缓冲区

目录

一、简单谈谈fd

二、输出重定向

三、理解一切皆文件

四、缓冲区

什么是缓冲区?

为什么要有缓冲区?

 缓冲区在哪?

缓冲区的额外认知

一个难以解释的现象!

 自己设计的FILE文件结构体


一、简单谈谈fd

文件类别:

  • 磁盘文件:没有被打开的文件
  • 内存文件:被进程在内存打开的文件

文件描述符(本质):就是数组的下标!!

<Linux>基础IO_输出重定向&&缓冲区_第1张图片

我们在进行C文件接口调用的时候,在OS内部是做了很多事情的!!

调用fopen时:首先调用fopen中封装的open系统接口,得到fd文件描述符,在实例化FILE结构体,最后再返回FILE*指针;

调用fwrite时:首先从传入的FILE*中找到fd,再将fd传入write,然后执行操作系统内部的write方法,找到struct_task结构体,找到指向文件数据结构的指针,找到struct_file,找到fd_array[]管理文件的数组,再利用fd找到需要改变的文件,执行文件操作; 

我们在进行C文件接口调用的时候,在OS内部是做了很多事情的!!

二、输出重定向

输出重定向表象:将原本应该打印在屏幕上的内容输出到文件中;

重定向本质:OS内部,更改了对应的内容指向;

<Linux>基础IO_输出重定向&&缓冲区_第2张图片

 

C程序在运行时,是默认打开了三个文件流的:

stdin:输入流对应的fd 0

stdout:输出流对应的fd 1

stderr :错误流对应的fd 2

<Linux>基础IO_输出重定向&&缓冲区_第3张图片

 

为什么我们的程序会优先打印到显示器上,是因为在fd_array[1]中默认放的是显示器的文件,如果我们要实现重定向,只需要将fd_array[1]中的文件指针变成我们想要指向的文件即可;

int dup2(int oldfd, int newfd);//专门用于输出重定向的接口,将oldfd的内容赋值给newfd

         if (argc != 2) 
 10     {
 11         return 2;
 12     }
 13 
 14     int fd = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
 15     if (fd < 0) 
 16     {
 17         perror("open");
 18         return 1;
 19     }
 20     
 21     dup2(fd, 1);//将fd_array[1]中的文件指针用fd_array[fd]来代替,效果就是原本在fd中打印的数据,在文件1中打印;
 22     fprintf(stdout, "%s\n", argv[1]); //stdout -> 1 -> 显示器
 23 
 24     close(fd);

三、理解一切皆文件

一切皆文件的设计哲学是体现在操作系统软件设计层面的!

Linux是C语言写的,如何使用C语言实现面向对象,甚至是多态?

通过函数指针的方式,就可以在struct结构体中,引入成员方法的概念; 

<Linux>基础IO_输出重定向&&缓冲区_第4张图片

同时我们不同的外设(硬件)的IO(输入输出)方法应该是不一样的,Linux做的就是创建了应该struct_file结构体,在这个结构体中有两个指针,分别指向我们需要使用的外设的read和write方法,而每一个外设自己都会提供一个read和write方法函数(驱动开发),这样我们在上层看来就屏蔽了硬件之间的差别,就所有的外设包括文件都用struct_file的方式统一起来,都是通过文件的方式来进行访问

<Linux>基础IO_输出重定向&&缓冲区_第5张图片

四、缓冲区

  • 什么是缓冲区?
  • 为什么要有缓冲区?
  • 缓冲区在哪?

什么是缓冲区?

就是一段内存空间(这个空间是由语言提供的);

为什么要有缓冲区?

写透模式:我们每想进行一次写入就访问一次硬件;

写回模式:创建一个缓冲区的概念,每当到了缓冲区的刷新条件或强制刷新时,才去访问硬件;

写回模式是更好的,我们访问硬件的时间是比较长的,而缓冲区是创建在内存的,我们可以直接对内存进行操作,内存的访问速度相对于硬件而言,是大了几个数量级的!!

<Linux>基础IO_输出重定向&&缓冲区_第6张图片

 缓冲区在哪?

一般由语言提供,也有内核级的缓冲区;

缓冲区的额外认知

一般而言,行缓冲的设备文件 -- 显示器

全缓冲的设备文件 -- 磁盘文件

所有的设备都倾向于全缓冲! -- 缓冲区满了,才刷新 --> 可以使IO操作减少 -> 减少访问外设的次数 -> 提高效率(和外部IO的时候,数据量的大小不主要的,和外设设备IO过程是最消耗时间的)

其他的刷新策略,结合具体情况做的妥协!

一个难以解释的现象!

看以下代码

//C语言提供的
printf("hello printf\n");
fprintf(stdout, "hello fprintf\n");
const char *s = "hello fputs\n";
fputs(s.stdout);

//OS提供的
const char *ss = "hello write\n";
write(1, ss, strlen(ss));
//注意我们调用fork时,上面的代码是执行完了的;
fork();

我们运行一下; 

<Linux>基础IO_输出重定向&&缓冲区_第7张图片

如果只是运行则显示器输出了四条语句,但是如果输出重定向就打印了七条语句在log.txt中,并且规律是系统接口只打印了一次,C接口打印了两次;

  • 如果向显示器打印,刷新策略是行刷新,那么最后执行fork()时,一定是函数执行完了,数据已经被刷新了!fork无意义了;
  • 如果对应的程序进行重定向,要向磁盘文件打印 -- 隐性的刷新策略就变成了全缓冲! -- \n就没有了意义;

<Linux>基础IO_输出重定向&&缓冲区_第8张图片

上面的代码没有影响系统接口!!说明如果有缓冲区,这个缓冲区一定不是OS提供维护的,如果是那么上面的代码应该是一样的结果;

那么缓冲区就只能是语言提供的了,C语言在FILE结构体不只是封装了fd,还有缓冲区

<Linux>基础IO_输出重定向&&缓冲区_第9张图片

 自己设计的FILE文件结构体

  1 #include                                                                                                                                                                       
  2 #include               
  3 #include 
  4 #include 
  5 #include 
  6 #include 
  7 #include 
  8 #include      
  9  
 10 #define NUM 1024//定义缓冲区大小
 11                   
 12 typedef struct MyFILE
 13 {
 14     int fd;//文件描述符
 15     char buffer[NUM];//缓冲区
 16     int end;//缓冲区结尾                            
 17 }MyFILE;           
 18      
 19 MyFILE* fopen_(const char* path, const char* op)
 20 {                
 21     assert(path);
 22     assert(op);
 23                                        
 24     MyFILE *ps = NULL;
 25              
 26     if (strcmp(op, "w") == 0)
 27     {        
 28         int fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
 29         if (fd >= 0)
 30         {           
 31             ps = (MyFILE*)malloc(sizeof(MyFILE));
 32             memset(ps, 0, sizeof(MyFILE));
 33             ps->fd = fd;
 34             ps->end = 0;
 35         }
 36      }
 37     else if (strcmp(op, "w+") == 0)
 38     {
 39 
 40     }                                                                                                                                                                                   
 41     else if (strcmp(op, "a") == 0) 
 42     {              
 43                                  
 44     }
 45     else if (strcmp(op, "a+") == 0)
 46     {
 47 
 48     }
 49     else if (strcmp(op, "r") == 0)
 50     {
 51 
 52     }
 53     else if (strcmp(op, "r+") == 0)
 54     {
 55 
 56     }
 57     else             
 58     {             
 59                      
 60     }              
 61                         
 62     return ps;       
 63 }               
 64                   
 65                      
 66 void fflush_(MyFILE* ps)                        
 67 {          
 68     dup2(1, ps->fd); 
 69     write(ps->fd, ps->buffer, ps->end); 
 70     ps->end = 0;
 71 
 72 }
 73 
 74 void fputs_(const char* str, MyFILE* ps)                                                                                                                                                
 75 {
 76     strcpy(ps->buffer + ps->end, str);
 77     ps->end += strlen(str);
 78     if (str[ps->end - 1] == '\n') 
 79     {                              
 80         fflush_(ps);
 81     }
 82 }
 83 
 84 void fclose_(MyFILE* ps)
 85 {                                
 86     fflush_(ps);
 87     close(ps->fd);
 88 }
 89 
 90 int main()
 91 {                                 
 92     MyFILE* ps = fopen_("log.txt", "w");
 93     if (ps == NULL)
 94     {
 95         printf("fopen error\n");   
 96         return 1;
 97     }
 98 
 99     const char *str = "hello MyFILE\n";
100     fputs_(str, ps);
101     sleep(1);        
102     fputs_("你好世界", ps);
103     sleep(1);           
104     fputs_(" 你好世界", ps);
105     sleep(1);
106     fputs_(str, ps);
107                                        
108     fclose_(ps);                                                                                                                                                                        
109                      
110     return 0;              
111 }           

你可能感兴趣的:(Linux,数据结构)