2013-03-30 wcdj
低速系统调用是可能会使进程永远阻塞的一类系统调用,而非阻塞I/O使调用open, read和write这样的I/O操作,并使这些操作不会永远阻塞,如果这种操作不能完成,则调用立即出错返回,表示该操作若继续执行将阻塞。
对于一个给定的描述符有2种方法对其指定为非阻塞I/O:
(1) 如果调用open获得描述符,则可指定O_NONBLOCK标志;
(2) 对于已经打开的一个描述符,则可调用fcntl,由该函数打开O_NONBLOCK文件状态标志;
测试用例:
从STDIN_FILENO标准输入读500K的buffer,设置STDOUT_FILENO为非阻塞I/O,然后将buffer写到标准输出上。
测试1:输出到普通文件
nonblock_write < buffer.txt > out.txt
测试2:输出到终端,并将标准错误信息输出到stderr.out文件
nonblock_write < buffer.txt 2>stderr.out
此时,write有时返回小于500k的一个数字,有时返回出错,errno显示系统调用返回的错误,EAGAIN在Linux上是11,在Max OS X上是35。通过统计,程序发出了数千个write调用,但是只有几百个是真正输出数据的,其余的则出错返回,这种形式的循环称为“轮询”,在多用户系统上它浪费了CPU时间。(对此问题的解决方法是:使用I/O多路转接select和poll函数,以节省CPU的时间)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
// test buffer is 500M
char buf[500000000];
// flags are file status flags to turn on
void
set_fl(int fd, int flags)
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)
{
printf("fcntl F_GETFL error\n");
exit(1);
}
// turn on flags
val |= flags;
if (fcntl(fd, F_SETFL, val) < 0)
{
printf("fcntl F_SETFL error\n");
exit(1);
}
}
// flags are file status flags to turn off
void
clr_fl(int fd, int flags)
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)
{
printf("fcntl F_GETFL error\n");
exit(1);
}
// turn off flags
val &= ~flags;
if (fcntl(fd, F_SETFL, val) < 0)
{
printf("fcntl F_SETFL error\n");
exit(1);
}
}
int
main(int argv, char **argc)
{
int ntowrite, nwrite;
char *ptr;
ntowrite = read(STDIN_FILENO, buf, sizeof(buf));
fprintf(stderr, "read %d bytes\n", ntowrite);
// set nonblocking
set_fl(STDOUT_FILENO, O_NONBLOCK);
// time beg
time_t beg = time(NULL);
ptr = buf;
while (ntowrite > 0)
{
errno = 0;
// it will not block here!
nwrite = write(STDOUT_FILENO, ptr, ntowrite);
fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);
if (nwrite > 0)
{
ptr += nwrite;
ntowrite -= nwrite;
}
}
// time end
time_t end = time(NULL);
fprintf(stderr, "used time: %lds\n", end - beg);
// clear nonblocking
clr_fl(STDOUT_FILENO, O_NONBLOCK);
return 0;
}
