linux之read函数解析

一、

[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]$ ls
a.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


©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页
实付 29.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值