File I/O Multiplexed I/O

2019-08-12  本文已影响0人  无无吴

select()

#include <sys/select.h>
int select(int n, fd_set *readfds, fd_set *writefds, 
      fd_set *exceptfds, struct timeval *timeout);
FD_CLR(int fd, fs_set *set);
FD_ISSET(int fd, fs_set *set);
FD_SET(int fd, fs_set *set);
FD_ZERO(int fd, fs_set *set);

select()的调用会一直阻塞,直到给定的文件描述符可以执行I/O,或是已过了可选指定的超时。
readfds中列出的文件描述符,以查看数据是否可用于读取。
writefds中列出的文件描述符,以查看写入操作是否将在不阻塞的情况下完成。
exceptfds中列出的文件描述符,以查看是否发生异常,或是否带外数据可用(这些状态仅适用于sockets)。
上述这些可以设置为NULL, 表示select不监视这个事件。
第一个参数n等于任何集合中值最高的文件描述符的值,再加上1。
timeout参数是指向timeval结构的指针,定义如下:

#include <sys/time.h>
struct timeval{
        long tv_sec;
        long tv_usec; 
};

如果这个参数不是NULL, select将会返回超时返回,即使没有任何的文件描述符准备好执行IO。
**因为这个结构体的状态在各个UNIX系统中都是未定的,所以在每次调用select前,都要重新初始化。连同文件描述符sets。
如果timeout的两个参数都设置为0,那么调用将立即返回,报告调用时尚未处理的任何事件,但不等待任何后续事件。

文件描述符集不是直接操作的,而是通过宏进行管理。

fd_set writefds;
FD_ZERO(&writefds);
FD_SET(fd, &writefds); /* add 'fd' to the set */
FD_CLR(fd, &writefds); /* oops, 
  remove 'fd' from the set */

设计良好的代码不应该使用FD_CLR,而且很少使用它。

if(FD_ISSET(fd, &writefds))
    /*'fd' is readable without blocking*/

在成功的情况下,select()返回所有三个集合中为I/O准备的文件描述符的数量。如果提供了超时,则返回值可能为0。如果出现错误,则调用返回−1,并设置errno。 对于以下值之一:

举例使用:

int selectExample() {
    struct timeval tv;
    fd_set readfds;
    int ret;

    /*wait on stdin for input*/
    FD_ZERO(&readfds);
    FD_SET(STDIN_FILENO, &readfds);

    /*Wait up to five seconds*/
    tv.tv_sec = TIMEOUT;
    tv.tv_usec = 0;

    /*All right, now block!*/
    ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
    if(ret == -1){
        perror("select");
        return 1;
    }
    else if(!ret){
        printf("%d seconds elapsed\n", TIMEOUT);
        return 0;
    }

    if(FD_ISSET(STDIN_FILENO, &readfds)){
        char buf[BUF_LEN+1];
        int len;
        /*guaranteed to not block*/
        len = read(STDIN_FILENO, buf, BUF_LEN);
        if(len == -1){
            perror("read");
            return -1;
        }
        if(len){
            buf[len] = '\0';
            printf("read: %s\n", buf);
        }
        return 0;
    }

    fprintf(stderr, "This should not happen!\n");
    return 1;
}

int main()
{
    while(1){
        selectExample();
    }
    return 0;
}
实验结果

用select实现可移植的延时,delay, sleep

因为select()在不同的Unix系统上比用于亚秒分辨率休眠的机制更容易实现,所以它通常被用作提供的可移植的睡眠方式。

int selectSleep(int sec, int usec) {
    struct timeval tv;
    tv.tv_sec = sec;
    tv.tv_usec = usec;

    /*select for 500 microseconds*/
    select(0, NULL, NULL, NULL, &tv);
    return 0;
}

int main()
{
    //testO_TRUNC();
    //testMode();
    //testTruncate();
    while(1){
        printf("Hello!\n");
        selectSleep(1, 0);
    }
    return 0;
}
delay 1s 实验

