linux通用基础

多路io

1. select

  • 最大监听 1024 个 fd(内核宏限制)
  • 每次调用都要把整个 fd 集合从用户态拷贝到内核态
  • 内核通过遍历所有 fd 找就绪的
  • 水平触发(LT)
  • 可移植性最好(几乎所有系统支持)

返回值:
>0:就绪的 fd 总数
0:超时
-1:出错(errno)

使用步骤:
定义 fd_set
FD_ZERO 清空
FD_SET 把需要监听的 fd 加进去
调用 select
用 FD_ISSET 遍历判断哪个就绪

需知道的套配宏

FD_ZERO(&set);      // 清空集合
FD_SET(fd, &set);   // 把 fd 加入监听
FD_CLR(fd, &set);   // 从集合删除 fd
FD_ISSET(fd, &set); // 判断 fd 是否就绪
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

FD_ZERO(fd_set *);
FD_SET(int fd, fd_set *);
FD_ISSET(int fd, fd_set *);
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>

int main() {
    fd_set rfds;
    struct timeval tv;
    int ret;

    while (1) {
        // 1. 清空集合
        FD_ZERO(&rfds);
        // 2. 添加要监听的 fd
        FD_SET(0, &rfds);

        tv.tv_sec = 1;
        tv.tv_usec = 0;

        // 3. 监听:最大fd+1,读集合,超时
        ret = select(1, &rfds, NULL, NULL, &tv);

        if (ret == -1) {
            perror("select");
            break;
        } else if (ret == 0) {
            printf("timeout...\n");
            continue;
        }

        // 4. 判断是否就绪
        if (FD_ISSET(0, &rfds)) {
            char buf[128];
            read(0, buf, 127);
            printf("select 读到: %s", buf);
        }
    }
    return 0;
}

2. poll

  • 没有 1024 数量限制
  • 原理和 select 几乎一样:仍遍历、仍全量拷贝
  • 只是数据结构换成链表,去掉上限
  • 水平触发(LT)

返回值:
>0:就绪 fd 数量
0:超时
-1:错误

使用步骤:
定义 struct pollfd 数组
填入 fd 和 events
调用 poll
遍历数组,判断 revents & POLLIN 等

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {
    int fd;
    short events;  // POLLIN/POLLOUT
    short revents; // 内核返回
};
#include <stdio.h>
#include <poll.h>
#include <unistd.h>

int main() {
    struct pollfd fds[1];
    fds[0].fd = 0;
    fds[0].events = POLLIN; // 监听可读

    while (1) {
        // 监听 1 个 fd,超时 1000ms
        int ret = poll(fds, 1, 1000);

        if (ret == -1) {
            perror("poll");
            break;
        } else if (ret == 0) {
            printf("timeout...\n");
            continue;
        }

        // 判断返回事件
        if (fds[0].revents & POLLIN) {
            char buf[128];
            read(0, buf, 127);
            printf("poll 读到: %s", buf);
        }
    }
    return 0;
}

3. epoll(高性能王者)

  • Linux 特有
  • 三个 API:epoll_create / epoll_ctl / epoll_wait
  • 内核使用红黑树管理 fd,只在添加 / 删除时拷贝一次
  • 回调机制,只返回就绪的 fd,不用遍历
  • 支持 LT(水平) + ET(边缘) 两种触发方式
  • 高并发下性能碾压 select/poll

返回值:
>0:就绪 fd 个数
0:超时
-1:错误

使用步骤:
epoll_create 创建句柄
构建 epoll_event,调用 epoll_ctl(ADD) 加入 fd
循环 epoll_wait 等待事件
直接遍历返回的就绪数组,不用全扫

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
#include <stdio.h>
#include <sys/epoll.h>
#include <unistd.h>

int main() {
    int epfd = epoll_create(1);

    struct epoll_event ev, events[1];
    ev.events = EPOLLIN;
    ev.data.fd = 0;

    // 添加监听
    epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &ev);

    while (1) {
        // 等待事件,最多返回 1 个,超时 1000ms
        int n = epoll_wait(epfd, events, 1, 1000);

        if (n == -1) {
            perror("epoll_wait");
            break;
        } else if (n == 0) {
            printf("timeout...\n");
            continue;
        }

        if (events[0].data.fd == 0) {
            char buf[128];
            read(0, buf, 127);
            printf("epoll 读到: %s", buf);
        }
    }
    return 0;
}

4、性能对比

特性selectpollepoll
监听上限1024
遍历方式遍历全部 fd遍历全部 fd只返回就绪 fd
数据拷贝每次都全量拷贝每次都全量拷贝仅增删时拷贝
效率O(n)O(n)O(1)
平台全平台全平台Linux 专属
触发方式LTLTLT + ET

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值