I/O多路转接之select

本文详细介绍了select函数的工作原理及其在多路复用输出/输入模型中的应用。通过讲解select函数的参数含义、内部结构fd_set及操作接口,帮助读者了解如何使用select监控多个文件描述符的状态变化,并给出具体示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系统提供select函数来实现多路复用输出/输入模型:
系统调用select是为了使一个程序监视多个文件描述符的状态变化,程序会停在select这里等待,直到被监视的文件描述符有一个或者多个文件描述符发生了状态改变。

#include<sys/select.h>

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

int nfds,                           ——需要监视的最大的文件描述符加1
fd_set *readfds,                    ——需要检测的可读文件描述符的集合
fd_set *writefds,                   ——需要检测的可写文件描述符的集合
fd_set *exceptfds,                  ——需要检测的异常文件描述符的集合
struct timeval *timeout,            ——用来设置select()的等待时间

我们看到都是一个结构体—— fd_set
我们只需要看看这个抽象的结构体:

typedefstruct fd_set {
 u_int fd_count;
 socket fd_array[FD_SETSIZE];
} fd_set;

这个结构就是一个整数数组,是一个“位图”,使用位图中的位来表示要监视的文件描述符。

提供了一组操作fd_set的接口:
void FD_CLR(int fd, fd_set *set);       // 用来清除描述词组set中相关fd 的位
int FD_ISSET(int fd, fd_set *set);      // 用来测试描述词组set中相关fd 的位是否为真
void FD_SET(int fd, fd_set *set);       // 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set);              // 用来清除描述词组set的全部位

ps:理解select执行过程,fd_set长度为一个字节,(0000 0000)我们取fd_set中的每一个bit可以对应一个文件描述符fd,则字节长的fd_set最大可以对应8个文件描述符。
(1)执行FD_ZERO(),则set用位表示为(0000 0000)
(2)若fd = 5 ,则set变为(0001 0000),第五位变成1
(3)若fd = 1,则set变为(0001 0001),第一位变成1
(4)执行select(6,&set,NULL,NULL,NULL)
(5)若fd = 1上都发生可读事件,则select返回,此时set变为(0000 0001)。——注意,此时没有事件发生的fd = 5被清空

select特点
1.可监控的文件描述符个数取决于sizeof(fd_set)的值,一般都是 sizeof(fd_set)* 8
2.将fd添加到select监视集的同时,还需要同时使用一个数组array保存原select监控集的数据
…(1)用于select返回后,array作为源数据和fd_set进行FD_ISSET判断
…(2)select返回后会把以前加入的但是无事件发生的fd清空,则每次开始select前都要重新的从array中把fd重新添加入监控集中(ZERO优先),扫描array的同时,记录下最大值maxfd,用于select的第一个参数

select缺点
1.每次调用select,都需要手动设置fd的集合,从接口使用角度来说很不方便
2.每次调用select,都需要把fd集合从用户态拷贝到内核态,fd很多时,拷贝开销会很大
3.每次调用select都需要在遍历内核传递进来的所有fd,fd很多时开销会很大
4.select支持的文件描述符太小

下面是 select 简单的监控标准输入实现:

#include<stdio.h>
#include<unistd.h>
#include<sys/select.h>

int main()
{
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(0,&read_fds);

    while(1)
    {
        printf(">");
        fflush(stdout);
        int ret = select(1,&read_fds,NULL,NULL,NULL);
        if(ret < 0)
        {
            perror("select");
            continue;
        }

        if(FD_ISSET(0,&read_fds))
        {
            char buf[1024] = {0};
            read(0,buf,sizeof(buf)-1);
            printf("input:%s",buf);
        }else{
            printf("error! invaild fd\n");
            continue;
        }
        FD_ZERO(&read_fds);
        FD_SET(0,&read_fds);
    }
    return 0;
}

select编写网络服务器:
有兴趣的戳一下:
GitHub:https://github.com/Shaweia/project/tree/master/select

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值