首页 > 请教一个非阻塞I/O的基础问题

请教一个非阻塞I/O的基础问题

当从标准输入读取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 然后看看可以写入多少。

【热门文章】
【热门文章】