非阻塞io和IO多路转接

本文参考《Unix高级环境编程》,仅用于个人学习,备忘。

1. sample 学习代码

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/select.h>

#define MAXLINE 500000

void set_fl(int, int);
void clr_fl(int, int);

int
main(void)
{
    char buf[MAXLINE];
    int ntowrite, nread, nwrite;
    int val;
    char *ptr;
    fd_set rset, wset;

    set_fl(STDIN_FILENO, O_NONBLOCK);   /* 将标准输入设置为非阻塞模式 */
    ntowrite = 0;
    ptr = buf;
    FD_ZERO(&rset);  /* 清空读fd */
    FD_SET(STDIN_FILENO, &rset);   /* 设置标准输入 */
    while (ntowrite < MAXLINE) {
        if (select(4, &rset, NULL, NULL, NULL) < 0) {
            fprintf(stderr, "select error\n");
            exit(1);
        }
        if (!FD_ISSET(STDIN_FILENO, &rset))continue;  /* 直到标准输入准备好 */
        nread = read(STDIN_FILENO, ptr, MAXLINE-ntowrite);
        fprintf(stderr, "read: %d, errno: %d, %s\n",
                    nread, errno, strerror(errno));
        if (nread == 0)   /* 遇到结束符号 */
            break;
        else if (nread > 0) {
            ntowrite += nread;
            ptr += nread;
        }
    }
    clr_fl(STDIN_FILENO, O_NONBLOCK);   /* 将标准输入恢复为阻塞模式 */

    set_fl(STDOUT_FILENO, O_NONBLOCK);   /* 将标准输出设置为非阻塞模式 */
    ptr = buf;
    FD_ZERO(&wset);
    FD_SET(STDOUT_FILENO, &wset);
    while (ntowrite > 0) {
        errno = 0;
        if (select(4, NULL, &wset, NULL, NULL) < 0) {
            fprintf(stderr, "select error, %s\n", strerror(errno));
            exit(3);
        }
        if (!FD_ISSET(STDOUT_FILENO, &wset)) continue;   /* 直到标准输出可以写入 */
        nwrite = write(STDOUT_FILENO, buf, ntowrite);
        fprintf(stderr, "nwrite: %d, errno: %d, %s\n",
                        nwrite, errno, strerror(errno));

        if (nwrite > 0) {
            ptr += nwrite;
            ntowrite -= nwrite;
        }
    }

    clr_fl(STDOUT_FILENO, O_NONBLOCK);  /* 将标注输出恢复为阻塞模式 */
    exit(0);
}

void set_fl(int fd, int flag)
{
    int val;

    if ((val = fcntl(fd, F_GETFL, 0)) < 0) {
        fprintf(stderr, "GETFL error\n");
        exit(1);
    }
    val |= flag;
    if (fcntl(fd, F_SETFL, val) < 0) {
        fprintf(stderr, "SETFL error\n");
        exit(1);
    }
}

void clr_fl(int fd, int flag)
{
    int val;

    if ((val = fcntl(fd, F_GETFL, 0)) < 0) {
        fprintf(stderr, "GETFL error\n");
        exit(1);
    }
    val &= ~flag;
    if (fcntl(fd, F_SETFL, val) < 0) {
        fprintf(stderr, "SETFL error\n");
        exit(1);
    }
}
                                                  

当将标准输入(或标准输出)设置为非阻塞模式, 并且未使用select等IO多路转接时,对它们进行读(或写)会立即返回。如果无法读取(或写入),那么read(或write)会返回-1,同时errno设置为错误代码。如下

read: -1, errno: 11, Resource temporarily unavailable

nwrite: -1, errno: 11, Resource temporarily unavailable

但使用select时,则程序会阻塞在select上,直到标准输入或标准输出准备就绪才返回。

2. select的用法说明

头文件 <sys/select.h>

第一个参数:int maxfdp1,说明需要检查的描述符数量(文件描述符号从0开始),所以这个值通常是(读,写,异常)三个中最大fd的值再加1。也可以设置为FD_SETSIZE,这个常量通常是1024,对大多数应用程序而言太大了,多数应用程序只是用3~10个。

第二,第三,第四个参数readfds, writefds, exceptfds,指向描述符号集的指针。通常用以下方法设置:

fd_set     rset;

FD_ZERO(&rset);

FD_SET(target_fd, &rset);

FD_SET(STDIN_FILENO, &rset);

从select返回后,使用FD_ISSET检查

if (FD_ISSET(target_fd, &rset))  {

...

}

最后一个参数, struct timeval *restrict tvptr,当为NULL时, 永远等待。如果捕捉到一个信号则中断,select返回-1,errno设置为EINTR。当不为零时,则等待指定的时间。如果在超时时还没有一个描述符准备好,则返回值为0。其他和情况一相同。当为零时,则完全不等待。立即返回。这是得到多个描述符的状态而不阻塞select函数的轮询方法。

一个描述符阻塞与否并不影响select是否阻塞。

如果在一个描述符上碰到文件结尾处,则select认为该描述符时可读的。然后调用read,它返回0。


注:如果select同时监听多个fd,当返回时,可能只有部分fd是准备好了的,部分fd是没准备好的。如果这是在一个循环之内,则下一次循环,那些没准备好的fd就相当于是被clear了,不会再被检测。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值