代驾小程序定制开发/seo搜索引擎优化求职简历
从一万英尺外看libevent
温馨提示:阅读时间大概二十分钟
前言
Libevent是用于编写高速可移植非阻塞IO应用的库,其设计目标是:
- 可移植性:使用libevent编写的程序应该可以在libevent支持的所有平台上工作。即使没有好的方式进行非阻塞IO,libevent也应该支持一般的方式,让程序可以在受限的环境中运行
- 速度:libevent尝试使用每个平台上最高速的非阻塞IO实现,并且不引入太多的额外开销。
- 可扩展性:libevent被设计为程序即使需要上万个活动套接字的时候也可以良好工作。
- 方便:无论何时,最自然的使用libevent编写程序的方式应该是稳定的、可移植的。(Libevent should compile on Linux, *BSD, Mac OS X, Solaris, Windows, and more.)
libevent由下列组件构成:
-
evutil:用于抽象不同平台网络实现差异的通用功能。
-
event和event_base:libevent的核心,为各种平台特定的、基于事件的非阻塞IO后端提供抽象API,让程序可以知道套接字何时已经准备好,可以读或者写,并且处理基本的超时功能,检测OS信号。
-
bufferevent:为libevent基于事件的核心提供使用更方便的封装。除了通知程序套接字已经准备好读写之外,还让程序可以请求缓冲的读写操作,可以知道何时IO已经真正发生。(bufferevent接口有多个后端,可以采用系统能够提供的更快的非阻塞IO方式,如Windows中的IOCP。)
-
evbuffer:在bufferevent层之下实现了缓冲功能,并且提供了方便有效的访问函数。
-
evhttp:一个简单的HTTP客户端/服务器实现。
-
evdns:一个简单的DNS客户端/服务器实现。
-
evrpc:一个简单的RPC实现。
-
libevent API提供了一种机制,可以在文件描述符上发生特定事件或达到超时后执行回调函数。此外,libevent还支持由于信号或常规超时而产生的回调。
-
Libevent旨在取代事件驱动网络服务器中的事件循环。应用程序只需要调用event_dispatch(),然后动态地添加或删除事件,而不必更改事件循环。
-
Libevent还为缓冲网络IO提供了一个复杂的框架,支持套接字、过滤器、速率限制、SSL、零拷贝文件传输和IOCP。Libevent支持几种有用的协议,包括DNS、HTTP和一个最小的RPC框架。
库
创建libevent时,默认安装下列库:
-
libevent_core:所有核心的事件和缓冲功能,包含了所有的event_base、evbuffer、bufferevent和工具函数。
-
libevent_extra:定义了程序可能需要,也可能不需要的协议特定功能,包括HTTP、DNS和RPC。
-
libevent:这个库因为历史原因而存在,它包含libevent_core和libevent_extra的内容。不应该使用这个库,未来版本的libevent可能去掉这个库。
某些平台上可能安装下列库:
- libevent_openssl:这个库为使用bufferevent和OpenSSL进行加密的通信提供支持。它独立于libevent_core,这样程序使用libevent时就不需要链接到OpenSSL,除非是进行加密通信。
- ibevent_pthreads:添加基于pthread可移植线程库的线程和锁定实现。它独立于libevent_core,这样程序使用libevent时就不需要链接到pthread,除非是以多线程方式使用libevent
头文件
libevent公用头文件都安装在event2目录中,分为三类:
- API头文件:定义libevent公用接口。这类头文件没有特定后缀。
- 兼容头文件:为已废弃的函数提供兼容的头部包含定义。不应该使用这类头文件,除非是在移植使用较老版本libevent的程序时。
- 结构头文件:这类头文件以相对不稳定的布局定义各种结构体。这些结构体中的一些是为了提供快速访问而暴露;一些是因为历史原因而暴露。直接依赖这类头文件中的任何结构体都会破坏程序对其他版本libevent的二进制兼容性,有时候是以非常难以调试的方式出现。这类头文件具有后缀“_struct.h”。
如何使用libevent
使用libevent函数之前需要分配一个或者多个``event_base结构体,每个
event_base`持有一个事件集合,可以检测哪个事件是激活(可读写)的。
如果设置event_base
使用锁,则可以安全地在多个线程中访问它。然而,其事件循环只能运行在一个线程中。如果需要用多个线程检测IO,则需要为每个线程使用一个event_base
。
每个event_base都有一种用于检测哪种事件已经就绪的“方法”.
- select
- poll
- epoll
- kqueue
- devpool
- evport
- win32
用户可以用环境变量禁止某些特定的后端。比如说,要禁止kqueue后端,可以设置EVENT_NOKQUEUE环境变量。如果要用编程的方法禁止后端,看关于event_config_avoid_method()的说明。
建立默认的event_base
event_base_new()
函数分配并且返回一个新的具有默认设置的event_base。函数会检测环境变量,返回一个到event_base的指针。如果发生错误,则返回NULL。选择各种方法时,函数会选择OS支持的最快方法。
event_base_new(void)
event_base_new()
函数声明在<event2/event.h>
中
struct event_base * event_base_new(void)
{struct event_base *base = NULL;//配置configstruct event_config *cfg = event_config_new();if (cfg) {base = event_base_new_with_config(cfg);event_config_free(cfg);}return base;
}
创建复杂的event_base()
要对取得什么类型的event_base有更多的控制,就需要使用event_config。event_config是一个容纳event_base配置信息的不透明结构体。需要event_base时,将event_config传递给event_base_new_with_config()。
event_config_new()
#define INT_MAX __INT_MAX__
//在 C/C++ 中用于表示 int 类型所能表示的最大值。它是一个编译器常量,值为 2147483647(0x7ffffffff)struct event_config * event_config_new(void)
{struct event_config *cfg = mm_calloc(1, sizeof(*cfg));if (cfg == NULL)return (NULL);TAILQ_INIT(&cfg->entries);cfg->max_dispatch_interval.tv_sec = -1;cfg->max_dispatch_callbacks = INT_MAX;cfg->limit_callbacks_after_prio = 1;return (cfg);
}
/** Tail queue functions.* 尾队列的头结点初始化为空队列。*/
#define TAILQ_INIT(head) do { \(head)->tqh_first = NULL; \(head)->tqh_last = &(head)->tqh_first; \
} while (/*CONSTCOND*/0)
要使用这些函数分配event_base
,先调用event_config_new()
分配一个event_config
。然后,对event_config
调用其它函数,设置所需要的event_base
特征。最后,调用event_base_new_with_config()
获取新的event_base
。完成工作后,使用event_config_free()
释放event_config
。
event_base_new_with_config
这个函数在new_event_base被调用,传入的参数是event_config,根据cfg的内容来配置event_base
struct event_base * event_base_new_with_config(const struct event_config *cfg)
{int i;struct event_base *base;int should_check_environment;#ifndef EVENT__DISABLE_DEBUG_MODEevent_debug_mode_too_late = 1;
#endifif ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) { //安全分配内存event_warn("%s: calloc", __func__);return NULL;}if (cfg)base->flags = cfg->flags;should_check_environment =!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));{struct timeval tmp;int precise_time =cfg && (cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER);int flags;if (should_check_environment && !precise_time) {precise_time = evutil_getenv_("EVENT_PRECISE_TIMER") != NULL;if (precise_time) {base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER;}}flags = precise_time ? EV_MONOT_PRECISE : 0;evutil_configure_monotonic_time_(&base->monotonic_timer, flags);gettime(base, &tmp);}min_heap_ctor_(&base->timeheap);base->sig.ev_signal_pair[0] = -1;base->sig.ev_signal_pair[1] = -1;base->th_notify_fd[0] = -1;base->th_notify_fd[1] = -1;TAILQ_INIT(&base->active_later_queue);evmap_io_initmap_(&base->io);evmap_signal_initmap_(&base->sigmap);event_changelist_init_(&base->changelist);base->evbase = NULL;if (cfg) {memcpy(&base->max_dispatch_time,&cfg->max_dispatch_interval, sizeof(struct timeval));base->limit_callbacks_after_prio =cfg->limit_callbacks_after_prio;} else {base->max_dispatch_time.tv_sec = -1;base->limit_callbacks_after_prio = 1;}if (cfg && cfg->max_dispatch_callbacks >= 0) {base->max_dispatch_callbacks = cfg->max_dispatch_callbacks;} else {base->max_dispatch_callbacks = INT_MAX;}if (base->max_dispatch_callbacks == INT_MAX &&base->max_dispatch_time.tv_sec == -1)base->limit_callbacks_after_prio = INT_MAX;for (i = 0; eventops[i] && !base->evbase; i++) {if (cfg != NULL) {/* determine if this backend should be avoided */if (event_config_is_avoided_method(cfg,eventops[i]->name))continue;if ((eventops[i]->features & cfg->require_features)!= cfg->require_features)continue;}/* also obey the environment variables */if (should_check_environment &&event_is_method_disabled(eventops[i]->name))continue;base->evsel = eventops[i];base->evbase = base->evsel->init(base);}if (base->evbase == NULL) {event_warnx("%s: no event mechanism available",__func__);base->evsel = NULL;event_base_free(base);return NULL;}if (evutil_getenv_("EVENT_SHOW_METHOD"))event_msgx("libevent using: %s", base->evsel->name);/* allocate a single active event queue */if (event_base_priority_init(base, 1) < 0) {event_base_free(base);return NULL;}/* prepare for threading */#if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE)event_debug_created_threadable_ctx_ = 1;
#endif#ifndef EVENT__DISABLE_THREAD_SUPPORTif (EVTHREAD_LOCKING_ENABLED() &&(!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) {int r;EVTHREAD_ALLOC_LOCK(base->th_base_lock, 0);EVTHREAD_ALLOC_COND(base->current_event_cond);r = evthread_make_base_notifiable(base);if (r<0) {event_warnx("%s: Unable to make base notifiable.", __func__);event_base_free(base);return NULL;}}
#endif#ifdef _WIN32if (cfg && (cfg->flags & EVENT_BASE_FLAG_STARTUP_IOCP))event_base_start_iocp_(base, cfg->n_cpus_hint);
#endifreturn (base);
}
event_config_free
//用来释放config
void event_config_free(struct event_config *cfg)
{struct event_config_entry *entry;while ((entry = TAILQ_FIRST(&cfg->entries)) != NULL) {TAILQ_REMOVE(&cfg->entries, entry, next);event_config_entry_free(entry);}mm_free(cfg);
}
event_config_avoid_method()
int event_config_avoid_method(struct event_config *cfg, const char *method)
{struct event_config_entry *entry = mm_malloc(sizeof(*entry));if (entry == NULL)return (-1);if ((entry->avoid_method = mm_strdup(method)) == NULL) {mm_free(entry);return (-1);}TAILQ_INSERT_TAIL(&cfg->entries, entry, next);return (0);
}
调用event_config_avoid_method()
可以通过名字让libevent避免使用特定的可用后端。调用event_config_require_feature()
让libevent不使用不能提供所有指定特征的后端。调用event_config_set_flag()
让libevent在创建event_base
时设置一个或者多个将在下面介绍的运行时标志。
event_config_require_feature()
int event_config_require_features(struct event_config *cfg,int features)
{if (!cfg)return (-1);cfg->require_features = features;return (0);
}
EV_FEATURE_ET:要求支持边沿触发的后端
EV_FEATURE_O1:要求添加、删除单个事件,或者确定哪个事件激活的操作是O(1)复杂度的后端
EV_FEATURE_FDS:要求支持任意文件描述符,而不仅仅是套接字的后端
event_config_set_flag
int event_config_set_flag(struct event_config *cfg, int flag)
{if (!cfg)return -1;cfg->flags |= flag;return 0;
}
-
EVENT_BASE_FLAG_NOLOCK:不要为event_base分配锁。设置这个选项可以为event_base节省一点用于锁定和解锁的时间,但是让在多个线程中访问event_base成为不安全的。
-
EVENT_BASE_FLAG_IGNORE_ENV:选择使用的后端时,不要检测EVENT_*环境变量。使用这个标志需要三思:这会让用户更难调试你的程序与libevent的交互。
-
EVENT_BASE_FLAG_STARTUP_IOCP:仅用于Windows,让libevent在启动时就启用任何必需的IOCP分发逻辑,而不是按需启用。
-
EVENT_BASE_FLAG_NO_CACHE_TIME:不是在事件循环每次准备执行超时回调时检测当前时间,而是在每次超时回调后进行检测。注意:这会消耗更多的CPU时间。
-
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST:告诉libevent,如果决定使用epoll后端,可以安全地使用更快的基于changelist的后端。epoll-changelist后端可以在后端的分发函数调用之间,同样的fd多次修改其状态的情况下,避免不必要的系统调用。但是如果传递任何使用dup()或者其变体克隆的fd给libevent,epoll-changelist后端会触发一个内核bug,导致不正确的结果。在不使用epoll后端的情况下,这个标志是没有效果的。也可以通过设置EVENT_EPOLL_USE_CHANGELIST环境变量来打开epoll-changelist选项。
上述操作event_config的函数都在成功时返回0,失败时返回-1。
注意
设置event_config,请求OS不能提供的后端是很容易的。比如说,对于libevent 2.0.1-alpha,在Windows中是没有O(1)后端的;在Linux中也没有同时提供EV_FEATURE_FDS和EV_FEATURE_O1特征的后端。如果创建了libevent不能满足的配置,event_base_new_with_config()会返回NULL。
event_config_set_num_cpus_hint()
int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)
{if (!cfg)return (-1);cfg->n_cpus_hint = cpus;return (0);
}
这个函数当前仅在Windows上使用IOCP时有用,虽然将来可能在其他平台上有用。这个函数告诉event_config在生成多线程event_base的时候,应该试图使用给定数目的CPU。注意这仅仅是一个提示:event_base使用的CPU可能比你选择的要少。
这些函数和类型在<event2/event.h>中声明。
EVENT_BASE_FLAG_IGNORE_ENV标志首次出现在2.0.2-alpha版本。event_config_set_num_cpus_hint()函数是2.0.7-rc版本新引入的。检查event_base
的后端方法
event_get_supported_methods
const char ** event_get_supported_methods(void)
{static const char **methods = NULL;const struct eventop **method;const char **tmp;int i = 0, k;/* count all methods */for (method = &eventops[0]; *method != NULL; ++method) {++i;}/* allocate one more than we need for the NULL pointer */tmp = mm_calloc((i + 1), sizeof(char *));if (tmp == NULL)return (NULL);/* populate the array with the supported methods */for (k = 0, i = 0; eventops[k] != NULL; ++k) {tmp[i++] = eventops[k]->name;}tmp[i] = NULL;if (methods != NULL)mm_free((char**)methods);methods = tmp;return (methods);
}
event_get_supported_methods()函数返回一个指针,指向libevent支持的方法名字数组。这个数组的最后一个元素是NULL。
注意
这个函数返回libevent被编译以支持的方法列表。然而libevent运行的时候,操作系统可能不能支持所有方法。比如说,可能OS X版本中的kqueue的bug太多,无法使用。
event_base_get_method
const char * event_base_get_method(const struct event_base *base)
{EVUTIL_ASSERT(base);//检查给定的条件 cond 是否满足return (base->evsel->name);
}
#define EVUTIL_ASSERT(cond) \do { \if (EVUTIL_UNLIKELY(!(cond))) { \event_errx(EVENT_ERR_ABORT_, \"%s:%d: Assertion %s failed in %s", \__FILE__,__LINE__,#cond,__func__); \/* In case a user-supplied handler tries to */ \/* return control to us, log and abort here. */ \(void)fprintf(stderr, \"%s:%d: Assertion %s failed in %s", \__FILE__,__LINE__,#cond,__func__); \abort(); \} \} while (0)
void event_errx(int eval, const char *fmt, ...)
{va_list ap;va_start(ap, fmt);event_logv_(EVENT_LOG_ERR, NULL, fmt, ap);va_end(ap);event_exit(eval);
}
释放event_base
使用完event_base
之后,使用event_base_free
()进行释放。
event_base_free()
void
event_base_free(struct event_base *base)
{event_base_free_(base, 1);
}
参数 base
是指向需要释放的 event_base
结构的指针,run_finalizers
是一个标志,用于指示是否需要运行终结器。
static void
event_base_free_(struct event_base *base, int run_finalizers)
{int i, n_deleted=0;struct event *ev;/* XXXX grab the lock? If there is contention when one thread frees* the base, then the contending thread will be very sad soon. *//* 这段注释表明,在一个线程释放资源(通常指的是一个共享的基础对象或结构)时,可能需要获取一个锁(lock)来防止其他线程同时 访问该资源。如果不加锁,而另一个线程尝试访问同一个资源,就会发生竞争(contention)。竞争会导致程序不正确的行为,甚至崩 溃。*//* event_base_free(NULL) is how to free the current_base if we* made it with event_init and forgot to hold a reference to it. *//*这段注释解释了如何释放当前的基础对象。如果您使用 event_init 函数创建了一个基础对象,但忘记了保存对它的引用,那么可以通 过调用 event_base_free(NULL) 来释放它。这表明 event_base_free 函数允许一个 NULL 参数,这种情况下,它将释放当前 上下文中的基础对象。*/if (base == NULL && current_base)base = current_base;/* Don't actually free NULL. */if (base == NULL) {event_warnx("%s: no base to free", __func__);return;}/* XXX(niels) - check for internal events first */#ifdef _WIN32event_base_stop_iocp_(base);
#endif/* threading fds if we have them */if (base->th_notify_fd[0] != -1) {event_del(&base->th_notify);EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);if (base->th_notify_fd[1] != -1)EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);base->th_notify_fd[0] = -1;base->th_notify_fd[1] = -1;event_debug_unassign(&base->th_notify);}/* Delete all non-internal events. */evmap_delete_all_(base);while ((ev = min_heap_top_(&base->timeheap)) != NULL) {event_del(ev);++n_deleted;}for (i = 0; i < base->n_common_timeouts; ++i) {struct common_timeout_list *ctl =base->common_timeout_queues[i];event_del(&ctl->timeout_event); /* Internal; doesn't count */event_debug_unassign(&ctl->timeout_event);for (ev = TAILQ_FIRST(&ctl->events); ev; ) {struct event *next = TAILQ_NEXT(ev,ev_timeout_pos.ev_next_with_common_timeout);if (!(ev->ev_flags & EVLIST_INTERNAL)) {event_del(ev);++n_deleted;}ev = next;}mm_free(ctl);}if (base->common_timeout_queues)mm_free(base->common_timeout_queues);for (;;) {/* For finalizers we can register yet another finalizer out from* finalizer, and iff finalizer will be in active_later_queue we can* add finalizer to activequeues, and we will have events in* activequeues after this function returns, which is not what we want* (we even have an assertion for this).** A simple case is bufferevent with underlying (i.e. filters).*/int i = event_base_free_queues_(base, run_finalizers);event_debug(("%s: %d events freed", __func__, i));if (!i) {break;}n_deleted += i;}if (n_deleted)event_debug(("%s: %d events were still set in base",__func__, n_deleted));while (LIST_FIRST(&base->once_events)) {struct event_once *eonce = LIST_FIRST(&base->once_events);LIST_REMOVE(eonce, next_once);mm_free(eonce);}if (base->evsel != NULL && base->evsel->dealloc != NULL)base->evsel->dealloc(base);for (i = 0; i < base->nactivequeues; ++i)EVUTIL_ASSERT(TAILQ_EMPTY(&base->activequeues[i]));EVUTIL_ASSERT(min_heap_empty_(&base->timeheap));min_heap_dtor_(&base->timeheap);mm_free(base->activequeues);evmap_io_clear_(&base->io);evmap_signal_clear_(&base->sigmap);event_changelist_freemem_(&base->changelist);EVTHREAD_FREE_LOCK(base->th_base_lock, 0);EVTHREAD_FREE_COND(base->current_event_cond);/* If we're freeing current_base, there won't be a current_base. */if (base == current_base)current_base = NULL;mm_free(base);
}
主要代码逻辑
/* Global state; deprecated */
//全局的event_base current_base(event_global_current_base)
EVENT2_EXPORT_SYMBOL
struct event_base *event_global_current_base_ = NULL;
#define current_base event_global_current_base_
-
处理
NULL
基础对象: 如果base
是NULL
并且current_base
存在,那么将base
设置为current_base
。if (base == NULL && current_base)base = current_base;d if (base == NULL) {event_warnx("%s: no base to free", __func__);return; }
-
停止 Windows 特定的 IOCP 处理(仅在 Windows 平台上有效):
#ifdef _WIN32 event_base_stop_iocp_(base);//此处不做讨论 #endif
-
处理线程通知文件描述符: 删除线程通知事件并关闭文件描述符。
if (base->th_notify_fd[0] != -1) {event_del(&base->th_notify);EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);if (base->th_notify_fd[1] != -1)EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);base->th_notify_fd[0] = -1;base->th_notify_fd[1] = -1;event_debug_unassign(&base->th_notify); }
-
删除所有非内部事件: 删除时间堆中的所有事件,并释放公共超时队列中的事件。
evmap_delete_all_(base); while ((ev = min_heap_top_(&base->timeheap)) != NULL) {event_del(ev);++n_deleted; } for (i = 0; i < base->n_common_timeouts; ++i) {struct common_timeout_list *ctl = base->common_timeout_queues[i];event_del(&ctl->timeout_event); // Internal; doesn't countevent_debug_unassign(&ctl->timeout_event);for (ev = TAILQ_FIRST(&ctl->events); ev; ) {struct event *next = TAILQ_NEXT(ev, ev_timeout_pos.ev_next_with_common_timeout);if (!(ev->ev_flags & EVLIST_INTERNAL)) {event_del(ev);++n_deleted;}ev = next;}mm_free(ctl); } if (base->common_timeout_queues)mm_free(base->common_timeout_queues);
-
释放一次性事件: 释放
once_events
列表中的事件。while (LIST_FIRST(&base->once_events)) {struct event_once *eonce = LIST_FIRST(&base->once_events);LIST_REMOVE(eonce, next_once);mm_free(eonce); }
-
释放选择器和锁: 释放事件选择器和相关的锁与条件变量。
if (base->evsel != NULL && base->evsel->dealloc != NULL)base->evsel->dealloc(base);for (i = 0; i < base->nactivequeues; ++i)EVUTIL_ASSERT(TAILQ_EMPTY(&base->activequeues[i]));EVUTIL_ASSERT(min_heap_empty_(&base->timeheap)); min_heap_dtor_(&base->timeheap);mm_free(base->activequeues);evmap_io_clear_(&base->io); evmap_signal_clear_(&base->sigmap); event_changelist_freemem_(&base->changelist);EVTHREAD_FREE_LOCK(base->th_base_lock, 0); EVTHREAD_FREE_COND(base->current_event_cond);
-
释放基础对象: 如果正在释放
current_base
,则将其设置为NULL
,并最终释放base
。if (base == current_base)current_base = NULL; mm_free(base);
总结
这个函数通过清理和释放所有相关资源和内存来销毁一个 event_base
对象。注释中提到的竞争条件提醒开发者在多线程环境中小心处理共享资源,确保程序的稳定性和正确性。
设置event_base
的优先级
libevent支持为事件设置多个优先级。然而,event_base默认只支持单个优先级。可以调用event_base_priority_init()设置event_base的优先级数目。
int event_base_priority_init(struct event_base *base, int npriorities)
成功时这个函数返回0,失败时返回-1。base是要修改的event_base,n_priorities是要支持的优先级数目,这个数目至少是1。每个新的事件可用的优先级将从0(最高)到n_priorities-1(最低)。
常量EVENT_MAX_PRIORITIES表示n_priorities的上限。调用这个函数时为n_priorities给出更大的值是错误的。
注意
必须在任何事件激活之前调用这个函数,最好在创建event_base后立刻调用。
示例
关于示例,请看event_priority_set
的文档·。
默认情况下,与event_base相关联的事件将被初始化为具有优先级n_priorities / 2
。event_base_priority_init()
函数定义在<event2/event.h>
中,从libevent 1.0版就可用了。
*这个宏用于获取event_base
*的锁(如果有support)
/** Lock an event_base, if it is set up for locking. Acquires the lockin the base structure whose field is named 'lockvar'. */
#define EVBASE_ACQUIRE_LOCK(base, lockvar) do { \EVLOCK_LOCK((base)->lockvar, 0); \} while (0)
#define N_ACTIVE_CALLBACKS(base) \((base)->event_count_active) //最大事件数量
/** Largest number of priorities that Libevent can support. */
#define EVENT_MAX_PRIORITIES 256
/**Set the number of different event prioritiesBy default Libevent schedules all active events with the same priority.However, some time it is desirable to process some events with a higherpriority than others. For that reason, Libevent supports strict priorityqueues. Active events with a lower priority are always processed beforeevents with a higher priority.The number of different priorities can be set initially with theevent_base_priority_init() function. This function should be calledbefore the first call to event_base_dispatch(). Theevent_priority_set() function can be used to assign a priority to anevent. By default, Libevent assigns the middle priority to all eventsunless their priority is explicitly set.Note that urgent-priority events can starve less-urgent events: afterrunning all urgent-priority callbacks, Libevent checks for more urgentevents again, before running less-urgent events. Less-urgent eventswill not have their callbacks run until there are no events more urgentthan them that want to be active.@param eb the event_base structure returned by event_base_new()@param npriorities the maximum number of priorities@return 0 if successful, or -1 if an error occurred@see event_priority_set()*/
int event_base_priority_init(struct event_base *base, int npriorities)
{int i, r;r = -1; //用于指示返回值的状态 //在多线程环境中,需要获取锁以确保对 base 的修改是线程安全的。EVBASE_ACQUIRE_LOCK(base, th_base_lock);//展开类似 EVLOCK_LOCK((base)->th_base_lock, 0);//参数和状态检查if (N_ACTIVE_CALLBACKS(base) || npriorities < 1|| npriorities >= EVENT_MAX_PRIORITIES)goto err;//检查是否需要重新分配队列 if (npriorities == base->nactivequeues)goto ok;if (base->nactivequeues) {mm_free(base->activequeues);base->nactivequeues = 0;}/* Allocate our priority queues *///分配新的优先级队列base->activequeues = (struct evcallback_list *)mm_calloc(npriorities, sizeof(struct evcallback_list));if (base->activequeues == NULL) {event_warn("%s: calloc", __func__);goto err;}base->nactivequeues = npriorities;for (i = 0; i < base->nactivequeues; ++i) {TAILQ_INIT(&base->activequeues[i]);}ok:r = 0;
err:EVBASE_RELEASE_LOCK(base, th_base_lock);return (r);
}
总结
这个函数的主要目的是为事件基础结构
base
初始化或重新初始化优先级队列。它通过以下步骤实现:
- 获取锁以确保线程安全。
- 检查输入参数和当前状态。
- 如果需要,释放现有的优先级队列。
- 分配并初始化新的优先级队列。
- 设置返回状态并释放锁。
通过这些步骤,确保
event_base
可以正确管理不同优先级的事件。
libevent
的内存分配与管理
内存分配的相关宏定义
#define mm_malloc(sz) event_mm_malloc_(sz)
#define mm_calloc(count, size) event_mm_calloc_((count), (size))
#define mm_strdup(s) event_mm_strdup_(s)
#define mm_realloc(p, sz) event_mm_realloc_((p), (sz))
#define mm_free(p) event_mm_free_(p)
//#ifdef EVENT__DISABLE_MM_REPLACEMENT 下
#define mm_malloc(sz) malloc(sz)
#define mm_calloc(n, sz) calloc((n), (sz))
#define mm_strdup(s) strdup(s)
#define mm_realloc(p, sz) realloc((p), (sz))
#define mm_free(p) free(p)
static void *(*mm_malloc_fn_)(size_t sz) = NULL;
static void *(*mm_realloc_fn_)(void *p, size_t sz) = NULL;
static void (*mm_free_fn_)(void *p) = NULL;
mm_realloc
void * event_mm_realloc_(void *ptr, size_t sz)
{if (mm_realloc_fn_)return mm_realloc_fn_(ptr, sz);elsereturn realloc(ptr, sz);
}
mm_strdup
char * event_mm_strdup_(const char *str)
{if (!str) {errno = EINVAL;return NULL;}if (mm_malloc_fn_) {size_t ln = strlen(str);void *p = NULL;if (ln == EV_SIZE_MAX)goto error;p = mm_malloc_fn_(ln+1);if (p)return memcpy(p, str, ln+1);} else//不同平台下的函数不一样
#ifdef _WIN32return _strdup(str);
#elsereturn strdup(str);
#endiferror:errno = ENOMEM;return NULL;
}
mm_malloc
void * event_mm_malloc_(size_t sz)
{if (sz == 0)return NULL;if (mm_malloc_fn_)return mm_malloc_fn_(sz);elsereturn malloc(sz);
}
mm_free()
#define mm_free(p) event_mm_free_(p)static void (*mm_free_fn_)(void *p) = NULL;//指向释放内存的函数
void event_mm_free_(void *ptr)
{if (mm_free_fn_)mm_free_fn_(ptr);elsefree(ptr);
}
mm_calloc()
#ifndef EVENT__DISABLE_MM_REPLACEMENT
#define mm_malloc(sz) event_mm_malloc_(sz)
#define mm_calloc(count, size) event_mm_calloc_((count), (size))
#define mm_strdup(s) event_mm_strdup_(s)
#define mm_realloc(p, sz) event_mm_realloc_((p), (sz))
#define mm_free(p) event_mm_free_(p)
#else
#define mm_malloc(sz) malloc(sz)
#define mm_calloc(n, sz) calloc((n), (sz))
#define mm_strdup(s) strdup(s)
#define mm_realloc(p, sz) realloc((p), (sz))
#define mm_free(p) free(p)
#endif //EVENT__DISABLE_MM_REPLACEMENTstatic void *(*mm_malloc_fn_)(size_t sz) = NULL;//定义一个接受参数为seize_t类型的函数指针mm_malloc_fn_
void * event_mm_calloc_(size_t count, size_t size)
{if (count == 0 || size == 0)return NULL;if (mm_malloc_fn_) { //非空size_t sz = count * size;void *p = NULL;if (count > EV_SIZE_MAX / size)goto error;p = mm_malloc_fn_(sz);if (p)return memset(p, 0, sz);} else {//为空void *p = calloc(count, size);
#ifdef _WIN32/* Windows calloc doesn't reliably set ENOMEM */if (p == NULL)goto error;
#endifreturn p;}error:errno = ENOMEM;return NULL;
}
libevent的一些重要的结构体
event_config_entry
结构体
libevent 中的事件配置项。
通过使用 event_config_entry
结构体,可以在 libevent 中定义和管理多个事件配置项,并按照链表的方式进行链接和访问。每个配置项都包含一个要避免使用的网络通信方法。
struct event_config_entry {TAILQ_ENTRY(event_config_entry) next;const char *avoid_method;//要避免使用的网络通信方法。
};
//通过使用 TAILQ_ENTRY 宏,可以为指定的数据类型创建一个双向链表的入口和出口结构体,方便在链表中进行插入、删除和遍历等操作。
#define _TAILQ_ENTRY(type, qual) \
struct { \qual type *tqe_next; /* next element */ \qual type *qual *tqe_prev; /* address of previous next element */\
}
#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
qual
用于指定链表结构体成员的修饰符(它可以是 const
、volatile
或其他限定符。)
tqe_next
:指向链表中下一个元素的指针。tqe_prev
:指向链表中上一个元素的指针的地址。这里使用了一个指向指针的指针,即二级指针,用于在删除元素时修改前一个元素的tqe_next
指针。
event_base
结构体
struct event_base {//一个指向特定于后端数据的指针,用于描述这个event_base端。const struct eventop *evsel;//一个指向特定于后端数据的指针,用于指向底层事件驱动后端的实现。void *evbase;/** List of changes to tell backend about at next dispatch. Only used* by the O(1) backends. *///一个结构体,用于描述在下次调度时需要通知后端更改的事件struct event_changelist changelist;/** Function pointers used to describe the backend that this event_base* uses for signals *///一个指向特定于信号处理后端数据的指针,用于描述后端event_base*用于信号处理。const struct eventop *evsigsel;/** Data to implement the common signal handler code. *///一个结构体,用于实现信号处理通用代码的数据。struct evsig_info sig;/** Number of virtual events *///用于表示当前虚拟事件数量。int virtual_event_count;/** Maximum number of virtual events active *///用于表示最大虚拟事件数量。int virtual_event_count_max;/** Number of total events added to this event_base *///用于表示已添加到event_base的事件数量。int event_count;/** Maximum number of total events added to this event_base *///用于表示最大已添加到event_base的事件数量。int event_count_max;/** Number of total events active in this event_base *///用于表示当前活动事件数量。int event_count_active;/** Maximum number of total events active in this event_base *///用于表示最大活动事件数量。int event_count_active_max;/** Set if we should terminate the loop once we're done processing* events. *///用于表示是否应该在处理完事件后终止循环。int event_gotterm;/** Set if we should terminate the loop immediately *///用于表示是否应该立即终止循环。int event_break;/** Set if we should start a new instance of the loop immediately. *///用于表示是否应该在处理完事件后继续执行循环。int event_continue;/** The currently running priority of events *///用于表示当前正在运行的事件优先级。int event_running_priority;/** Set if we're running the event_base_loop function, to prevent* reentrant invocation. *///用于表示是否正在运行event_base_loop函数,以防止重入调用。int running_loop;/** Set to the number of deferred_cbs we've made 'active' in the* loop. This is a hack to prevent starvation; it would be smarter* to just use event_config_set_max_dispatch_interval's max_callbacks* feature *///用于表示已 deferred_cbs 数量int n_deferreds_queued;/* Active event management. *//** An array of nactivequeues queues for active event_callbacks (ones* that have triggered, and whose callbacks need to be called). Low* priority numbers are more important, and stall higher ones.*///用于存储活动事件队列。struct evcallback_list *activequeues;/** The length of the activequeues array */int nactivequeues;//用于表示活动事件队列的数量。/** A list of event_callbacks that should become active the next time* we process events, but not this time. *///于存储应该在下次处理事件时激活的事件。struct evcallback_list active_later_queue;/* common timeout logic *//** An array of common_timeout_list* for all of the common timeout* values we know. *///用于存储所有已知的时间outs。struct common_timeout_list **common_timeout_queues;/** The number of entries used in common_timeout_queues *///用于表示已使用的时间outs数量。int n_common_timeouts;/** The total size of common_timeout_queues. *///用于表示已分配的时间outs数量。int n_common_timeouts_allocated;/** Mapping from file descriptors to enabled (added) events *///用于存储文件描述符到已添加事件的映射。struct event_io_map io;/** Mapping from signal numbers to enabled (added) events. *///用于存储信号编号到已添加事件的映射。struct event_signal_map sigmap;/** Priority queue of events with timeouts. *///1个优先队列,用于存储具有超时的事件。struct min_heap timeheap;/** Stored timeval: used to avoid calling gettimeofday/clock_gettime* too often. *///用于存储当前时间,以避免频繁调用gettimeofday/clock_gettime。struct timeval tv_cache;//,用于实现单调时钟。struct evutil_monotonic_timer monotonic_timer;/** Difference between internal time (maybe from clock_gettime) and* gettimeofday. *///用于存储内部时间与gettimeofday之间的差异。struct timeval tv_clock_diff;/** Second in which we last updated tv_clock_diff, in monotonic time. *///用于存储上次更新tv_clock_diff时的单调时间。time_t last_updated_clock_diff;#ifndef EVENT__DISABLE_THREAD_SUPPORT/* threading support *//** The thread currently running the event_loop for this base */unsigned long th_owner_id;/** A lock to prevent conflicting accesses to this event_base */void *th_base_lock;/** A condition that gets signalled when we're done processing an* event with waiters on it. */void *current_event_cond;/** Number of threads blocking on current_event_cond. */int current_event_waiters;
#endif/** The event whose callback is executing right now */struct event_callback *current_event;#ifdef _WIN32/** IOCP support structure, if IOCP is enabled. *//**:一个指向`event_iocp_port`结构体的指针,如果在Windows平台上使用IOCP(I/O Completion Port)作为事件驱动后端,则使用此字段。
*/struct event_iocp_port *iocp;
#endif/** Flags that this base was configured with *///`flags`:一个枚举类型,用于表示`event_base`的配置标志。enum event_base_config_flag flags;//,用于表示最大调度时间。struct timeval max_dispatch_time;int max_dispatch_callbacks;//用于表示最大调度回调数量。int limit_callbacks_after_prio;//用于表示在达到特定优先级后限制回调数量。/* Notify main thread to wake up break, etc. *//** True if the base already has a pending notify, and we don't need* to add any more. */int is_notify_pending;//用于表示`event_base`是否已经有一个待处理的通知。/** A socketpair used by some th_notify functions to wake up the main* thread. *///用于free baseevutil_socket_t th_notify_fd[2];//一个套接字对,用于在另一个线程中唤醒主线程。/** An event used by some th_notify functions to wake up the main* thread. */struct event th_notify;//用于在主线程中唤醒其他线程。/** A function used to wake up the main thread from another thread. *//**`th_notify`:一个`event`结构体,用于在主线程中唤醒其他线程。*/int (*th_notify_fn)(struct event_base *base);/** Saved seed for weak random number generator. Some backends use* this to produce fairness among sockets. Protected by th_base_lock. *///一个`evutil_weakrand_state`结构体,用于存储弱随机数生成器的种子。struct evutil_weakrand_state weakrand_seed;/** List of event_onces that have not yet fired. *//**`once_events`:一个`LIST_HEAD`结构体,用于存储尚未触发的事件。*/LIST_HEAD(once_event_list, event_once) once_events;};
eventop
结构体
/** 用于定义给定事件基础结构的后端的结构体。 */struct eventop {/** 后端的名称。 */const char *name;/** 初始化函数,用于设置事件基础结构以使用该后端。它应该创建一个新的结构体,* 保存运行该后端所需的任何信息,并将其返回。返回的指针将由event_init存储在* event_base.evbase字段中。如果初始化失败,该函数应返回NULL。 */void *(*init)(struct event_base *);/** 启用给定文件描述符或信号的读写事件。'events'参数表示我们要启用的事件类型,* 可能是EV_READ、EV_WRITE、EV_SIGNAL和EV_ET的组合。'old'参数表示之前在该* 文件描述符上启用的事件。'fdinfo'参数是与文件描述符相关联的结构体,在evmap* 中管理;其大小由下面的fdinfo_len字段定义。第一次添加文件描述符时,* 它将被设置为0。该函数应在成功时返回0,在错误时返回-1。 */int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);/** 类似于'add'函数,但'events'参数表示我们要禁用的事件类型。 */int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);/** 实现事件循环的核心功能。它需要检查哪些已添加的事件已准备就绪,并为每个活动事件* 调用event_active函数(通常通过event_io_active等方式)。该函数应在成功时返回0,* 在错误时返回-1。 */int (*dispatch)(struct event_base *, struct timeval *);/** 用于清理和释放事件基础结构中的数据的函数。 */void (*dealloc)(struct event_base *);/** 标志:如果我们在fork之后需要重新初始化事件基础结构,则设置此标志。 */int need_reinit;/** 支持的事件方法特性的位数组。 */enum event_method_feature features;/** 每个具有一个或多个活动事件的文件描述符应记录的额外信息的长度。* 此信息作为每个文件描述符的evmap条目的一部分记录,并作为参数传递给上述的* 'add'和'del'函数。 */size_t fdinfo_len;
};
event_config
结构体
/** Internal structure: describes the configuration we want for an event_base* that we're about to allocate. */
struct event_config {于定义一个双向链表的头部。它将被用作存储event_config_entry类型的结构体的链表。TAILQ_HEAD(event_configq, event_config_entry) entries;//用于提示事件配置所需的CPU数量。int n_cpus_hint;//指定最大分派间隔的时间值。struct timeval max_dispatch_interval;//指定在一次循环中最大的分派回调数量。int max_dispatch_callbacks;//用于限制在特定优先级之后的回调数量。int limit_callbacks_after_prio;//指定要求的事件方法特性。//event_config_require_featureEV_FEATURE_ET:要求支持边沿触发的后端等enum event_method_feature require_features;//指定事件基础配置的标志。enum event_base_config_flag flags;//event_config_set_flag 设置具体可用参数见上文
};
#define TAILQ_HEAD(name, type) \
struct name { \struct type *tqh_first; \struct type **tqh_last; \
}
timeval
结构体
/* A time value that is accurate to the nearestmicrosecond but also has a range of years. */struct timeval{__time_t tv_sec; /* Seconds. */__suseconds_t tv_usec; /* Microseconds. */};
相关文章:

从一万英尺外看libevent(源码刨析)
从一万英尺外看libevent 温馨提示:阅读时间大概二十分钟 前言 Libevent是用于编写高速可移植非阻塞IO应用的库,其设计目标是: 可移植性:使用libevent编写的程序应该可以在libevent支持的所有平台上工作。即使没有好的方式进行非…...

Linux部署SVN
一.下载与安装 (1)yum安装 yum install subversion (2)源文件编译安装 ①下载svn源文件 subversion-xxx.tar.gz(subversion 源文件) subversion-deps-xxx.tar.gz(subversion依赖文件&…...

Linux高并发服务器开发(二)系统调用函数
文章目录 1 系统调用2 errno3 虚拟内存空间4 文件描述符5 常用文件IO函数6 阻塞和非阻塞7 lseek 偏移函数8 文件操作函数之stat函数9 文件描述符复制 dup10 fcnlt函数 修改文件属性11 目录相关操作12 时间相关函数 1 系统调用 根据系统调用,获取驱动信息、CPU的信息…...

rk3568 Android 11在系统怎样执行命令获取SN号
目录 1. 使用ADB(Android Debug Bridge)2. 使用Shell脚本或应用程序3. 使用系统API4. 直接在设备上使用Shell5. getprop使用方法常见属性示例注意事项 在瑞芯微RK3568 Android 11系统中执行命令或获取SN号(序列号)通常可以通过几种…...

PostgreSQL 性能优化与调优(六)
1. 索引优化 1.1 创建索引 索引可以显著提高查询性能。创建索引的基本语法如下: CREATE INDEX index_name ON table_name (column_name);例如,为 users 表的 username 列创建索引: CREATE INDEX idx_username ON users (username); 1.2 …...

win10 安装openssl并使用openssl创建自签名证书
win10创建自签名证书 下载安装配置openssl 下载地址: https://slproweb.com/download/Win64OpenSSL-3_3_1.exe https://slproweb.com/products/Win32OpenSSL.html 完成后安装,一路next,到达选位置的之后选择安装的位置,我这里选…...

【OpenCV 图像处理 Python版】图像处理的基本操作
文章目录 1.图像的 IO 操作1.1 图像读取 imread1.2 图像显示1.2.1 opencv 方式1.2.2 matplotlib 方式 1.3 图像保存 imwrite 2.绘制几何图形1. 绘制直线2. 绘制矩形3. 绘制圆形4. 绘制多边形5. 添加文字 3.获取并修改图像中的像素点3.1 获取像素值3.2 修改像素值3.3 获取和修改…...

HarmonyOS应用开发学习经验
一、HarmonyOS学习官网 开发者能力认证 HarmonyOS应用开发者基础认证6月之前的学习资源官网已经关闭过期,大家不要慌,官方更新了最新资源,但是,对于之前没有学习完的学员不友好,存在知识断片的现象,建议官…...

LLM大语言模型应用方案之RAG检索增强生成的实现步骤。
0.我理解的RAG 什么是RAG? RAG的全称是“检索增强生成模型”(Retrieval-Augmented Generation)。这是一种特别聪明的大语言模型。 RAG是怎么工作的呢? 1.检索:当你问RAG一个问题时,它会先去“图书…...

【python学习】学习python的小项目
学习Python时,通过完成一些小项目可以帮助你巩固知识并提升实践能力。以下是一些适合学习Python的小项目建议: 命令行计算器: 创建一个简单的命令行计算器,可以执行基本的算术运算(加、减、乘、除)。使用i…...

java-冒泡排序 1
## Java中的冒泡排序 ### 1. 冒泡排序的基本概念 冒泡排序(Bubble Sort)是一种简单且直观的排序算法。它通过重复地遍历待排序的列表,比较相邻的元素并交换它们的位置,使较大的元素逐步从列表的一端移动到另一端,就像…...

【STM32】USART串口通讯
1.USART简介 STM32芯片具有多个USART外设用于串口通讯,它是 Universal Synchronous Asynchronous Receiver and Transmitter的缩写, 即通用同步异步收发器可以灵活地与外部设备进行全双工数据交换。有别于USART, 它还有具有UART外设(Univers…...

Qt6中如何将QList转为QSet?
QSet是一个具有唯一值的哈希集合。比较少用。比较有用的是QSet里面的intersect查找两个集合中不同元素,并合并。 转换过程比较简单,第一种是直接用迭代器。 QSet<int> set(list.begin(), list.end()); 第二种就是逐一遍历赋值: QLi…...

aspectj:AOP编程备忘录-切面定义的注意事项
AOP编程时定义切面时需要注意的事 Around 以Around注解拦截构造方法(Constructor)时切面定义只能用call方式而不能是execution,否则 ProceedingJoinPoint.proceed()返回的是null,得不到构造的实例。 execution execution切入点要修改对象内部&#x…...

大数据面试题之Hive(1)
目录 说下为什么要使用Hive?Hive的优缺点?Hive的作用是什么? 说下Hive是什么?跟数据仓库区别? Hive架构 Hive内部表和外部表的区别? 为什么内部表的删除,就会将数据全部删除,而外部表只删除表结构?为什么用外部表更好? Hive建表语句?创建表…...

【Git】分布式版本控制工具
一、简介 二、目标 Git分布式版本控制工具 一、简介 Git是一种分布式版本控制系统,用于跟踪和管理源代码的变化。它由林纳斯托瓦兹(Linus Torvalds)于2005年开发,并迅速成为最流行的版本控制工具之一。以下是关于Git的一些关键…...

排序之插入排序----直接插入排序和希尔排序(1)
个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 排序之插入排序----直接插入排序和希尔排序(1) 收录于专栏【数据结构初阶】 本专栏旨在分享学习数据结构学习的一点学习笔记,欢迎大家在评论区交流讨…...

快速创建条形热力图
Excel中的条件格式可以有效的凸显数据特征,如下图中B列所示。 现在需要使用图表展现热力条形图,如下图所示。由于颜色有多个过渡色,因此手工逐个设置数据条的颜色,基本上是不可能完成的任务,使用VBA代码可以快速创建这…...

go switch 与 interface
go switch 与 interface 前言 前言 github.com/google/cel-go/common/types/ref type Val interface {// ConvertToNative converts the Value to a native Go struct according to the// reflected type description, or error if the conversion is not feasible.ConvertTo…...

BaseMapper 接口介绍
基于 mybatis-mapper/provider 核心部分实现的基础的增删改查操作,提供了一个核心的 io.mybatis.mapper.BaseMapper 接口和一个 预定义 的 io.mybatis.mapper.Mapper 接口,BaseMapper 接口定义如下: /*** 基础 Mapper 方法,可以在…...

HAL-Cubemax定时器使用记录
title: HAL-Cubemax定时器使用记录 tags: STM32HalCubemax 文章目录 HAL-Cubemax定时器使用记录分享一种思路1.创建一个ms(毫秒)级延时中断2.创建计数的变量3.在需要延时的函数中对变量阈值进行判断4.验证实例--完整使用记录代码 问题往期内容基础库HAL cubemax VSCODE GCC …...

同时使用磁吸充电器和Lightning时,iPhone充电速度会变快吗?
在智能手机的世界里,续航能力一直是用户关注的焦点。苹果公司以其创新的MagSafe技术和传统的Lightning接口,为iPhone用户提供了多样化的充电解决方案。 然而,当这两种技术同时使用时,它们能否带来更快的充电速度?本文…...

零成本搭建个人图床服务器
前言 图床服务器是一种用于存储和管理图片的服务器,可以给我们提供将图片上传后能外部访问浏览的服务。这样我们在写文章时插入的说明图片,就可以集中放到图床里,既方便多平台文章发布,又能统一管理和备份。 当然下面通过在 Git…...

SpringBoot 搭建sftp服务 实现远程上传和下载文件
maven依赖: <dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version> </dependency>application.yml sftp:protocol: sftphost: port: 22username: rootpassword: sp…...

IDEA中使用leetcode 刷题
目录 1.IDEA下载leetcode插件 2.侧边点开插件 3.打开网页版登录找到cookie复制 4.回到IDEA登录 5.刷题 6.共勉 1.IDEA下载leetcode插件 2.侧边点开插件 3.打开网页版登录找到cookie复制 4.回到IDEA登录 5.刷题 6.共勉 算法题来了不畏惧, 挑战前行是成长的舞台…...

华为海思CPU解读
安全可靠CPU测评结果(华为海思篇) 中国信息安全测评中心于2024年5月20日发布安全可靠测评结果公告(2024年第1号),公布依据《安全可靠测评工作指南(试行)》的测评结果,自发布起有效期…...

中介子方程三十三
XXFXXuXXWXXuXXdXXrXXαXXuXpXXdXXpXuXXαXXrXXdXXuXWXπXXWXeXyXeXbXπXpXXNXXqXeXXrXXαXXuXpXXdXXpXuXXαXXrXXeXqXXNXXpXπXbXeXyXeXWXXπXWXuXXdXXrXXαXXuXpXXdXXpXuXXαXXrXXdXXuXXWXXuXXFXXEXXyXXEXXrXXαXXuXpXXdXXpXuXXαXXrXXEXXyXXαXiXXαXiXrXkXtXyXXpXVXXdXuXWX…...

今年哪两个行业可能有贝塔?
银行和综合板块存在比较明显的行业贝塔,背后原因是:银行板块中,最小的几家银行市值也不小;综合板块中,最大的几家市值也不大。 一、今年哪两个行业可能有贝塔? 我们一直强调今年市场呈现出【行业弱beta、风…...

嵌入式软件开发工具使用介绍
软件开发工具 辅助开发工具 硬件工具与仪器设备 逻辑分析仪使用 串口数据解码分析 示波器使用 1.示波器简介 TBS 1052B(Tektronix)系列数字存储示波器在紧凑的设计中提供了经济的性能。 由于多种标配功能, 包括 USB 连接、34 种自动测量、…...

【TB作品】MSP430G2553,单片机,口袋板, 交通灯控制系统
题8 交通灯控制系统 十字路口交通灯由红、绿两色LED显示器(两位8段LED显示器)组成,LED显示器显示切换倒计时,以秒为单位,每秒更新一次;为确保安全,绿LED计数到0转红,经5秒延时&#…...