网站pc端建设,99个创意营销方案,wordpress采集别人公众号,兰州新站seo一.select函数
select 的调用注意事项 在使用 select 函数时#xff0c;需要注意以下几个关键点#xff1a;
1. 参数的修改与拷贝 readfds 等参数是结果参数 #xff1a; select 函数会直接修改传入的 fd_set#xff08;如 readfds、writefds 和 exceptfds#xf…一.select函数
select 的调用注意事项 在使用 select 函数时需要注意以下几个关键点
1. 参数的修改与拷贝 readfds 等参数是结果参数 select 函数会直接修改传入的 fd_set如 readfds、writefds 和 exceptfds。 为了保留原始监听集合通常会定义一个备份集合如 allread_fdset并将它的拷贝传递给 select。 示例 fd_set allread_fdset, readfds; FD_ZERO(allread_fdset); FD_SET(fd1, allread_fdset); FD_SET(fd2, allread_fdset); readfds allread_fdset; // 拷贝到临时集合 select(..., readfds, ...); 2. 计算 nfds nfds 是最大文件描述符值 1 在新增监听句柄时更新 nfds 较为简单。 在减少监听句柄时更新 nfds 较为复杂 如果需要精确计算可以通过遍历或维护一个最大堆等数据结构来找到第二大的文件描述符。 或者可以选择忽略 nfds 的更新但可能导致性能下降。 3. 超时参数 timeout timeout 的含义 如果为 NULL表示阻塞等待直到有事件发生。 如果指向的时间为 0表示非阻塞模式。 如果指定超时时间则 select 会在超时后返回。 注意Linux 实现中select 返回时会修改 timeout 为剩余时间 如果需要重复使用 timeout需要重新初始化。 4. 返回值的处理 返回值的意义 -1表示错误。 0表示超时时间到没有事件发生。 正数表示监听到的事件总数包括可读、可写和异常事件。 优化事件处理 可以利用返回值避免不必要的检查。例如如果返回值为 1并且已经在可读集合中处理了一个事件则无需再检查可写和异常集合。
select 的缺点 尽管 select 是一种经典的 I/O 多路复用机制但它存在以下显著缺点 1. 文件描述符数量限制 FD_SETSIZE 的限制 每个 fd_set 最多只能监听 FD_SETSIZE 个文件描述符在 Linux 上通常是 1024。 这一限制使得 select 不适合高并发场景。 2. 遍历效率低 需要逐一检查文件描述符 返回的 fd_set 是一个位图应用程序需要对所有监听的文件描述符逐一调用 FD_ISSET 来判断是否就绪。 示例 for (int i 0; i nfds; i) { if (FD_ISSET(i, readfds)) { // 处理可读事件 } } 3. nfds 的效率问题 select 的实现方式 select 内部会遍历从 0 到 nfds-1 的所有文件描述符判断每个描述符是否是关心的并检查是否有事件发生。 即使只监听少数几个文件描述符如 0 和 1000select 仍然需要遍历 1001 个描述符导致效率低下。 总结 优点 简单易用跨平台支持广泛。 缺点 文件描述符数量受限 最多只能监听 FD_SETSIZE 个文件描述符。 遍历效率低 需要逐一检查文件描述符增加了开销。 nfds 的问题 即使监听的文件描述符稀疏分布select 仍需遍历所有小于 nfds 的描述符。 这些缺点促使了更高效的 I/O 多路复用机制如 poll 和 epoll的出现尤其是在高并发场景下epoll 成为了更优的选择。
【1】管道select 【2】tcp服务器select 二.poll函数
poll 针对select 做了改进 底层实现 --- 用的是数组 poll --- 链表 poll 引入了事件机制 1. 遍历? 2. poll 需要在 用户空间 和 内核空间 来回拷贝
epoll 三种多路IO操作中最高效 三.epoll函数
3. epoll int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;
struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); epoll 解决了select和poll的几个性能上的缺陷 ① 不限制监听的描述符个数poll也是只受进程打开描述符总数的限制 ② 监听性能不随着监听描述 符数的增加而增加是O(1) 的 不再是轮询描述符来探测事件而是由描述符主动上报事件 //事件机制的 ③ 使用共享内存的方式不在用户和内核之间反复传递监听的描述 符信息 ④ 返回参数中就是触发事件的列表不用再遍历输入事件表查询各个事件是否被触发
------------------------------------------
epoll显著提高性能的前提是 监听大量描述符 并且每次触发事件的描述符文件非常少。 epoll的另外区别是 ①epoll创建了描述符记得close ②支持水平触发和边沿触发。
epoll使用注意事项 //epoll_create ① int epoll_create(int size) //创建epoll文件描述符 参数size并不是限制了epoll所能监听的描述符最大个数 只是对内核初始分配内部数据结构的一个建议。 返回是epoll描述符。-1表示创建失败。 //epoll_ctl ② int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) //epoll文件描述符的控制接口 功能: epoll_ctl控制对指定描述符fd执行op操作event是与fd关联的监听事件。 参数: epfd --- epoll对象 op op操作有三种 添加EPOLL_CTL_ADD 删除EPOLL_CTL_DEL 修改EPOLL_CTL_MOD。 分别添加、删除和修改对fd的监听事件。 重复添加fd会怎样event相同或不相同 添加失败errno17 File exists 删除和修改不存在的fd会怎样 删除或修改失败errno9Bad file descriptor fd -- 关心的fd event是与监听的fd相关联的事件信息event-events描述了要监听的事件类型有以下类型 //事件类型: EPOLLIN 可读 EPOLLOUT 可写 EPOLLRDHUP 套接口对端close或shutdown写在ET模式下比较有用 EPOLLPRI 紧急数据可读 EPOLLERR 异常条件 EPOLLHUP 挂起EPOLLERR和EPOLLHUP始终由epoll_wait监听不需要用户设置 EPOLLET 边沿触发模式在描述符状态跳变时才上报监听事件。监听默认都是LT模式(ET非阻塞模式) EPOLLONESHOT 只一次有效设置oneshot标记描述符在触发一次事件之后自动失效fd还被监听 不会再上报任何事件直到使用EPOLL_CTL_MOD重新激活 设置新的监听事件为止可不可以和之前的事件一样。
event-data是个共用体可以存放和fd绑定的描述符信息 比如就存放描述符本身fd或者一个结构体信息包括fdipport等等。
在epoll_wait返回时只会返回一个event列表需要从列表元素中获取fd等信息。 返回值: 返回0表示控制成功 返回-1表示失败。
③ int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); //等待epfd上的io事件最多返回maxevents个事件 timeout -1 的行为是block timeout 0 是立即返回
④ epoll监听ET事件时fd必须是非阻塞套接口。 比如监听可读事件当ET上报可读后需要一直读fd直到遇到EAGAIN错误为止以免遗留数据在缓冲区中。 如果fd是阻塞的则会读到阻塞了。 EAGAIN错误对于非阻塞套接口来说不是错误只是说没有数据可读或者没有空间可写。 EWOULDBLOCK就是EAGAIN值都是11。 selset/poll/epoll的LT模式监听的fd可以是阻塞模式的。
⑤ 多路复用监听io事件时如果对某个套接口监听可写事件总是会返回可写而事实上可能没有数据要写。 处理方法 ①只有在有数据要写时才把要写的套接口加入 监听列表中数据全部写完之后从监听列表中删除它 ②在有数据写时首先尝试直接写当直接写没有把数据全部写入发送缓冲区时再把这个套接口加入可写事件 监听列表。 这种方式效率较高需要套接口是非阻塞的前一种方式可以是阻塞的吗 可以是阻塞的。 四. 特点和区别