【Linux】浅谈eloop机制

news2024/11/18 13:40:20

目录

1.eloop 机制

2.eloop结构体

2.1.eloop_data结构体

2.2 Socket事件结构体

2.3 Timeout事件结构体

2.4 Signal事件结构体

3.eloop_init

4.eloop_run

4.1 signal事件

4.2 socket事件

4.3 timeout事件


1.eloop 机制


        主线程中启动事件监听机制,对不同的事件响应不同的处理函数。即所有的操作都是基于事件驱动的。事件驱动和消息驱动类似,主线程运行一个 event loop,等待事件的发生并处理它们。

2.eloop结构体

2.1.eloop_data结构体

 eloop.c中主要核心结构体为eloop_data,以下是eloop_data结构体:

eloop主要处理三大类型的Event事件:Socket事件,Timeout事件,Signal事件:

2.2 Socket事件结构体

      Socket事件:有 readers,writers,exceptions 三个 eloop_sock_table 结构体,
每个里面都有一个 eloop_sock 类型的指针table,这里可以将该指针变量理解成动态数组,
可以向各个table里面添加、删除 eloop_sock,
事件处理就是遍历 eloop_sock_table,依次运行里面的每个handler。

2.3 Timeout事件结构体

        Timeout事件:每个 struct eloop_timeout 都被放在一个双向链表中dl_list中,
链表头就是 eloop_data 中的“timeout”项。这些struct eloop_timeout按超时先后排序。

2.4 Signal事件结构体

        Signal事件:每个 struct eloop_signal 都通过 eloop_signal 类型的指针链接起来,下面是eloop_signal结构体:

3.eloop_init

  eloop为静态全局变量:

  eloop初始化函数为eloop_init,如下: 

  1.  通过os_memset将eloop结构体清0;
  2. 通过dl_list_init初始化双向链表prev和next指向eloop.timeout;
  3. 初始化eaders,writers,exceptionsSocket事件类型为EVENT_TYPE_READ、EVENT_TYPE_WRITE、EVENT_TYPE_EXCEPTION。

4.eloop_run

         下面结合eloop_run函数来分析eloop机制,部分内容摘自链接: wpa_supplicant之eloop_run分析

先看eloop_run源码:

void eloop_run(void)
{
	fd_set *rfds, *wfds, *efds;
	struct timeval _tv;
	int res;
	struct os_reltime tv, now;

	rfds = os_malloc(sizeof(*rfds));
	wfds = os_malloc(sizeof(*wfds));
	efds = os_malloc(sizeof(*efds));
	if (rfds == NULL || wfds == NULL || efds == NULL)
		goto out;

	while (!eloop.terminate &&
	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
		struct eloop_timeout *timeout;

		if (eloop.pending_terminate) {
			/*
			 * This may happen in some corner cases where a signal
			 * is received during a blocking operation. We need to
			 * process the pending signals and exit if requested to
			 * avoid hitting the SIGALRM limit if the blocking
			 * operation took more than two seconds.
			 */
			eloop_process_pending_signals();
			if (eloop.terminate)
				break;
		}

		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
					list);
		if (timeout) {
			os_get_reltime(&now);
			if (os_reltime_before(&now, &timeout->time))
				os_reltime_sub(&timeout->time, &now, &tv);
			else
				tv.sec = tv.usec = 0;
			_tv.tv_sec = tv.sec;
			_tv.tv_usec = tv.usec;
		}

		eloop_sock_table_set_fds(&eloop.readers, rfds);
		eloop_sock_table_set_fds(&eloop.writers, wfds);
		eloop_sock_table_set_fds(&eloop.exceptions, efds);
		res = select(eloop.max_sock + 1, rfds, wfds, efds,
			     timeout ? &_tv : NULL);
		if (res < 0 && errno != EINTR && errno != 0) {
			wpa_printf(MSG_ERROR, "eloop: %s: %s",
				   "select"

				   , strerror(errno));
			goto out;
		}

		eloop.readers.changed = 0;
		eloop.writers.changed = 0;
		eloop.exceptions.changed = 0;

		eloop_process_pending_signals();

		/* check if some registered timeouts have occurred */
		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
					list);
		if (timeout) {
			os_get_reltime(&now);
			if (!os_reltime_before(&now, &timeout->time)) {
				void *eloop_data = timeout->eloop_data;
				void *user_data = timeout->user_data;
				eloop_timeout_handler handler =
					timeout->handler;
				eloop_remove_timeout(timeout);
				handler(eloop_data, user_data);
			}

		}

		if (res <= 0)
			continue;

		if (eloop.readers.changed ||
		    eloop.writers.changed ||
		    eloop.exceptions.changed) {
			 /*
			  * Sockets may have been closed and reopened with the
			  * same FD in the signal or timeout handlers, so we
			  * must skip the previous results and check again
			  * whether any of the currently registered sockets have
			  * events.
			  */
			continue;
		}

		eloop_sock_table_dispatch(&eloop.readers, rfds);
		eloop_sock_table_dispatch(&eloop.writers, wfds);
		eloop_sock_table_dispatch(&eloop.exceptions, efds);
	}

	eloop.terminate = 0;
