• linux c编程:非阻塞I/O


    通常来说,从普通文件读数据,无论你是采用 fscanf,fgets 也好,read 也好,一定会在有限的时间内返回。但是如果你从设备,比如终端(标准输入设备)读数据,只要没有遇到换行符(‘ ’),read 一定会“堵”在那而不返回。还有比如从网络读数据,如果网络一直没有数据到来,read 函数也会一直堵在那而不返回。

    read 的这种行为,称之为 block,一旦发生 block,本进程将会被操作系统投入睡眠,直到等待的事件发生了(比如有数据到来),进程才会被唤醒。

    系统调用 write 同样有可能被阻塞,比如向网络写入数据,如果对方一直不接收,本端的缓冲区一旦被写满,就会被阻塞。

    比如下面的程序
    int main() {
      char buf[10];
      int len;
      while(1) {
        // STDIN_FILENO 是标准输入的描述符,它的值是 0. STDOUT_FILENO 是标准输出的描述符,它的值是 1.
        len = read(STDIN_FILENO, buf, 10);
        write(STDOUT_FILENO, buf, len);
      }
      return 0;
    }
    gcc –o main.out main.c
    ./main.out 
    如果不向终端输入数据,程序将永远阻塞在read系统调用处。要规避这个问题,我们就需要用到非阻塞的IO。
    对于一个给定的描述符有两种方法对其指定非阻塞I/O:
    1)      如果调用open获得描述符,则可指定O_NONBLOCK标志
    2)      对于已打开的一个描述符,则可调用fcntl,由该函数打开O_NONBLOCK文件状态标志。
    程序如下:
    set_f1是设置读取的方式为非阻塞,clr_f1则是清除非阻塞的方式


    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <malloc.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <unistd.h>



    void set_f1(int fd,int flags){
        int val;
        if ((val=fcntl(fd,F_GETFL,0)) < 0)
            printf("fcntl F_GETFL error");
        val|=flags;
        if (fcntl(fd,F_SETFL,val) < 0)
            printf("fcntl F_sETFL error");
    }

    void clr_f1(int fd,int flags){
        int val;
        if ((val=fcntl(fd,F_GETFL,0)) < 0)
            printf("fcntl F_GETFL error");
        val&=~flags;
        if (fcntl(fd,F_SETFL,val) < 0)
            printf("fcntl F_sETFL error");
    }


    char buf[50000];

    int main()
    {

        int ntowrite,nwrite;
        char *ptr;
        ntowrite=read(STDIN_FILENO,buf,sizeof(buf));
        fprintf(stderr,"read %d bytes ",ntowrite);
        set_f1(STDOUT_FILENO,O_NONBLOCK);
        ptr=buf;
        while(ntowrite > 0){
            errno=0;
            nwrite=write(STDOUT_FILENO,ptr,ntowrite);
            fprintf(stderr,"nwrite=%d,errno=%d ",nwrite,errno);
            if(nwrite > 0){
                ptr+=nwrite;
                ntowrite-=nwrite;
            }
        }
        clr_f1(STDOUT_FILENO,O_NONBLOCK);
        return 0;
    }

     

    若标准输出是普通文件,则可以期望write只执行一次

    root@maple-VirtualBox:/home/maple/codeblock_prj/func_test# ./main.out < /etc/services > temp.file
    read 19605 bytes
    nwrite=19605,errno=0
    root@maple-VirtualBox:/home/maple/codeblock_prj/func_test# ls -al temp.file
    -rw-r--r-- 1 root root 19605 8月  12 15:33 temp.file

    但是,如果标准输出是终端。则有时会返回错误。

    root@maple-VirtualBox:/home/maple/codeblock_prj/func_test# cat stderr.out
    read 19605 bytes
    nwrite=14907,errno=0
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=-1,errno=11
    nwrite=4698,errno=0

    程序发出了多个write调用,但是只有部分真正输出了数据。其他的都只返回了错误。这种形式的循环成为轮询。在多用户系统上会浪费CPU时间。

  • 相关阅读:
    Triangle LOVE
    数据传送指令具体解释
    关于C++String字符串的使用
    TCP/IP基础(一)
    java打开目录(含推断操作系统工具类和解压缩工具类)
    hdu-1848 Fibonacci again and again
    opencv2对读书笔记——图像二值化——thresholded函数
    安卓中四种点击事件
    @MappedSuperclass注解的使用说明
    Androidclient採用Http 协议Post方式请求与服务端进行数据交互
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/9463236.html
Copyright © 2020-2023  润新知