标准的I/O缓冲:全缓冲,行缓冲,无缓冲

本文通过示例解析了标准I/O缓冲机制,并探讨了进程间如何共享缓冲区,特别是父进程缓冲区如何被子进程继承的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在学习进程时遇到关于一个I/O缓冲区的的问题,和大家分享一下,首先举个简单的例子:

#include <stdio.h>
int main()                                                                      
{
        printf("hello,world!");
        _Exit(0);
}

编译成功后却不出现hello,world,这是为什么呢,注意,在代码中printf语句打印hello,world的字符串最后面没带换行符.而且最后调用了_Exit函数,这导致了在终端上显不出hello,world. 首先介绍关于标准I/O的几种缓冲机制: 1.全缓冲:全缓冲指的是系统在填满标准的I/O缓冲区后才进行实际的I/O操作,注意,对于驻留在磁盘上的文件来说通常是由标准的I/O库实施全缓冲. 2.行缓冲: 在这种情况下,标准的IO在输入和输出中遇到换行符执行IO操作;注意,当流涉及终端时,都使用行缓冲. 3.无缓冲:无缓冲指的是标准的IO库不对字符进行缓冲存储,注意,标准的出错流stderr通常是无缓冲的. 在看几个退出函数: 1.exit(),调用exit函数之后,它首先执行一系列的清理处理,包括调用执行各种终止处理程序,关闭所有标准IO流等,然后进入内核; 2._exit().与exit不同的是,它不进行清理直接进入内核此函数由POSIX.1说明,放在unistd.h里面. 3._Exit ()。同样,它也不进行清理工作而直接进入内核。此函数跟exit一样由ISO C说明,放在stdlib.h里面。 所以可以有很多方法修正这段代码: 1.在hello,world后加一个换行符,此时行缓冲遇到换行符\n.执行实际IO操作. 2.调用exit()函数,让它帮我们进行相应的IO操作,也就是把_Exit(),换成exit(); 3.改变标准的输出流的默认缓冲区,这个要用到函数setvbuf(),那就先介绍以下这个函数吧: #include <stdio.h> void setbuf(FILE *stream, char *buf); void setbuffer(FILE *stream, char *buf, size_t size); void setlinebuf(FILE *stream); int setvbuf(FILE *stream, char *buf, int mode, size_t size); 函数说明: 1.对于setbuf()函数,buf指出的缓冲区长度由头文件stdio.h中定义的 宏BUFSIZE的值决定,缺省值为512字节。当选定buf为空时,setbuf函数将使的文件I/O不带缓冲。 2.setvbuf函数,则由 malloc函数来分配缓冲区。参数size指明了缓冲区的长度(必须大于0),而参数type则表示了缓冲的类型,其值可以取如下值: 3.在打开文件流后,读取内容之前,调用setbuffer()可用来设置文件流的缓冲区。参数stream为指定的文件流,参数buf指向自定的缓冲区起始地址,参数size为缓冲区大小。 4. setlinebuf()用来设置文件流以换行为依据的缓冲IO,即行缓冲。 type 值 含义 _IOFBF 文件全部缓冲,即缓冲区装满后,才能对文件读写 _IOLBF 文件行缓冲,即缓冲区接收到一个换行符时,才能对文件读写 _IONBF 文件不缓冲,此时忽略buf,size的值,直接读写文件,不再经过文件缓冲区缓冲 所以我们可以通过调用setvbuf函数,把标准的输出流默认的行缓冲变成无缓冲 setvbuf(stdout, NULL, _IONBF, 0); 现在说一说我今天遇到的具体的问题吧,大家都知道,fork一个子进程,子进程会复制父进程的许多资源(这个自己查),最重要的是它会复制父进程的缓冲区, 举个例子吧,

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
extern char **environ;
int main()
{
        pid_t pid;
        int stat_val;
        char *argv[]={"ls","-al","/",NULL};
        //printf("exec函数族实例:\n");//去掉注释;
        printf("exec函数实例:");
        pid=fork();
        switch(pid) {
        case -1:
                perror("进程创建失败!\n");
                exit(1);
        case 0:
                printf("子进程正在运行!\n");
                printf("我的ID=%d,父亲的ID=%d\n",getpid(),getppid());   
                //execve("/bin/ls",argv,environ);
                //execv("/bin/ls",argv);
                //execvp("ls",argv);
                //execl("/bin/ls","ls","-al","/",NULL);
//              execlp("ls","ls","-al","/",NULL);
                execvp("ls",argv);
                printf("如果exec函数调用成功,这一句不会被执行!\n");
                exit(0);
        default:
                
                printf("父进程正在运行!\n");
                break;                                                                                                                          
        }       
        wait(&stat_val);

        return 0;
}

编译运行:

yang@liu:~/Linux C$ gcc processimage1.c 
yang@liu:~/Linux C$ ./a.out
exec函数实例:父进程正在运行!
exec函数实例:子进程正在运行!
我的ID=6032,父亲的ID=6031
总用量 105
drwxr-xr-x  24 root root  4096  7月 13 12:11 .
drwxr-xr-x  24 root root  4096  7月 13 12:11 ..
drwxr-xr-x   2 root root  4096  5月 31 11:42 bin

然后把注释写//去掉注释那一行注释去掉,注释掉下面那一行: 编译运行:

exec函数族实例:
父进程正在运行!
子进程正在运行!
我的ID=7354,父亲的ID=7353
总用量 105
drwxr-xr-x  24 root root  4096  7月 13 12:11 .
drwxr-xr-x  24 root root  4096  7月 13 12:11 ..
drwxr-xr-x   2 root root  4096  5月 31 11:42 bin
drwxr-xr-x   4 root root  1024  5月 31 14:20 boot

第一次没有加进行加\n,所以父进程运行后"exec函数族实例:"就存在于父进程的缓冲区中,当fork后,子进程也复制了父进程的缓冲区,所以输出了两次"exec函数族实例", 第二次,加了\n,进行了行缓冲清理,所以只由父进程输出一次;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值