out:
	os_free(rfds);
	os_free(wfds);
	os_free(efds);
	return;
}
eloop_run方法中的“死循环”:
	while (!eloop.terminate &&
	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
		eloop.writers.count > 0 || eloop.exceptions.count > 0))

如果eloop.terminate变为非零值,就会退出循环。这是为了提供一种从外部结束循环的方法。例如 void eloop_terminate(void) 方法。
如果eloop.terminate为零,只要timeout链表或者任一个Socket不为空,都会继续循环。

select系统调用的原型是:

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

关于select()函数的相关介绍,可见 select()函数用法

简单来讲:select函数可以同时监视多个文件描述符,并且可以监视三种事件。一旦某个文件描述符所指的对象发生了相应事件,就可以进行相应的处理。

在循环体里面:

(1)找到timeout链表的第一项(因为是按超时先后排序的,所以第一项肯定是最先超时的),计算超时时间距现在还有多久,并据此设置select的timeout参数。

(2)设置rfds,wfds和efds三个fd_set:方法是遍历各个eloop_sock_table,把每个sock描述符加入相应的fd_set里面。

(3)调用select。可能阻塞在此处。最大等待时间取决于“最快会发生的一次”timeout时间 。

(4)eloop_process_pending_signals(); 处理Signal事件。

(5)判断是否有timeout事件“超时”发生,如果是则调用其handler,并从timeout链表移除。然后继续下次循环。

(6)如果不是超时事件,则应该是rfds, wfds或者efds事件,fd_set里面会被改变,存放发生事件的描述符。因此分别遍历三个sock_table,如果其描述符在fd_set里面则调用其handler方法。

(7)继续下次循环。

我按循环中处理事件类型的先后顺序来分析
 

4.1 signal事件

 int eloop_register_signal(int sig, eloop_signal_handler handler,
 			  void *user_data)
 {
 	struct eloop_signal *tmp;
 
 	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
 			       sizeof(struct eloop_signal));
 	if (tmp == NULL)
 		return -1;

	tmp[eloop.signal_count].sig = sig;
	tmp[eloop.signal_count].user_data = user_data;
	tmp[eloop.signal_count].handler = handler;
	tmp[eloop.signal_count].signaled = 0;
	eloop.signal_count++;
	eloop.signals = tmp;
	signal(sig, eloop_handle_signal);

	return 0;
}

        别处调用 eloop_register_signal() 函数注册 signal 时,对应的 eloop_handle_signal 不会立即执行(初注册时将其 signaled置0)
        只有当 signal 发生时才会将此 signal 的 signaled加1,表示此类 signal 已收到并处于 pending 状态(如何做到的呢?见 signal(sig, eloop_handle_signal); eloop_handle_signal 会将此 signal 的 signaled加1且eloop_data的signaled加1)
       signal()函数用法见 signal()用法

      在select()超时或者有Socket事件方式时才会顺便调用eloop_process_pending_signals(), 对每个处于Pending状态的struct eloop_signal调用其handler。

4.2 socket事件


        在循环期间,外部调用 eloop_sock_table_add_sock() 函数往 eloop_sock_table 中添加 eloop_sock
