【Linux系统编程】文件IO系统——IO标准库函数与系统调用

IO标准库函数与系统调用 📁📑

标准库函数运行在用户空间下,而系统调用运行在内核空间下。
标准库函数与系统调用
IO标准库函数中使用了系统调用。例如,fopen(3) 调用 open(2) 打开指定的文件,返回一个文件描述符(就是一个int类型的编号),分配一个 FILE 结构体,其中包含该文件的描述符(FD)、IO缓冲区和当前读写位置等信息,返回这个 FILE 结构体的地址。Linux中输入命令 man 2 open 查看第二章节系统调用的 open
image-20220206162425495image-20220206161746354

使用命令 man man 查看 man 命令的帮助,man帮助手册的第3部分是库函数的调用,如fopen(3),第2部分是系统调用,即由系统内核提供的函数,如 open(2)

image-20220206161607505
fgetc(3) 通过传入的 FILE * 参数找到该文件的描述符、IO缓冲区和当前读写位置,判断能否从IO缓冲区中读到下一个字符,如果能读到就直接返回该字符,否则调用 read(2),把文件描述符传进去,让内核读取该文件的数据到IO缓冲区,然后返回缓冲区的下一字符。
image-20220206201145070
fputc(3) 判断该文件的IO缓冲区是否有空间再存放一个字符,如果有空间则直接保存在IO缓冲区中并返回,如果IO缓冲区已满就调用 write(2),让内核把IO缓冲区的内容写回文件。

fclose(3) 如果IO缓冲区中还有数据没写回文件,就调用 write(2) 写回文件,然后调用 close(2) 关闭文件,释放 FILE结构体IO缓冲区
标准库函数与系统调用
fgetcfputc 等函数本质上只是用户对传入的 FILE * 参数进行操作,并没有直接对文件进行操作,用户只是调用系统调用,让操作系统内核帮我们操作文件。操作系统才是直接对文件进行操作的角色,用户只是通过系统调用间接操作文件。用户对 FILE * 这个指针参数进行操作便能完成对文件整体的操作,故将 FILE * 参数称为句柄(类似于门把手,可以通过门把手打开整扇门)。

缓冲区 📁📑

同时,IO标准库帮我们维护 buffer 这个IO缓冲区📬,因此IO标准库也叫做缓冲IO函数。buffer 缓冲区的作用是帮助提高文件读写的效率,可以将IO缓冲区类比为收发快递的菜鸟驿站,当用户寄出去的快递达到一定数量的时候,才会统一发货,否则会提高运输次数,浪费资源(当程序要写进文件的字符达到一定数量的时候,才会统一写进文件,最大化利用资源,因为用户态和内核态的转换需要时间)。

当商户给用户发多件货物时,肯定是统一发送至菜鸟驿站比较高效节约运输次数(当程序要从文件中读取字符时,先切换至内核态,拉一批字符出来到IO缓冲区,再切回用户态逐一读取字符,这样只需要切换两次状态)。

缓冲区📬分为三类:全缓冲、行缓冲、无缓冲。全缓冲是指待缓冲区填满的时候,再进行系统调用将缓冲区内容输入到内存或输出到磁盘;行缓冲是指当缓冲区出现换行符或填满时,立即进行系统调用;无缓冲是指当缓冲区用东西进来的时候,马上进行系统调用。

stdout 是行缓冲,遇到换行符或填满才将缓冲区内容输出到标准输出(屏幕)中;stderr 是无缓冲,立即将“错误信息”输出到标准输出中。

行缓冲测试

/*** 测试stdout(行缓冲)***/
/*** test1 ***/
#include <stdio.h>
int main() {
    fputc('A', stdout);
    fputc('B', stdout);
    fputc('C', stdout);
    while (1) {} //加入死循环,使程序一直在运行,避免进程结束将缓冲区内容输出到屏幕
    return 0;
}

/*** test2 ***/
#include <stdio.h>
int main() { 
    fputc('A', stdout);
    fputc('B', stdout);
    fputc('C', stdout);
    fputc('\n', stdout); //加入了换行符
    while (1) {}
    return 0;
}

/*** test3 ***/
#include <stdio.h>
int main() { //向缓冲区中输入1024个字符
    for (int i = 0; i < 1024; i++) {
        fputc('A', stdout);
    }
    while (1) {}
    return 0;
}

/*** test4 ***/
#include <stdio.h>
int main() {
    for (int i = 0; i < 1025; i++) {
        fputc('A', stdout);
    }
    while (1) {}
    return 0;
}

test1结果如下,屏幕没有任何输出。
image-20220207004114109
test2结果如下,stdout 缓冲区中有换行符输入,立即进行系统调用将缓冲区内容(ABC\n)输出到标准输出(屏幕)中。
image-20220207004258473
test3结果如下,向缓冲区中输入1024个A后,仍然没有进行系统调用将缓冲区内容输出到屏幕中,说明缓冲区大小至少为1024个字节(即1KB),因为缓冲区满了之后,需要再输入一个字符才能将缓冲区内容输出到屏幕。
image-20220207004523358
test4结果如下,向缓冲区输入1025个A后,缓冲区内容通过系统调用输出到了屏幕中,是因为当输入第1024个A之后,缓冲区刚好满了,在输入第1025个字符时,发现缓冲区满了,立即通过系统调用将缓冲区内容(1024个A)输出到屏幕中,最后再将第1025个A输入到缓冲区中,即此时缓冲区中仍然有一个A。
image-20220207005639898

无缓冲测试

/*** 测试stderr(无缓冲)***/
/*** test1 ***/
#include <stdio.h>
int main() {
    fputc('A', stderr);
    fputc('B', stderr);
    fputc('C', stderr);
    while (1) {}
    return 0;
}

test1结果如下,stderr 缓冲区中一旦有字符输入,立即进行系统调用将该字符输出到屏幕中。
image-20220206234613871

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值