pselect()

#define _XOPEN_SOURCE 600
#include <sys/select.h>
int pselect (int n,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
const struct timespec *timeout,
const sigset_t *sigmask);

/* these are the same as those used by select() */
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);

pselect()和select()之间有三个不同之处:

#include <sys/time.h>
struct timespec {
        long tv_sec; /* seconds */
        long tv_nsec; /* nanoseconds */
};

在Unix工具箱中添加pselect()的主要动机是添加sigmask参数,该参数试图解决等待文件描述符和signal之间的竞争。

poll()

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {
      int fd; /* file descriptor */
      short events; /* requested events to watch */
      short revents; /* returned events witnessed */
};

在events字段中请求的所有事件都可以在revents字段中返回。
有效事件如下:

此外,下列事件可在revents字段中返回:

POLLIN | POLLPRI等价于select()的read事件
POLLOUT | POLLWRBAND 等价于select()的write事件

POLLIN == POLLRDNORM | POLLRDNORM
POLLOUT == POLLWRNORM

Timeout参数指定以毫秒为单位等待的时间长度,负值表示无限超时。值为0表示调用立即返回。

在成功情况下,poll()返回其结构具有非零revents字段的文件描述符的数量。
如果超时发生在任何事件发生之前,则返回0。
失败时,返回−1。

基本用法1:

int pollExample() {
    struct pollfd fds[2];
    int ret;

    /*watch stdin for input*/
    fds[0].fd = STDIN_FILENO;
    fds[0].events = POLLIN;

    /*watch stdout for ability to write(always true)*/
    fds[1].fd = STDOUT_FILENO;
    fds[1].events = POLLOUT;

    /*All set, block!*/
    ret = poll(fds, 2, TIMEOUT * 1000);
    if(ret == -1){
        perror("poll");
        return 1;
    }
    if(!ret){
        printf("%d seconds elapsed.\n", TIMEOUT);
        return 0;
    }

    if(fds[0].revents & POLLIN){
        printf("stdin is readable\n");
    }

    if(fds[1].revents & POLLOUT){
        printf("stdout is writeable\n");
    }

    return 0;

}

int main()
{
    pollExample();
    return 0;
}
基本用法1实验结果

基本用法2:

int pollExample() {
    struct pollfd fds[2];
    int ret;

    /*watch stdin for input*/
    fds[0].fd = STDIN_FILENO;
    fds[0].events = POLLIN;

    /*All set, block!*/
    while(1) {
        ret = poll(fds, 1, TIMEOUT * 1000);
        if (ret == -1) {
            perror("poll");
        }
        if (!ret) {
            printf("%d seconds elapsed.\n", TIMEOUT);
        }

        if (fds[0].revents & POLLIN) {
            char buf[BUF_LEN + 1];
            int len;
            /*guaranteed to not block*/
            len = read(STDIN_FILENO, buf, BUF_LEN);
            if (len == -1) {
                perror("read");
            }
            if (len) {
                buf[len] = '\0';
                printf("read: %s\n", buf);
            }
        }
    }

    return 0;

}

int main()
{
    pollExample();
    return 0;
}
基本用法2实验结果

ppoll()

Linux提供了一个ppoll轮询(),与pselect()的一脉相承。但是,与pselect()不同的是,ppoll()是一个特定于Linux的接口:

#define _GNU_SOURCE
#include <poll.h>
int ppoll (struct pollfd *fds,nfds_t nfds,
const struct timespec *timeout,
const sigset_t *sigmask);

与pselect()一样,timeout参数指定一个以秒和纳秒为单位的超时值,sigmask参数提供了一组等待的信号。

poll()和select()对比

尽管它们执行相同的基本任务,但出于以下几个原因,轮询()系统调用优于select():

select()调用确实也有一些优势功能:

上一篇 下一篇

猜你喜欢

热点阅读