eloop_sock_table_set_fds() 函数将 rfds/wfds/efds 对应 sock位用 FD_SET 置1
而后调用 select() 函数

        select()返回值:发生错误时返回-1,超时时返回0。如果发生监视的事件,返回相应的文件描述符。
        如果 select() 返回结果大于0,说明有socket事件发生,则调用 eloop_sock_table_dispatch() 依次处理三个 table(遍历三个table中的eloop_sock,调用其handler)

4.3 timeout事件

        eloop_date中维护一个 timeout 链表,按超时先后排序,依次处理

读者可自行把 eloop.c 和 eloop.h 中的一些 tool function读完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/492579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python入门】字符串的扩展

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于Python零基础入门系列&#xff0c;本专栏主要内容为Python基础语法、判断、循环语句、函…

Nginx介绍及安装

简介 Nginx 是一个高性能的 HTTP 和反向代理服务器。它最初由 Nigel Cook 开发&#xff0c;旨在解决 Apache 服务器在高并发环境下性能瓶颈的问题。Nginx 具有占用资源少、处理能力强等优点&#xff0c;在互联网应用中广泛应用于静态资源服务、反向代理、负载均衡、HTTP缓存、…

2023年web前端开发之JavaScript进阶(一)

接上篇博客进行学习,通俗易懂,详细 博客地址: 2023年web前端开发之JavaScript基础(五)基础完结_努力的小周同学的博客-CSDN博客 学习内容 学习 作用域、变量提升、 闭包等语言特征&#xff0c;加深对 JavaScript 的理解&#xff0c;掌握变量赋值、函数声明的简洁语法&#xff0…

rs485转tcp网关盒子怎么用(rs485协议转以太网tcp/ip)

随着工业自动化技术的不断发展&#xff0c;越来越多的工业设备在使用时需要进行数据通信。其中&#xff0c;RS485通信协议是一种常见的工业通信协议&#xff0c;而TCP/IP协议则是互联网通信的标准协议。为了实现RS485协议与TCP/IP协议之间的通信&#xff0c;可以使用RS485转TCP…

【Java】面试常问知识点(Java基础—2)

Java基础 多线程的状态 新建状态 当用new操作符创建一个线程时&#xff0c; 例如new Thread(r)&#xff0c;线程还没有开始运行&#xff0c;此时线程处在新建状态。 当一个线程处于新生状态时&#xff0c;程序还没有开始运行线程中的代码 就绪状态 一个新创建的线程并不自动…

ChatGLM-6B微调与部署

文章目录 基于ChatGLM-6B的推理与部署配置环境与准备配置环境模型文件准备 代码运行 Demo命令行 Demo基于 Gradio 的网页版 Demo基于 Streamlit 的网页版 Demo 基于peft框架的LoRA微调ChatGLM-6B配置环境与准备配置环境模型文件准备数据准备数据处理 微调过程 基于P-Tuning v2微…

stm32影子寄存器、预装载寄存器,TIM_OC1PreloadConfig和TIM_ARRPreloadConfig的作用

一直没搞清楚stm32定时器的TIM_OC1PreloadConfig、TIM_ARRPreloadConfig函数的作用&#xff0c;影子寄存器、预装载寄存器、重载寄存器的概念。今天来研究一下&#xff1a; 首先看定时器的框图&#xff1a; 图中有阴影的小方框&#xff0c;代表该功能对应的寄存器有影子寄存器&…

Canal实战使用(集群部署)和原理解析

1.mysql数据同步工作原理 MySQL master将数据变更写入二进制日志(binary log&#xff0c;其中记录叫做二进制日志事件binary log events&#xff0c;可以通过 show binlog events 进行查看) MySQL slave将master的binary log events拷贝到它的中继日志(relay log) MySQL slav…

Java基础(十八)网络编程

1. 网络编程概述 Java是 Internet 上的语言&#xff0c;它从语言级上提供了对网络应用程序的支持&#xff0c;程序员能够很容易开发常见的网络应用程序。 Java提供的网络类库&#xff0c;可以实现无痛的网络连接&#xff0c;联网的底层细节被隐藏在 Java 的本机安装系统里&am…

软件测试培训了几个月,找到工作了,面试经验分享给各位

面试问的一些基本问题 功能方面&#xff1a;问的最多的就是测试流程&#xff0c;测试计划包含哪些内容&#xff0c;公司人员配置&#xff0c;有bug开发认为不是 bug怎么处理&#xff0c;怎样才算是好的用例&#xff0c;测试用例设计方法&#xff08;等价类&#xff0c;边界值等…

无线之红外线技术的组网方式详解

红外线(Infrared rays)也是一种光线&#xff0c;由于它的波长比红色光750nm)还长&#xff0c;超出了人眼可以识别的可见光&#xff09;范围&#xff0c;所以我们看不见它&#xff0c;又称为红外热辐射(Infrared radiation)&#xff0c;通常把波长为0.75&#xff5e;1000μm的光…

腾讯高工手写13W字“Netty速成手册”,3天走向实战

前言 在java界&#xff0c;netty无疑是开发网络应用的拿手菜。你不需要太多关注复杂的nio模型和底层网络的细节&#xff0c;使用其丰富的接口&#xff0c;可以很容易的实现复杂的通讯功能。 作为当前最流行的NIO框架&#xff0c;Netty在互联网领域、大数据分布式计算领域、游…

【Linux0.11代码分析】02 之 bootsect.s 启动流程

【Linux0.11代码分析】02 之 bootsect.s 启动流程 一、boot\bootsect.s1.1 将bootsect.s 从0x7c00 移动到 0x90000 &#xff08;512byte&#xff09;1.2 使用 int 0x13 中断加载 setup.s 程序到 0x902001.3 获取并解析磁盘驱动器的参数1.4 开始加载 System模块到 0x10000 地址1…

计算机网络之应用层

文章目录 应用层1. 应用层协议原理1.1 网络应用程序体系结构1.2 进程通信1.3 可供程序使用的运输服务1.4 因特网提供的运输服务1.5 应用层协议 2.Web应用和HTTP协议2.1 HTTP概况2.2 非持久连接和持久连接2.3 HTTP报文格式2.4 用户服务器的交互&#xff1a;cookie2.5 Web缓存器 …

数字医疗算法应用创新大赛-食品与疾病关系预测算法赛道top5方案分享

一、比赛地址和背景 1.1 比赛地址 比赛地址 1.2 数据说明及任务 本次算法赛将提供超过 23.5W 的食物、疾病对应关系及其量化得分&#xff0c;其中食物特征超过 200 个&#xff0c;疾病特征由 3 种不同的方式抽取&#xff0c;累积超过 4000 个特征信息。初赛为 0、1 二分类预…

递归递推练习题

1.用递归的方法123…N的值&#xff08;in:5,out:15&#xff09; 2.输出斐波那契数列的第N项&#xff0c;0&#xff0c;1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13…(in:3,out:1) 3.求n!&#xff08;in:5,out:120&#xff09;n!123……

STM32启动/下载模式

启动模式 主闪存存储器区存放用户设计的代码&#xff0c;代码烧录到这里&#xff0c;一般情况在正常工作时需要把STM32设置为从这里启动。 系统存储器区用来实现ISP功能的。 内嵌SRAM区用来实现调试器调试功能的。 ISP ISP一般由PC机通过串口把bin/hex文件直接烧写到单片机内…

java并发编程之美第五章读书笔记

java并发包中并发List源码剖析 介绍 CopyOnWriteArrayList 线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)进行的,也就是写时复制策略 类图 每一个对象里面有一个array数组进行存放具体的元素,ReentrantLock独占锁对象用来保证同时只有一个线程对…

Maven中optional标签详解

这一篇文章应该是全网讲解optional最细致的&#xff0c;因为我都是拿我自己的试验来证明&#xff0c;从试验当中也发现了很多出乎意外的知识&#xff01;感兴趣的跟着小编一块来学习呀&#xff01; 目录 一、前言二、依赖传递代码演示三、是否会影响父子工程之间的依赖继承呢&a…

精致女孩必备的6款APP,内外兼修,提升气质

自立自强、敢于追求、内外兼修&#xff0c;这样的女孩不管在哪个时代都是非常有魅力的&#xff01; 在我看来&#xff0c;自律、上进、情商高、会打扮、会赚钱、独立、落落大方&#xff0c;这才是精致女孩该有的样子。 不墨迹&#xff0c;速速揭开6款宝藏APP的面纱&#xff0…