select和pselect学习笔记

2 篇文章 0 订阅
1 篇文章 0 订阅

简介

select、pselect用于同步I/O多路复用。select、pselect允许程序同时监听多个fd,直到其中一个或多个fd上有对应的I/O操作就绪时,或超时时才返回。

相关结构体定义

fd_set

/* fd_set for select and pselect.  */
typedef struct
  {
    /* XPG4.2 requires this member name.  Otherwise avoid the name
       from the global namespace.  */
#ifdef __USE_XOPEN
    __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else
    __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif
  } fd_set;

fd_set采用bitset方式来存储设置的fd,这种方式在有效fd数量较少时存在较大的浪费。fd_set有最大连接数限制,即最大连接数为__FD_SETSIZE。如果fd的值太大则会出现数组内存溢出的风险。

struct timeval

struct timeval {
    long    tv_sec;         /* seconds */
    long    tv_usec;        /* microseconds */
};

struct timespec

struct timespec {
    long    tv_sec;         /* seconds */
    long    tv_nsec;        /* nanoseconds */
};

相关函数介绍

辅助宏函数

辅助宏函数有FD_CLR/FD_ISSET/FD_SET/FD_ZERO,这些宏函数主要是用于对fd_set的成员__fds_bits进行bitset操作。宏函数的详细定义见头文件:/usr/include/sys/select.h和/usr/include/bits/select.h。

FD_CLR

void FD_CLR(int fd, fd_set *set);

从集合set中删除fd

FD_ISSET

int  FD_ISSET(int fd, fd_set *set);

判断集合set中是否已经设置了fd

FD_SET

void FD_SET(int fd, fd_set *set);

将fd添加到集合set中

FD_ZERO

void FD_ZERO(fd_set *set);

清空整个集合set

select

依赖头文件

POSIX.1-2001之后

/* According to POSIX.1-2001 */
#include <sys/select.h>

早期版本

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

函数定义

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

允许程序同时监听多个fd的I/O事件是否就绪,当一个或多个fd上有I/O事件就绪时则返回,否则会等到超时后才返回。
参数说明
nfds: nfds的值等于readfds、writefds、exceptfds三个集合中编号最高的fd的值再+1。
readfds: 输入监听的所有读事件的fd集合,输出触发了读事件的所有fd集合。输出fd集合是输入fd集合的子集。
writefds: 输入监听的所有写事件的fd集合,输出触发了写事件的所有fd集合。输出fd集合是输入fd集合的子集。
exceptfds: 输入监听的所有异常事件的fd集合,输出触发了异常事件的所有fd集合。输出fd集合是输入fd集合的子集。
timeout: select等待超时时间。如果传入NULL,则阻塞等待,直到有对应的事件发生才返回;否则最多等待timeout指定的超时时间。
返回值说明
成功返回大于等于0,返回大于0表示三个fd集合中触发事件的fd的总个数,等于0表示等待超时;错误返回-1,错误码从errno中获取。
错误码说明
EBADF: 某个fd集合中添加了无效的fd。
EINTR: 被信号中断。
EINVAL: 参数非法。nfds小于0,或timeout是无效的。
ENOMEM: 内存不足错误,导致没有内存用于select内部使用。
说明: 对集合readfds、writefds、exceptfds的所有操作必须使用宏函数FD_CLR、FD_ISSET、FD_SET、FD_ZERO。

pselect

glibc支持与否检查

检查当前系统中的glibc库是否支持pselect接口的方法是检查对应的宏_POSIX_C_SOURCE或_XOPEN_SOURCE的值。具体示例代码如下:

#if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
/* can use pselect */
#else
/* can't use pselect */
#endif

依赖头文件

#include <sys/select.h>

函数定义

int pselect(int nfds, fd_set *readfds, fd_set *writefds,
            fd_set *exceptfds, const struct timespec *timeout,
            const sigset_t *sigmask);

