当从标准输入读取500000字节,并试图将其依非阻塞I/O的方式分别写入文件和终端时。
同样是非阻塞I/O为什么输出到指定文件的时候是1次 write
调用就能完成,而输出到终端时,则分了1000多次次呢?
难道是因为终端的缓冲区不能一次性容纳 500000 byte
?我发现最多的时候也才只能一次写入 7154 byte
,也就是说在我当前环境下终端程序一次能接受到数据量也就是7154
左右了?是吗?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
char buf[500000];
void set_fl(int fd, int flags);
void clr_fl(int fd, int flags);
int main(void)
{
int ntowrite, nwrite;
char *ptr;
ntowrite = read(STDIN_FILENO, buf, sizeof(buf));
fprintf(stderr, "read %d bytes\n", ntowrite);
set_fl(STDOUT_FILENO, O_NONBLOCK); /* set nonblocking */
ptr = buf;
while (ntowrite > 0)
{
errno = 0;
nwrite = write(STDOUT_FILENO, ptr, ntowrite);
fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);
if (nwrite > 0)
{
ptr += nwrite;
ntowrite -= nwrite;
}
}
clr_fl(STDOUT_FILENO, O_NONBLOCK); /* clear nonblocking */
exit(0);
}
/**
* fd 需要修改的文件描述符
* flags 需要清除的文件状态标志
*/
void set_fl(int fd, int flags)
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)
{
perror("fcntl F_GETFL error: ");
}
val |= flags; /* turn no flags */
if (fcntl(fd, F_SETFL, val) < 0)
{
perror("fcntl F_SETFL error: ");
}
}
/**
* fd 需要修改的文件描述符
* flags 需要开启的文件状态标志
*/
void clr_fl(int fd, int flags)
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)
{
perror("fcntl F_GETFL error: ");
}
val &= ~flags; /* turn flags off */
if (fcntl(fd, F_SETFL, val) < 0)
{
perror("fcntl F_SETFL error: ");
}
}
上面的文件我命名为nonblock_io.c
[zhoumengkang@localhost unix]$ gcc nonblock_io.c
[zhoumengkang@localhost unix]$ ./a.out < /etc/services 2>stderr.out
[zhoumengkang@localhost unix]$ awk '{a[$3]++}END{for(i in a){print i,a[i]}}' stderr.out |sort -k1nr|head -5
7154, 1
7121, 1
7094, 1
7082, 2
7075, 1
linux 所有的文件写入,其实一开始都是写入到缓冲区,然后选适当的时候写入磁盘或是设备(linux看来设备就是文件)。至于默认缓冲区多大,我就忘记了,文件的缓冲区500K还是有的,控制台好像只有1K吧。
其实最关键的不是这个,而是从缓冲区写入磁盘的速度远远大于输出的屏幕上。UI总是很浪费时间。
缓冲区在内存,一个小的程序的话,循环写缓冲区的速度可想而知,写入设备就很满了。
你代码这样写本身就是知道不一定能够全部写入,但不要等待写完,而是写入多少就是多少立即返回然后处理其它事情,系统后台写入设备,程序处理完其他事情,再写入。
多亏你write
后加了个 fprintf
否则一次写入的数据会更少,因为在此期间操作系统只把缓冲区中的这么多数据写入到了设备,缓冲区就空出这么多。要测缓冲区多大,你可以 write
之后加一个足够的长的 sleep
然后看看可以写入多少。