一、
[lingyun@localhost read_1]$ cat read.c
/********************************************************************************** Copyright: (C) 2013 fulinux<fulinux@sina.com>
* All rights reserved.
*
* Filename: read.c
* Description: This file
*
* Version: 1.0.0(07/29/2013~)
* Author: fulinux <fulinux@sina.com>
* ChangeLog: 1, Release initial version on "07/29/2013 09:37:59 AM"
*
********************************************************************************/
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
char buf[10];
int n;
n = read(STDIN_FILENO, buf, 10);
if(n < 0)
{
perror("read STDIN_FILENO");
exit(1);
}
write(STDOUT_FILENO, buf, n);
return 0;
}
[lingyun@localhost read_1]$ gcc read.c
[lingyun@localhost read_1]$ ./a.out
hello
hello
[lingyun@localhost read_1]$ ./a.out
hello world
hello worl[lingyun@localhost read_1]$ d
-bash: d: command not found
[lingyun@localhost read_1]$
第一次执行a.out的结果很正常,而第二次执行的过程有点特殊,现在分析一下:
Shell进程创建a.out进程,a.out进程开始执行,而Shell进程睡眠等待a.out进程退出。
a.out调用read时睡眠等待,直到终端设备输入了换行符才从read返回,read只读走10个字符,剩下的字符仍然保存在内核的终端设备输入缓冲区中。
a.out进程打印并退出,这时Shell进程恢复运行,Shell继续从终端读取用户输入的命令,于是读走了终端设备输入缓冲区中剩下的字符d和换行符,把它当成一条命令解释执行,结果发现执行不了,没有d这个命令。
二、
如果在open一个设备时指定了O_NONBLOCK标志,read/write就不会阻塞。以read为例,如果设备暂时没有数据可读就返回-1,同时置errno为EWOULDBLOCK(或者EAGAIN,这两个宏定义的值相同),表示本来应该阻塞在这里(would block,虚拟语气),事实上并没有阻塞而是直接返回错误,调用者应该试着再读一次(again)。这种行为方式称为轮询(Poll),调用者只是查询一下,而不是阻塞在这里死等,这样可以同时监视多个设备:
while(1) {
非阻塞read(设备1);
if(设备1有数据到达)
处理数据;
非阻塞read(设备2);
if(设备2有数据到达)
处理数据;
...
}
如果
read(设备1)
是阻塞的,那么只要设备1没有数据到达就会一直阻塞在设备1的
read
调用上,即使设备2有数据到达也不能处理,使用非阻塞I/O就可以避免设备2得不到及时处理。
非阻塞I/O有一个缺点,如果所有设备都一直没有数据到达,调用者需要反复查询做无用功,如果阻塞在那里,操作系统可以调度别的进程执行,就不会做无用功了。在使用非阻塞I/O时,通常不会在一个while循环中一直不停地查询(这称为Tight Loop),而是每延迟等待一会儿来查询一下,以免做太多无用功,在延迟等待的时候可以调度其它进程执行。
while(1) {
非阻塞read(设备1);
if(设备1有数据到达) 处理数据;
非阻塞read(设备2);
if(设备2有数据到达) 处理数据;
... sleep(n);
}
这样做的问题是,设备1有数据到达时可能不能及时处理,最长需延迟n秒才能处理,而且反复查询还是做了很多无用功。以后要学习的select(2)函数可以阻塞地同时监视多个设备,还可以设定阻塞等待的超时时间,从而圆满地解决了这个问题。
以下是一个非阻塞I/O的例子。目前我们学过的可能引起阻塞的设备只有终端,所以我们用终端来做这个实验。程序开始执行时在0、1、2文件描述符上自动打开的文件就是终端,但是没有O_NONBLOCK标志。所以就像例1 “阻塞读终端”一样,读标准输入是阻塞的。我们可以重新打开一遍设备文件/dev/tty(表示当前终端),在打开时指定
O_NONBLOCK标志。
例 2非阻塞读终端
[lingyun@localhost read_2]$ cat read.c
/*********************************************************************************
* Copyright: (C) 2013 fulinux<fulinux@sina.com>
* All rights reserved.
*
* Filename: read.c
* Description: This file
*
* Version: 1.0.0(07/29/2013~)
* Author: fulinux <fulinux@sina.com>
* ChangeLog: 1, Release initial version on "07/29/2013 09:52:44 AM"
*
********************************************************************************/
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MSG_TRY "try again\n"
int main(void)
{
char buf[10];
int fd, n;
fd = open("/dev/tty", O_RDONLY|O_NONBLOCK);
if(fd < 0)
{
perror("open /dev/tty");
exit(1);
}
tryagain:
n = read(fd, buf, 10);
if(n < 0)
{
if(errno == EAGAIN)
{
sleep(1);
write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
goto tryagain;
}
perror("read /dev/tty");
exit(1);
}
write(STDOUT_FILENO, buf, n);
close(fd);
return 0;
}
[lingyun@localhost read_2]$ gcc read.c
[lingyun@localhost read_2]$ ./a.out
try again
htry again
etry again
ltry again
ltry again
otry again
try again
hello
[lingyun@localhost read_2]$
三、
非阻塞读终端和等待超时例子:
[lingyun@localhost read_3]$ cat read.c
/*********************************************************************************
* Copyright: (C) 2013 fulinux<fulinux@sina.com>
* All rights reserved.
*
* Filename: read.c
* Description: This file
*
* Version: 1.0.0(07/29/2013~)
* Author: fulinux <fulinux@sina.com>
* ChangeLog: 1, Release initial version on "07/29/2013 10:06:25 AM"
*
********************************************************************************/
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "timeout\n"
int main(void)
{
char buf[10];
int fd, n, i;
fd = open("/dev/tty", O_RDONLY|O_NONBLOCK);
if(fd < 0)
{
perror("open /dev/tty");
exit(1);
}
for(i = 0; i< 5; i++)
{
n = read(fd, buf, 10);
if(n >= 0)
break;
if(errno != EAGAIN)
{
perror("read /dev/tty");
exit(1);
}
sleep(1);
write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
}
if(5 == i)
write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT));
else
write(STDOUT_FILENO, buf, n);
close(fd);
return 0;
}
[lingyun@localhost read_3]$
测试结果:
[lingyun@localhost read_3]$ gcc read.c
[lingyun@localhost read_3]$ ./a.out
try again
try again
try again
try again
try again
timeout
[lingyun@localhost read_3]$ ./a.out
try again
try again
htry again
try again
try again
timeout
[lingyun@localhost read_3]$ h
-bash: h: command not found
[lingyun@localhost read_3]$ ./a.out
try again
try again
try again
h
try again
h
[lingyun@localhost read_3]$ ./a.out
hhhhhtry again
hhhhtry again
try again
hhhhhhhhh
[lingyun@localhost read_3]$ ./a.out
htry again
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhtry again
hhhhhhhhhtry again
try again
hhhhhhhhhh[lingyun@localhost read_3]$ hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
-bash: hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh: command not found
[lingyun@localhost read_3]$
四、
假设某个文件的长度是600字符,而n的值是512,则在第1次调用读这个文件时,系统可以正常地读取512个字符地内容,并将这些字符数量传给number变量,因此number的值将变为88。要第2次读取这个文件时,因为文件已经没有内容可供读取了,此时系统会返回0给number。另外,如果读取文件失败,系统将返回-1给number。
比如一个有100个字节的文件,第一次读取10个字节,这时读取指针在第10个字节处。再次进行10个字节的读操作时,会接着第一次读的位置接着往后读。如果还想从开始读,可使用lseek函数定位。
[lingyun@localhost read_4]$ lsa.out read.c
[lingyun@localhost read_4]$ cat read.c
/*********************************************************************************
* Copyright: (C) 2013 fulinux<fulinux@sina.com>
* All rights reserved.
*
* Filename: read.c
* Description: This file
*
* Version: 1.0.0(07/29/2013~)
* Author: fulinux <fulinux@sina.com>
* ChangeLog: 1, Release initial version on "07/29/2013 10:34:56 AM"
*
********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF 10
int main(void)
{
static char filename[] = "read.c";
char buffer[BUF];
int fd;
int i;
int total = 0;
fd = open(filename, 0, O_RDONLY);
if(fd == -1)
{
printf("[%s] create fail !!!!", filename);
exit(1);
}
else
{
while((i = read(fd, buffer, BUF)))
total += i;
}
printf("The total character in [%s] is %d\n", filename, total);
close(fd);
exit(0);
}
[lingyun@localhost read_4]$
[lingyun@localhost read_4]$ gcc read.c
[lingyun@localhost read_4]$ ./a.out
The total character in [read.c] is 1089