允许程序同时监听多个fd的I/O事件是否就绪,当一个或多个fd上有I/O事件就绪时则返回,否则会等到超时后才返回。
参数说明
nfds: nfds的值等于readfds、writefds、exceptfds三个集合中编号最高的fd的值再+1。
readfds: 输入监听的所有读事件的fd集合,输出触发了读事件的所有fd集合。输出fd集合是输入fd集合的子集。
writefds: 输入监听的所有写事件的fd集合,输出触发了写事件的所有fd集合。输出fd集合是输入fd集合的子集。
exceptfds: 输入监听的所有异常事件的fd集合,输出触发了异常事件的所有fd集合。输出fd集合是输入fd集合的子集。
timeout: select等待超时时间。如果传入NULL,则阻塞等待,直到有对应的事件发生才返回;否则最多等待timeout指定的超时时间。
sigmask: 指向一个设置了信号掩码信息的结构。如果不为NULL,则pselect先将当前的信号掩码替换成sigmask指向的信号掩码,再执行select,最后再替换会原来的信号掩码。
返回值说明
成功返回大于等于0,返回大于0表示三个fd集合中触发事件的fd的总个数,等于0表示等待超时;错误返回-1,错误码从errno中获取。
错误码说明
EBADF: 某个fd集合中添加了无效的fd。
EINTR: 被信号中断。
EINVAL: 参数非法。nfds小于0,或timeout是无效的。
ENOMEM: 内存不足错误,导致没有内存用于select内部使用。
说明: 对集合readfds、writefds、exceptfds的所有操作必须使用宏函数FD_CLR、FD_ISSET、FD_SET、FD_ZERO。

select和pselect比较

  1. 对pselect的支持对系统和glibc库有要求,所以使用是必须进行对应编译宏检查,必须要_POSIX_C_SOURCE>=200112L或_XOPEN_SOURCE>=600时才能使用。select则没有这些限制。
  2. select的timeout参数使用的是struct timeval,精度为微秒。pselect的timeout参数使用的是struct
    timespec,精度为纳秒。看似pselect超时精度更高,但由于其底层实现是复用的select,所以最后还是会转换成struct timeval,所以其精度是一样的。
  3. select返回时会修改timeout的值为其剩余时间,从而可以得到一次select所消耗的时间。pselect返回时不会修改timeout的值。
  4. pselect有sigmask参数,而select没有。

struct timeval和struct timespec定义

所在头文件

#include <sys/time.h>

结构定义

struct timeval

struct timeval {
	long    tv_sec;         /* seconds */
	long    tv_usec;        /* microseconds */
};

struct timespec

struct timespec {
	long    tv_sec;         /* seconds */
	long    tv_nsec;        /* nanoseconds */
};

用例展示

select

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

int
main(void)
{
    fd_set rfds;
    struct timeval tv;
    int retval;

    /* Watch stdin (fd 0) to see when it has input. */
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

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

    retval = select(1, &rfds, NULL, NULL, &tv);
    /* Don't rely on the value of tv now! */

    if (retval == -1)
        perror("select()");
    else if (retval) {
        if (FD_ISSET(0, &rfds)) {
            printf("Data is available now with fd 0.\n");
        }
    } else
        printf("No data within five seconds.\n");

    exit(EXIT_SUCCESS);
}

pselect

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

int
main(void)
{
    fd_set rfds;
    struct timespec ts;
    int retval;

    /* Watch stdin (fd 0) to see when it has input. */
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    /* Wait up to five seconds. */
    ts.tv_sec = 5;
    ts.tv_usec = 0;

    retval = pselect(1, &rfds, NULL, NULL, &ts, NULL);
    /* Don't rely on the value of ts now! */

    if (retval == -1)
        perror("pselect()");
    else if (retval) {
        if (FD_ISSET(0, &rfds)) {
            printf("Data is available now with fd 0.\n");
        }
    } else
        printf("No data within five seconds.\n");

    exit(EXIT_SUCCESS);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值