您的位置:首页 > 其它

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

2016-03-14 10:02 155 查看
今天在学习进程时遇到关于一个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,进行了行缓冲清理,所以只由父进程输出一次;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: