7、epoll边沿触发与反应堆

2023-10-12 03:28
文章标签 触发 epoll 边沿 反应堆

本文主要是介绍7、epoll边沿触发与反应堆,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

epoll边沿触发

1、epoll事件模型:

​ epoll监听的是文件描述符,也可以监控进程间通信的事件。

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>#define MAXLINE 10int main(int argc, char *argv[])
{int efd, i;int pfd[2];pid_t pid;char buf[MAXLINE], ch = 'a';pipe(pfd);//创建一个管道pid = fork();//创建一个主进程if (pid == 0) //pid = 0说明是子进程,子进程完成写操作{          close(pfd[0]);//关闭子进程读端while (1){//aaaa\nfor (i = 0; i < MAXLINE/2; i++)buf[i] = ch;buf[i-1] = '\n';ch++;//bbbb\nfor (; i < MAXLINE; i++)buf[i] = ch;buf[i-1] = '\n';ch++;//aaaa\nbbbb\nwrite(pfd[1], buf, sizeof(buf));//向管道里写bufsleep(5);}close(pfd[1]);} else if (pid > 0){ struct epoll_event event;struct epoll_event resevent[10];        //epoll_wait就绪返回eventint res, len;close(pfd[1]);efd = epoll_create(10);//创建一个文件描述字来进行监听cfdevent.events = EPOLLIN | EPOLLET;     // ET 边沿触发// event.events = EPOLLIN;                 // LT 水平触发 (默认)event.data.fd = pfd[0];epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event);/*event.events = EPOLLIN | EPOLLET;:这行代码设置了event结构体的events字段,指示关		联的文件描述符(pfd[0])应该以边沿触发(Edge Triggered,ET)模式监视可读事件			 (EPOLLIN)。在ET模式下,只有当文件描述符上的状态从不可读变为可读时,才会触发事件,因此需要		 确保在处理可读事件后,读取所有可用数据,直到再次返回EAGAIN或EWOULDBLOCK。event.data.fd = pfd[0];:这行代码设置了event结构体的data.fd字段,将文件描述符		  pfd[0]与这个事件关联起来。epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event);:这行代码将event结构体中定义的事	   件添加到epoll实例(由efd表示)。这将告诉epoll开始监视pfd[0]上的可读事件,并在该事件发生	  时通知应用程序。*/	//LT为水平触发,只要缓冲区里还有就会触发//ET为边沿触发,只有缓冲区中内容在增加时才会触发while (1) {res = epoll_wait(efd, resevent, 10, -1);//epoll_wait函数来等待事件的发生。efd是epoll实例的文件描述符,resevent是用于存			 //储就绪事件的数组,10表示resevent数组的大小,-1表示等待时间无限长printf("res %d\n", res);if (resevent[0].data.fd == pfd[0]) //检查就绪事件数组中的第一个事件是否与文件描述符pfd[0]相关。data.fd字段存储了与事件			  //相关的文件描述符。{len = read(pfd[0], buf, MAXLINE/2);write(STDOUT_FILENO, buf, len);}}close(pfd[0]);close(efd);} else {perror("fork");exit(-1);}return 0;
}

ET模式:边沿触发

​ 缓冲区剩余未读尽的数据不会导致epoll_wait返回。新的事件满足才会触发。

LT模式:水平触发(默认)

​ 缓冲区未读尽的数据会导致epoll_wait返回

2、epoll中的ET非阻塞模式

readn:读够一定数量的字节才会返回

**只需要三行代码:**文件描述字设置非阻塞

flag = fcntl(cfd,F_GETFL);
flag |= 0_NOBLOCK;
fcntl(cfd,F_SETFL,flag);

缺点:不能跨平台,只能在Linux上使用

epoll反应堆模型

1、概述:

​ 有n个客户端,服务器就会有n个连接(n个客户端和连接端)。

客户端
连接端
epoll检测是可读事件还是可写事件
服务器

​ n:有可读事件(包括lfd)和可写事件。

/*一个事件其是可以看成下面结构体的三个成员*/
struct event
{int cfd;read_cb();write_cb();
}

​ 反应堆:一个IO对应多个事件。

2、代码实现(检测业务的实现)

lfd采用accept处理
cfd采用recv和send处理
#define EVENT_LENGTH 1024
unsigned char buf[];
int init_server
{}
struct item
{int cfd;unsigned char rbuffer[1024];//读缓冲区int rlen;unsigned char wbuffer[4096];int wlen;//将要写的长度int wsize;//已经写的长度
};
struct item *get_item_by_clientfd(int clientfd)
{//通过cfd来找item数组
};int main()
{int epfd = epoll_create(1);int sockfd = init_server();//最开始的时候,红黑树只有一个lfdepoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);struct epoll_event events[EVENT_LENGTH] = {0};while(1){int nready = epoll_wait(epfd,events,EVENT_LENGTH,-1);int i = 0;for(i = 0; i < nready; i++){//第一层if用来判断是lfd还是cfd//第二层if用来判断是读cfd,还是写cfdif(events[i].data.fd == sockfd){struct sockaddr_in client;socklen_t len = sizeof(client);int cfd = accept(sockfd,(struct sockaddr*)&client,len);//来一个客户端连接一个服务器struct epoll_event ev = {0};ev.event = EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);}else{struct item *it = get_item_by_clientfd(events[i].data.fd);if(event[i].event & EPOLLIN){//读客户端it->len = recv(events[i].data.fd,buffer,1024,0);//只读一半,没有读完}if(event[i].event & EPOLLOUT){//写客户端send(events[i].data.fd,it->wbuffer+it->wsize,it->wlen-it.size,0);//当一次性没有写完时,要放在写缓冲区里面//对于一次性没有读完时,要放在读缓冲区//这里的缓冲区指的是内核的缓冲区it->wsize += ret;}}}}
}

