Ⅰ. 标准库函数与系统调用
1.fopen(3)
调用open(2)打开制定的文件,返回一个文件描述符FD(就是一个int类型的编号),分配一个FILE结构体,其中包含改文件的描述符、I/O缓冲区和当前读写位置等信息,返回这个FILE结构体的地址。
- I/O缓冲区:buffer
- 当前读写位置:相对buffer首地址的偏移量
- 用户程序通过得到的是FILE结构体的地址,通过操作这个结构体实现对文件的操作
FILE* 其实就是句柄(或者也可以叫做上下文)。柄就是把手的意思,我们开门关门其实只对门把手进行操作,而不是直接对门操作,即我们对FILE* 进行操作其实就可以实现对文件的操作。
2.fgetc(3)
通过传入的FILE* 参数找到该文件的描述符、I/O缓冲区和当前读写位置,判断能否从I/O缓冲区中读取到下一个字符,如果能读到就直接返回该字符,否则就调用read(2),把文件描述符传进去,让内核读取该文件的数据到I/O缓冲区,然后返回下一个字符。
- tip:第一次去读buffer中肯定是没有东西的,因此肯定会去调用read
#include <stdio.h>
int main() {
FILE *fp = fopen("./hello.txt", "r");
if (!fp) {
perror("open file");
return -1;
}
char c;
while ((c = fgetc(fp)) != EOF) {
printf("%c", c);
}
fclose(fp);
return 0;
}
3.fputc(3)
判断该文件的I/O缓冲区是否有空间在存放一个字符,如果有空间则直接保存在I/O缓冲区中并返回,如果I/O缓冲区已满就调用write(2),让内核把I/O缓冲区的内容写回文件。
#include <stdio.h>
int main() {
FILE *fp = fopen("./hello.txt", "r+");
if (!fp) {
perror("open file");
return -1;
}
fputc('A', fp);
fclose(fp);
return 0;
}
4.fclose(3)
如果I/O缓冲区中还有数据没写回文件,就调用write(2)写回文件,然后调用close(2)关闭文件,释放FILE结构体和I/O缓冲区。
Q:open、read、write、close等系统函数称为无缓冲I/O(Unbuffer I/O)函数,因为它们位于C标准库函数的I/O缓冲区的底层。用户在读写文件时既可以调用C标准I/O库函数,也可以直接调用底层的Unbuffer I/O函数,那么用哪一组函数更好呢?
A:对于大部分不需要实时操作的场景,优先使用C标准I/O库函数,因为可以提高效率,减少用户空间和内核空间的切换。可以将buffer类比成快递驿站,用户寄快递时将快递放到驿站中,等到驿站满了,快递员再去发快递。或者快递员送快递的时候将快递放在驿站中,让用户自己去取,而不是将每个快递挨个送到不同的用户手中。
但是对于需要实时操作的内容(例如网络操作、stderr),最好直接使用系统调用。
Ⅱ. 缓冲(buffer)分类
- 全缓冲:当把buffer填满就触发系统调用
- 行缓冲:当出现换行(\n)时就触发系统调用(当然,buffer满了也是会触发的)
#include <stdio.h>
int main(void) {
fputc('A', stdout); // stdout:标准输出,行缓冲
fputc('\n', stdout);
// 将1kb的缓冲区写满
//for (int i = 0; i < 1024; i++) {
// fputc('B', stdout);
//}
while (1) {
// 写个死循环是为了不让程序结束,否则会自动调用系统调用将缓冲区写入文件
}
return 0;
}
- 无缓冲:有缓冲区,但是就是不缓冲
#include <stdio.h>
int main(void) {
fputc('A', stderr); // stderr:标准错误输出,无缓冲
while (1) {
// 写个死循环是为了不让程序结束,否则会自动调用系统调用将缓冲区写入文件
}
return 0;
}
Ⅲ. 文件描述符FD
每个进程在Linux内核中都有一个task_struck结构体来维护进程相关的信息,称为进程控制块(Process Control Block,PCB)。task_struct中有一个指针指向file_struct结构体,称为文件描述符表,其中每个表项包含一个指向已打开的文件的指针,用户程序不能直接访问内核中的文件描述符表,而只能使用文件描述符表的索引(即0、1、2、3这些数字),这些索引就称为文件描述符(File Descriptor),用int型变量保存。
(句柄思想)
程序启动时会自动打开三个文件:标准输入,标准输出和标准错误输出。在C标准库中分别用FILE*指针stdin、stdout和stderr来表示。这三个文件的描述符分别是0、1、2,保存在相应的FILE结构体中。头文件unistd.h中有如下的宏定义来表示这三个文件描述符:
/* Standard file descriptors */
#define STDIN_FILENO 0 /* Standard input. */
#define STDOUT_FILENO 1 /* Standard output. */
#define STDERR_FILENO 2 /* Standard error output. */
#include <stdio.h>
#include <unistd.h>
int main() {
printf("%d\n", STDIN_FILENO);
printf("%d\n", STDOUT_FILENO);
printf("%d\n", STDERR_FILENO);
return 0;
}
//输出结果:
//0
//1
//2
评论 (0)