Linux IO多路复用(多路转接)

IO多路复用(多路转接)
在没有IO多路复用技术前,进程在同一时间只能监控一个IO状态(一个文件描述符的状态),例如A和B两个文件在操作,定的顺序是A前B后,那么在A阻塞过程中、B在后面也是不能被操作的,这样会造成资源浪费、响应缓慢。
IO多路复用:同时监控多个IO文件描述符状态,如果没有准备好的描述符、则进程始终处于睡眠状态(或超时返回);在监控期间内 如果有准备好的描述符,则立刻告诉进程,进程开始处理就绪的描述符。例如上面的A前B后的情形,使用IO复用技术则可同时监听A和B哪个就绪,接下来可优先处理已就绪的文件事件。

两种方法解决IO多路复用,epoll后续博客再说。


方法一 select : 

int select (int n,                        // 所有集合中文件描述符的最大值+1
            fd_set *readfds,            // 需监控的读 描述符集合
            fd_set *writefds,            // 需监控的写 描述符集合
            fd_set *exceptfds,            // 需监控的异常/带外数据的 描述符集合
            struct timeval *timeout);    // 超时时间

// 其中timeout参数:
/* 时间结构体 */
struct timeval {
    long tv_sec; /* seconds */
    long tv_usec; /* microseconds */
};

select在等待时间范围内,没有准备好的描述符,则会阻塞等待;在此期间 如果有文件描述符就绪,立刻返回。
返回值:通常情况下返回所有就绪的文件描述符的数目,发生错误返回-1 。
且在返回时,各描述符集合中 只保留 就绪的文件描述符

select调用超过一定时间(参数timeout),还没有准备好的描述符,则 select() 调用返回0(就绪的描述符数量为0)。

以下是管理文件描述符集合的宏:

/* 以下是一些宏,管理文件描述符集合 */
FD_CLR(int fd, fd_set *set);    // 从set集合中移除一个描述符fd
FD_ISSET(int fd, fd_set *set);    // 测试某描述符fd是否在set集合中。返回值:0-不在;非负值-在
FD_SET(int fd, fd_set *set);    // 向set集合中添加一个描述符fd
FD_ZERO(fd_set *set);            // 从set集合中移除所有描述符

程序示例:

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main(){;
    fd_set read_fds;
    fd_set write_fds;

    FD_ZERO(&read_fds);
    FD_SET(STDIN_FILENO, &read_fds);    // 将STDIN_FILENO添加到监控的描述符中

    // 设定select等待时间
    struct timeval wait_time;
    wait_time.tv_sec = 5;   // 秒
    wait_time.tv_usec = 0;  // 微秒

    int select_rslt = select(STDOUT_FILENO + 1,
                             &read_fds,
                             NULL,
                             NULL,
                             &wait_time);

    if (select_rslt == -1) {
        perror("select");
        return 1;
    } else if (select_rslt == 0) {
        fprintf(stdout, "No fd is ready, and 5 seconds elapsed.\n");
        return 0;
    } else {
        // 有就绪的描述符
        if (FD_ISSET(STDIN_FILENO, &read_fds)) {    // 如果标准输入就绪
            char buf[20] = {0};
            int n_read = read(STDIN_FILENO, buf, 10);
            if (n_read == -1) {
                perror("read");
                return 1;
            }
            fputs(buf, stdout);
        }
    }
    return 0;
}


方法二 poll :

int poll (struct pollfd *fds,         // struct pollfd的数组
            unsigned int nfds,        // 上面数组中的元素个数
            int timeout);            // 等待的时间长度(毫秒),负数表示永远等待,0表示立即返回
             
// 其中 struct pollfd如下:
struct pollfd {
    int fd;         // 监视的文件描述符
    short events;     // 要监视的事件,由用户设置
    short revents;     // 发生的事件,内核设置
};

参数 fds 说明: 一个struct pollfd代表一个文件描述符,结构内:fd是文件描述符值,events是用来被用户(即程序员)设置希望可监听的属性(例如可读、可写等等),revents是内核返回时设置的已发生的事件(例如这个描述符已经可写、可读等等)。

返回值:返回pollfd结构体中,revents非零的结构体的个数(其实就是有事件发生的描述符个数)。

并且在返回时,可查看相应文件描述符的pollfd结构中的 revents字段,根据此字段判断描述符是否可操作。

程序示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/poll.h>

int main() {
    struct pollfd fds[2];
    fds[0].fd = STDIN_FILENO;
    fds[0].events = POLLIN;     // 监听标准输入是否可读

    int time_wait = 3;

    int poll_rslt = poll(fds, 1, time_wait * 1000);

    if (poll_rslt == -1) {              // poll() error
        perror("poll");
        return 1;
    } else if (poll_rslt == 0) {        // non fd is ready
        fprintf(stdout, "No fd is available.\n");
        return 0;
    } else {
        if (fds[0].revents & POLLIN) {      // 判断 fds[0]是否可读
            char buf[20] = {0};
            ssize_t n_read = read(STDIN_FILENO, buf, 5);
            if (n_read == -1) {
                perror("read");
                return 1;
            }
            puts(buf);
        }
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值