Linux系统编程-标准I/O与系统I/O的比较
目录
一. 标准I/O与系统I/O
1.1 标准I/O与系统I/O的区别
1、标准I/O
2、系统I/O
3、总结
二. 标准I/O
2.1 标准I/O的缓冲机制
2.1.1 标准I/O的缓冲模式:
缓冲区:
行缓冲模式:
全缓冲模式:
无缓冲模式:
2.1.2 fflush()函数
2.1.3 标准I/O合并系统调用
命令:strace
一. 标准I/O与系统I/O
1.1 标准I/O与系统I/O的区别
| 文件I/O | 标准I/O |
| 面向文件描述符fd | 面向文件流指针FILE* |
| 由linux内核提供,属于系统调用,跨平台可移植性差 | C标准库提供,跨平台可移植性好 |
| 无缓冲区,实时性高,可以用在对硬件的控制上 | 有缓冲区,实现数据缓冲,避免频繁的在用户态和内核态之间切换,提高了效率 |
| 可以操作普通文件,也可以操作硬件相关的设备类文件,管道,套接字等 | 主要用在对普通文件(-)的读写上,文本文件用的多 |
1、标准I/O
标准I/O具有缓存机制,可以合并系统调用
2、系统I/O
系统I/O每调用一次都会完成从user态到kernel态的切换,实时性高
3、总结
标准I/O与系统I/O的区别在与响应速度(实时性)与吞吐量:
标准I/O的吞吐量大,效率高
系统I/O的实时性强
二. 标准I/O
2.1 标准I/O的缓冲机制
首先,来看一段程序:
这里直观的感受是打印出第一个printf的内容,然后进入死循环,但结果不是这样的:
int main() { printf("before while(1)"); while(1); printf("after while(1)"); return 0; }结果:直接进入了死循环,没有打印,这是因为终端是行缓冲模式
2.1.1 标准I/O的缓冲模式:
缓存区的作用:大多情况下是好事---合并系统调用
| 缓存模式 | 解释 | 设备 |
| 行缓冲模式 | 遇到\n刷新 程序退出时刷新 缓冲区满了刷新 强制刷新 | 缓冲区大小1024字节(1k) 主要用于人机交互的界面 标准输出(终端) |
| 全缓冲模式 | 缓冲区满了刷新 文件关闭刷新 程序退出刷新 强制刷新 | 缓冲区大小4096字节(4k) 主要用于对文件的读写(普通文件) 文件操作 默认(除了终端) |
| 无缓冲模式 | 需要立即输出的内容 | strderr |
缓冲区:
高速设备和低速设备进行交互时,为了匹配低速设备的速率,需要在高速设备和低速设备之间增加一个缓冲区,用于数据缓冲
行缓冲模式:
换行刷新:
int main() { printf("hello world\n");//换行刷新 while(1); return 0; }结果:
强制刷新:
int main() { printf("hello world");//强制刷新 fflush(stdout); while(1); return 0; }结果:
满了刷新:
int main() { while(1){ printf("hello world"); usleep(100000); } return 0; }结果:可知行缓冲的大小是1024字节
程序退出刷新:
int main() { printf("hello world"); return 0; }结果:
全缓冲模式:
满了刷新:
int main() { FILE *fp = fopen("1.txt", "w"); if(fp == NULL){ perror("fopen():"); return -1; } while(1){ fputs("hello world", fp); usleep(10000); } return 0; }结果:从这里也可以看出,全缓冲的缓冲区大小是4096字节,是以4096的整数倍增加的
无缓冲模式:
stderr:
int main() { fprintf(stderr,"hello world"); return 0; }结果:
这里介绍一个强制刷新缓冲区的函数:fflush()
2.1.2 fflush()函数
作用:强制刷新指定流的缓冲区
1、对于输出流而言,调用 fflush() 会强制将给定输出或更新流的用户空间缓冲区中的所
有数据进行写入操作,具体操作是通过该流的底层写入函数来实现的。
2、对于与可寻址文件(例如磁盘文件,但不包括管道或终端)相关的输入流,调用
fflush() 会清除从底层文件中读取但尚未被应用程序使用的任何缓冲数据。
3、该流的开启状态未受任何影响。
4、如果流参数为 NULL,则 fflush() 函数会刷新所有打开的输出流。
返回值:成功返回0失败返回-1并且设置error
由于终端是行缓存模式,则上面那段程序可以做以下修改使其打印出内容:
int main() { printf("before while(1)\n"); while(1); printf("after while(1)\n"); return 0; }结果:
也可以使用fflush来强制刷新缓冲区:
int main() { printf("before while(1)"); fflush(stdout); while(1); printf("after while(1)"); fflush(NULL); return 0; }结果:
2.1.3 标准I/O合并系统调用
这里可以通过一个程序来说明标准I/O缓冲区合并系统调用的作用:
代码:可能打印出ababab
int main() { putchar('a'); write(1, "b", 1); putchar('a'); write(1, "b", 1); putchar('a'); write(1, "b", 1); return 0; }结果:
命令:strace
这里介绍一个命令:strace + 可执行文件:用来看一个可执行文件的系统调用是如何发生的。使用此命令来查看这段程序使用的系统调用:
可以看到三次putchar被合并为了一次write系统调用,这种合并系统调用大幅降低内核态 / 用户态切换耗时
