企业门户网站需求模板软件设计公司排名
企业门户网站需求模板,软件设计公司排名,深圳seo网站排名优化,郑州网站制作的公司此为牛客Linux C课程和黑马Linux系统编程笔记。
1. 关于epoll
epoll是Linux下多路复用IO接口select/poll的增强版本#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率#xff0c;因为它会复用文件描述符集合来传递结果而不用迫使开发者每次…此为牛客Linux C课程和黑马Linux系统编程笔记。
1. 关于epoll
epoll是Linux下多路复用IO接口select/poll的增强版本它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合另一点原因就是获取事件的时候它无须遍历整个被侦听的描述符集只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
目前epoll是linux大规模并发网络程序中的热门首选模型。
epoll除了提供select/poll那种IO事件的水平触发Level Triggered外还提供了边沿触发Edge Triggered这就使得用户空间程序有可能缓存IO状态减少epoll_wait/epoll_pwait的调用提高应用程序效率。
2. epoll API介绍
2.1 创建epoll实例epoll_create
#include sys/epoll.h
int epoll_create(int size);功能在内核中创建一个新的epoll实例并返回一个操纵该epoll的文件描述符这个文件描述符和真正的文件没有关系仅仅是为了后续调用epoll而创建的。该函数调用后在内核中创建了一个存储事件的数据结构这个数据结构中有两个比较重要的子结构一个是需要检测的文件描述符的信息使用红黑树实现还有一个是就绪列表存放检测到数据发送改变的文件描述符信息使用双向链表实现关于epoll更详细的内部实现在这里不详细讨论。
参数size : 自从linux2.6.8之后size参数是被忽略的。随便写一个数必须大于0。
返回值 -1 : 失败 0 : 用于操作epoll实例的文件描述符
2.2 注册epoll的监听事件epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);功能向内核中的epoll实例中添加、修改、移除事件。epoll和select的一个显著区别就在这里select是在监听事件时告诉内核要监听什么类型的事件而epoll是在这里先注册要监听的事件类型然后再调用epoll_wait监听。
参数
epfd : epoll实例对应的文件描述符op : 要进行什么操作 EPOLL_CTL_ADD: 添加 EPOLL_CTL_MOD: 修改 EPOLL_CTL_DEL: 删除fd : 要检测的文件描述符event : 检测文件描述符什么事件这里涉及到epoll_event定义如下
struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */
};typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;这里我们只需要关注两个字段即可events和data.fd
其中events表示要检测的事件有以下选择 EPOLLIN 表示对应的文件描述符可以读包括对端SOCKET正常关闭 EPOLLOUT表示对应的文件描述符可以写 EPOLLPRI表示对应的文件描述符有紧急的数据可读这里应该表示有带外数据到来 EPOLLERR表示对应的文件描述符发生错误 EPOLLHUP表示对应的文件描述符被挂断 EPOLLET 将EPOLL设为边缘触发(Edge Triggered)模式这是相对于水平触发(Level Triggered)来说的。 EPOLLONESHOT只监听一次事件当监听完这次事件之后如果还需要继续监听这个socket的话需要再次把这个socket加入到EPOLL队列里。
其中data.fd表示该事件对应的socket的文件描述符。
返回值
成功返回发送变化的文件描述符的个数 0失败 -1
2.3 监听事件epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);功能等待已注册的事件发生返回事件的数目并将已触发的事件写入events数组第二个参数中。
参数
epfd : epoll实例对应的文件描述符events : 传出参数保存了发送了变化的文件描述符的信息需要调用者先创建好maxevents : 第二个参数结构体数组的大小timeout : 阻塞时间 0 : 不阻塞-1 : 阻塞直到检测到fd数据发生变化解除阻塞 0 : 阻塞的时长毫秒
返回值
成功返回发送变化的文件描述符的个数 0失败 -1
3. 示例程序
以下用epoll实现了一个简单的服务端把客户端传来的小写字母转换成大写字母并传回给客户端。
/*用epoll实现一个简单的服务器-客户端通信*/#include stdio.h
#include unistd.h
#include arpa/inet.h
#include stdlib.h
#include pthread.h
#include strings.h
#include sys/epoll.h// 设定一个服务器端口号
#define SERV_IP 127.0.0.1
#define SERV_PORT 9999int main()
{int lfd socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serv_addr;serv_addr.sin_family AF_INET;serv_addr.sin_port htons(SERV_PORT); // 注意转化成网络字节序inet_pton(AF_INET, SERV_IP, serv_addr.sin_addr.s_addr); // 注意转化成网络字节序bind(lfd, (struct sockaddr*)serv_addr, sizeof(serv_addr));listen(lfd, 128);int epfd epoll_create(100); // 内核创建epoll实例struct epoll_event epev;epev.events EPOLLIN; // 要检测lfd的读事件epev.data.fd lfd;// 注册了对lfd的监听此后如果不删除这个注册信息每次调用epoll_wait都将监听lfd的读事件也就是客户端的连接epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, epev); struct epoll_event epevs[1024]; // 用作epoll_wait的第二个参数传出参数 while(1) {int ret epoll_wait(epfd, epevs, 1024, -1); // 监听已注册的事件最后一个参数-1表示阻塞等待if(ret -1) {perror(epoll_wait error);exit(-1);}// 一旦走到这里说明解除了阻塞就是指epoll监测到了事件的发生遍历每个事件for(int i 0; i ret; i) {int curfd epevs[i].data.fd; // 表示当前触发的事件对应的fdif(curfd lfd) { // 如果监听到lfd的读事件了说明有一个新客户端建立连接struct sockaddr_in clie_addr;int clie_addr_len sizeof(clie_addr); int cfd accept(lfd, (struct sockaddr*)clie_addr, clie_addr_len);char clie_IP[BUFSIZ];printf(Client IP: %s, client port: %d connected\n, inet_ntop(AF_INET, clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)),ntohs(clie_addr.sin_port));epev.events EPOLLIN; // 要检测cfd的读事件epev.data.fd cfd;epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, epev); // 把对该cfd的读事件监听注册上以后epoll会同时监听lfd和cfd} else { // 说明检测到的是某个cfd的读事件读该客户端传来的数据char buf[BUFSIZ] {0};int len read(curfd, buf, sizeof(buf));if(len 0) {// 小写转大写int i;for(i 0; i len; i) {if(buf[i] a buf[i] z) {buf[i] - 32;}}write(curfd, buf, len); // 写回给客户端write(STDOUT_FILENO, buf, len);} else if(len 0) {// 说明读完了客户端已关闭此时epoll已经没有必要继续监听该cfd了epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);close(curfd);} else {perror(read error);exit(-1);}}}}close(lfd);close(epfd); // 别忘了关epfdreturn 0;
}4. epoll的两种触发方式
EPOLL事件有两种模型
Edge Triggered (ET) 边缘触发只有数据到来才触发不管缓存区中是否还有数据。Level Triggered (LT) 水平触发只要有数据都会触发。
LTlevel - triggered是缺省的工作方式并且同时支持 block 和 no-block socket。在这种做法中内核告诉你一个文件描述符是否就绪了然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作内核还是会继续通知你的。
ETedge - triggered是高速工作方式只支持 no-block socket。在这种模式下当描述符从未就绪变为就绪时内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪并且不会再为那个文件描述符发送更多的就绪通知直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意如果一直不对这个 fd 作 IO 操作从而导致它再次变成未就绪内核不会发送更多的通知only once。
ET 模式在很大程度上减少了 epoll 事件被重复触发的次数因此效率要比 LT 模式高。epoll工作在 ET 模式的时候必须使用非阻塞套接口以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
关于LT和ET的详细介绍以及为什么ET模式要搭配非阻塞IO见这篇博客写的极好。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/85707.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!