目录
一、简单谈谈fd
二、输出重定向
三、理解一切皆文件
四、缓冲区
什么是缓冲区?
为什么要有缓冲区?
缓冲区在哪?
缓冲区的额外认知
一个难以解释的现象!
自己设计的FILE文件结构体
文件类别:
文件描述符(本质):就是数组的下标!!
我们在进行C文件接口调用的时候,在OS内部是做了很多事情的!!
调用fopen时:首先调用fopen中封装的open系统接口,得到fd文件描述符,在实例化FILE结构体,最后再返回FILE*指针;
调用fwrite时:首先从传入的FILE*中找到fd,再将fd传入write,然后执行操作系统内部的write方法,找到struct_task结构体,找到指向文件数据结构的指针,找到struct_file,找到fd_array[]管理文件的数组,再利用fd找到需要改变的文件,执行文件操作;
我们在进行C文件接口调用的时候,在OS内部是做了很多事情的!!
输出重定向表象:将原本应该打印在屏幕上的内容输出到文件中;
重定向本质:OS内部,更改了对应的内容指向;
C程序在运行时,是默认打开了三个文件流的:
stdin:输入流对应的fd 0
stdout:输出流对应的fd 1
stderr :错误流对应的fd 2
为什么我们的程序会优先打印到显示器上,是因为在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结构体中,引入成员方法的概念;
同时我们不同的外设(硬件)的IO(输入输出)方法应该是不一样的,Linux做的就是创建了应该struct_file结构体,在这个结构体中有两个指针,分别指向我们需要使用的外设的read和write方法,而每一个外设自己都会提供一个read和write方法函数(驱动开发),这样我们在上层看来就屏蔽了硬件之间的差别,就所有的外设包括文件都用struct_file的方式统一起来,都是通过文件的方式来进行访问;
就是一段内存空间(这个空间是由语言提供的);
写透模式:我们每想进行一次写入就访问一次硬件;
写回模式:创建一个缓冲区的概念,每当到了缓冲区的刷新条件或强制刷新时,才去访问硬件;
写回模式是更好的,我们访问硬件的时间是比较长的,而缓冲区是创建在内存的,我们可以直接对内存进行操作,内存的访问速度相对于硬件而言,是大了几个数量级的!!
一般由语言提供,也有内核级的缓冲区;
一般而言,行缓冲的设备文件 -- 显示器
全缓冲的设备文件 -- 磁盘文件
所有的设备都倾向于全缓冲! -- 缓冲区满了,才刷新 --> 可以使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();
我们运行一下;
如果只是运行则显示器输出了四条语句,但是如果输出重定向就打印了七条语句在log.txt中,并且规律是系统接口只打印了一次,C接口打印了两次;
上面的代码没有影响系统接口!!说明如果有缓冲区,这个缓冲区一定不是OS提供维护的,如果是那么上面的代码应该是一样的结果;
那么缓冲区就只能是语言提供的了,C语言在FILE结构体不只是封装了fd,还有缓冲区;
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 }