用户级缓冲区与内核级缓冲区

用户级缓冲区与内核级缓冲区

在 C 语言中,缓冲区(Buffer)是一个用于临时存储数据的区域,通常用于提高输入和输出的性能。标准输入、标准输出以及文件 I/O 都使用缓冲区来管理数据流。

1. 用户级缓冲区(C语言缓冲区)

标准输出缓冲区: C 标准库通过使用标准输出缓冲区来提高 printf 和 fprintf 函数的输出效率。
默认情况下,标准输出缓冲区在以下几种情况下会被刷新:

当程序正常终止时。
当 fflush 函数被调用。
当缓冲区满时。
当遇到换行符 \n 时

#include <stdio.h>

int main() {
    printf("Hello, ");
    printf("world!\n");
    return 0;
}

输出:
在这里插入图片描述

2. 文件 I/O 缓冲区

文件 I/O 函数也使用缓冲区来提高性能。在使用 fopen 打开文件时,C 标准库会为该文件分配一个缓冲区。缓冲区的刷新发生在文件关闭时或者调用 fflush 函数。

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "w");
    if (file != NULL) {
        fprintf(file, "This is a line in the file.\n");
        fclose(file);  // 刷新缓冲区并关闭文件
    }
    return 0;
}

3. 手动刷新缓冲区

如果需要在某个时刻立即刷新缓冲区,可以使用 fflush 函数。这对于确保输出及时显示非常有用。

#include <stdio.h>

int main() {
    printf("This message ");
    fflush(stdout);  // 刷新标准输出缓冲区
    printf("will be immediately visible.\n");
    return 0;
}

输出:
在这里插入图片描述

4. 内核级缓冲区

无缓冲输出(write 函数): 与 printf 和 fprintf 不同,write 函数是一个系统调用,直接将数据写入文件描述符,而不使用 C 标准库的缓冲机制。因此,write 可以用于实现无缓冲输出,数据会立即写入目标文件。

#include <unistd.h>

int main() {  
      const char* s = " \n";
      printf("This was written late");
      const char *message = "This is a direct write.";                                                                                                                                    
      write(1, message, strlen(message));  // 1 表示标准输出,无缓冲写入
      write(1,s,strlen(s));
     return 0;
  }

输出:
在这里插入图片描述

5. 缓冲区的复制(fork 函数)

在使用 fork 函数创建子进程时,子进程会继承父进程的文件描述符和缓冲区。这可能导致输出的重复,因为缓冲区的内容被复制到子进程。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    printf("Hello, ");
    fork();
    printf("world\n");
    return 0;
}

输出:
在这里插入图片描述

6. 例子

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main() {
    // 定义三个字符串常量
    const char* s1 = "hello char\n";
    const char* s2 = "hello fwrite\n";
    const char* s3 = "hello write\n";

    // 使用 printf 打印字符串 s1
    printf("%s", s1);

    // 使用 fprintf 将字符串输出到标准输出流
    fprintf(stdout, "hello fprintf\n");

    // 使用 fwrite 将字符串 s2 写入到标准输出流
    fwrite(s2, strlen(s2), 1, stdout);

    // 使用 write 系统调用将字符串 s3 写入到文件描述符为 1 的文件,即标准输出
    write(1, s3, strlen(s3));

    fork();

    return 0;
}

将程序输出到文件里:
在这里插入图片描述

在程序中,涉及到标准输出的两个函数 printf 和 fprintf 使用了用户级缓冲区,而使用系统调用 write 则涉及到了内核级缓冲区。这涉及到缓冲区的刷新和进程复制的问题,导致输出在两个进程中交错和重复。

printf和fprintf 都使用了用户级缓冲区,这表示它们将数据存储在用户空间的缓冲区中,而不是直接发送到内核空间。

fwrite也是使用用户级缓冲区的函数,但由于是二进制写入,不受行缓冲的影响,数据存储在用户空间的缓冲区中,不会立即刷新。

write 是系统调用,直接操作内核级缓冲区,将数据写入内核空间的缓冲区。

fork 在这里导致了进程的复制,父进程和子进程都继续执行接下来的代码,包括缓冲区。

由于进程复制,两个进程共享相同的用户级缓冲区,但它们各自有独立的内核级缓冲区。因此,输出在两个进程中交错和重复,其中用户级缓冲区的内容被复制到了子进程,而内核级缓冲区是独立的。这就解释了 log.txt 中看到的输出结果。要避免这种情况,可以在 fork 之前使用 fflush(stdout); 来刷新用户级缓冲区,确保两个进程的输出不会冲突。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值