那么对于以上代码,buffer的处理业务在哪里呢?

对于数据来说,有三层操作:

检测IO事件
read/write
解析

​ 为什么不能直接将解析代码放到recv和send后面呢?因为如果这样写的话,代码的可复用性较差。所以使用回调函数。

​ 反应堆主要是用来检测fd是读还是写。

错误说法:reactor是epoll加上了回调函数: reactor的是用来形容一个事件是否触发

对于下面一个结构体的两种封装方式,哪一个好:

struct reactor
{int epfd;struct item *items;int count;int ucount;
};

封装方式一:

struct reactor *Init_reactor(int size)
{struct reactor *t = malloc(sizeof(struct reactor));}
struct reactor *del_reactor(struct reactor *r)
{free(r);
}

封装方式二:

struct reactor *Init_reactor(struct reactor *r)
{r->epfd = epoll_create(1);r->items = malloc(1024*sizeof(struct reactor *r));r->count = 1024;r->ucount = 0;
}
struct reactor *Init_reactor(struct reactor *r)
{close(r->epfd);
}

封装方式二更好,因为其避免了出现返回值

之后需要封装下面几个函数

int accept_callback(int fd, int events, void *arg)
{   
}
int recv_callback(int fd, int events, void *arg)
{    
}
int send_callback(int fd, int events, void *arg)
{ 
}
int set_events(int fd, int events, void *arg)
{
}

这篇关于7、epoll边沿触发与反应堆的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/192904

相关文章

C++ I/O多路复用 select / poll / epoll

I/O多路复用:在网络I/O中,用 1个或1组线程 管理 多个连接描述符。             如果有至少一个描述符准备就绪,就处理对应的事件             如果没有,就会被阻塞,让出CPU给其他应用程序运行,直到有准备就绪的描述符 或 超时

select、poll、epoll的区别

select、poll、epoll均为linux中的多路复用技术。3种技术出现的顺序是select、poll、epoll,3个版本反应了多路复用技术的迭代过程。我们现在开发网络应用时, 一般都会使用多路复用,很少有用一个线程来监听一个fd的,其中epoll又是最常使用的。关于epoll的实现和常见问题可以参考epoll实现原理和常见问题总结。 当我们在使用epoll的时候,会想当然的认为这种技术

外部中断的边缘触发和电平触发

MCS-51单片机中的边缘触发是指当输入引脚电平由高到低发生跳变时,才引起中断。而电平触发是指只要外部引脚为低电平就引起中断。         在电平触发方式下,当外部引脚的低电平在中断服务返回前没有被拉高时(即撤除中断请求状态),会引起反复的不需要的中断,造成程序执行的错误。这类中断方式下,需要在中断服务程序中设置指令,清除外部中断的低电平状态,使之变为高电平。

ASP.NET手动触发页面验证控件事件

开发环境:.NET Framework 3.5.1 sp1 参考文章: http://www.codeproject.com/KB/aspnet/JavascriptValidation.aspx http://msdn.microsoft.com/zh-cn/library/aa479045.aspx http://www.cnblogs.com/minsentinel/archive/

select poll epoll之间的区别比较

select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核

Flink实例(六十七):自定义时间和窗口的操作符(十二)Flink事件时间何时触发窗口计算

思考:     什么时候才会触发窗口计算?     既然使用的是事件时间那么必然会涉及到水位线(water_mark),水位线在其中扮演的角色是什么?     此时我们带着疑问,一步一步的探究 注意:本篇博客中的所有解释都是在滚动窗口的前提下 Q:为什么要在滚动窗口的前提下进行解释? A:因为滚动窗口相比较滑动和会话来说更容易让大家理解,在本篇博客中着重的是讨论水位线在窗口触发下的场景,

JS触发按键事件

<script type="text/javascript" language=JavaScript charset="UTF-8">document.onkeydown=function(event){var e = event || window.event || arguments.callee.caller.arguments[0];if(e && e.keyCode==27){ // 按

学习记录-Qt按键单击后延迟一段时间触发下一个函数执行

<span style="font-family: Arial, Helvetica, sans-serif;">QTimer::singleShot(1000, this, SLOT(on_pushButton_pcba_readfilename_clicked()));</span>项目中,需要按键单击后发送一条指令,等待一段时间后在发另一条指令,看文档发现使用如上方式可以实现

记录ssl epoll的tcp socket服务端在客户端断开时崩溃的问题

文章目录 当客户端关闭后,Epoll 的 TCP socket 服务端会收到两次断开事件可能有以下原因及解决方法:原因分析解决方法 问题ssl socket服务端代码出错现象第一次尝试修改正确改法附上客户端代码 记录ssl epoll的tcp socket服务端在客户端断开时接收到多次disconnect事件导致崩溃的问题. 流程:在linux服务器上跑socke服务, 客户端连

select、poll、epoll的原理

目录 1.IO多路复用 2.select原理 3.poll原理 4.epoll原理 5.select、poll、epoll总结 6.epoll原理详解 6.1内核收包的过程 6.2进程调度时的阻塞 6.3再来看一下内核收网络数据的过程 6.4select的原理 6.5epoll的设计原理 6.6补充 6.7总结 1.IO多路复用 IO多路复用就是一个线程同时监