设置libevent

设置libeventlibevent 有一些全局设置是整个程序共享的 这些设置影响整个库 在调用 libevent 库的其他部分之前都需要对这些设置进行修改 否则 libevent 可能处于不一致状态 1 日志 libevent 记录内在的错误和告警 如果支持日志进行编译 它也能记录调试信息 默认情况下 这些信息输出到 stderr 我们也可以提供自己的日志函数改变输出 涉及的接口 defineEVENT L

libevent有一些全局设置是整个程序共享的,这些设置影响整个库。在调用libevent库的其他部分之前都需要对这些设置进行修改,否则libevent可能处于不一致状态。
1.日志
libevent记录内在的错误和告警,如果支持日志进行编译,它也能记录调试信息。默认情况下,这些信息输出到stderr,我们也可以提供自己的日志函数改变输出。
涉及的接口:
#define EVENT_LOG_DEBUG 0 #define EVENT_LOG_MSG 1 #define EVENT_LOG_WARN 2 #define EVENT_LOG_ERR 3 /* Deprecated; see note at the end of this section */ #define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG #define _EVENT_LOG_MSG EVENT_LOG_MSG #define _EVENT_LOG_WARN EVENT_LOG_WARN #define _EVENT_LOG_ERR EVENT_LOG_ERR typedefvoid (*event_log_cb)(int severity, constchar *msg); void event_set_log_callback(event_log_cb cb);

要改变日志输出,只需要写一个跟
event_log_cb签名一样的函数,然后把它传给
event_set_log_callback即可。这样,当libevent要输出日志时,使用的就是我们写的函数,如果要恢复libevent的日志输出设置,值需要给event_set_log_callback传参数NULL就可以了。

例如

#include 
   
     #include 
     
     static void discard_cb( 
     int severity, 
     const char *msg) { 
      /* This callback does nothing. */ } 
     static FILE *logfile = NULL; 
     static void write_to_file_cb( 
     int severity, 
     const char *msg) { 
     const char *s; 
     if (!logfile) 
     return; 
     switch (severity) { 
     case _EVENT_LOG_DEBUG: s = "debug"; 
     break; 
     case _EVENT_LOG_MSG: s = "msg"; 
     break; 
     case _EVENT_LOG_WARN: s = "warn"; 
     break; 
     case _EVENT_LOG_ERR: s = "error"; 
     break; 
     default: s = "?"; 
     break; /* never reached */ } fprintf(logfile, "[%s] %s\n", s, msg); } 
     /* Turn off all logging from Libevent. */ 
     void suppress_logging( 
     void) { event_set_log_callback(discard_cb); } 
     /* Redirect all Libevent log messages to the C stdio file 'f'. */ 
     void set_logfile(FILE *f) { logfile = f; event_set_log_callback(write_to_file_cb); } 
     
   

 int main() 
 { 
    FILE * pFile; 
    char buffer [100]; 

     pFile = fopen ("./myfile.txt" , "w"); 
      if (pFile == NULL) 
          return 0; 
     set_logfile(pFile); 
     write_to_file_cb(_EVENT_LOG_MSG,"hello world"); 
     return 0; 
 } 
注意:
从一个用户提供的event_log_cb callback函数内调用libevent函数不是很安全。例如,你想写一个使用bufferevents 给socket发送告警信息的回调函数,那么有可能会出现一个很陌生而且很难调试的bug。这一限制在将来的版本可能会移除。
通常情况下,debug日志是不可用的,可以用以下函数手动开启。
#define EVENT_DBG_NONE 0 #define EVENT_DBG_ALL 0xffffffffu void event_enable_debug_logging(ev_uint32_t which);
2.处理致命错误
当libevent捕获一个致命错误时,默认去调用exit()或abort()来跳出当前进程。出现这种情况时,意味着你的代码或者libevent出现bug。
通过以下函数,你可以有改变libevent的行为来优雅的处理致命错误。
typedef void (*event_fatal_cb)(int err); void event_set_fatal_callback(event_fatal_cb cb);

你需要定义一个类似event_fatal_cb签名的回调函数,然后把它传给event_set_fatal_callback。如此一来,当程序出现致命错误时,将调用你定义的回调函数。你的回调函数不能调用任何libevent函数,

3.内存管理
默认情况下,libevent用C语言的内存管理函数从堆上分配内存。你可以为libevent提供别的内存管理器来替代C的内存管理函数。如果你有更好的内存分配器的话。
void event_set_mem_functions(void *(*malloc_fn)(size_t sz), void *(*realloc_fn)(void *ptr, size_t sz), void (*free_fn)(void *ptr));

例如

#include 
     
       #include 
      
        #include 
        
        /* This union's purpose is to be as big as the largest of all the * types it contains. */ 
        union alignment { size_t sz; 
        void *ptr; 
        double dbl; }; 
        /* We need to make sure that everything we return is on the right alignment to hold anything, including a double. */ #define ALIGNMENT 
        sizeof( 
        union alignment) 
        /* We need to do this cast-to-char* trick on our pointers to adjust them; doing arithmetic on a void* is not standard. */ #define OUTPTR(ptr) ((( 
        char*)ptr)+ALIGNMENT) #define INPTR(ptr) ((( 
        char*)ptr)-ALIGNMENT) 
        static size_t total_allocated = 0; 
        static void *replacement_malloc(size_t sz) { 
        void *chunk = malloc(sz + ALIGNMENT); 
        if (!chunk) 
        return chunk; total_allocated += sz; *(size_t*)chunk = sz; 
        return OUTPTR(chunk); } 
        static void *replacement_realloc( 
        void *ptr, size_t sz) { size_t old_size = 0; 
        if (ptr) { ptr = INPTR(ptr); old_size = *(size_t*)ptr; } ptr = realloc(ptr, sz + ALIGNMENT); 
        if (!ptr) 
        return NULL; *(size_t*)ptr = sz; total_allocated = total_allocated - old_size + sz; 
        return OUTPTR(ptr); } 
        static void replacement_free( 
        void *ptr) { ptr = INPTR(ptr); total_allocated -= *(size_t*)ptr; free(ptr); } 
        void start_counting_bytes( 
        void) { event_set_mem_functions(replacement_malloc, replacement_realloc, replacement_free); } 
        
       
     

注意事项:

1.必须确保在调用其他libevent函数之前进行替换。
2.你的内存分配函数返回的内存块的对齐要与C库的一样。
3.你的realloc 函数要能正确处理realloc(NULL, sz)
4..你的realloc 函数要能正确处理realloc(ptr, 0)
5.你的free函数不需要处理free(NULL)
6.你的malloc函数不需要处理malloc(0)
7.你的内存分配器有钱哦确保线程安全
8.如果你替换了libevent的内存分配函数,而且调用了libevent返回内存,你就必须用你的free函数释放内存。

4.线程和锁
libevent处理多线程的三种方法:
1.有些结构本身就是单线程的。
2.一些结构可以选择性的锁。
3.一些结构一致是锁定的。
要获得libevent锁,你必须告诉libevent要用那个锁函数,在分配一个线程间共享的结构之前,你必须这么做。
#ifdef WIN32 int evthread_use_windows_threads(void); #define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED #endif #ifdef _EVENT_HAVE_PTHREADS int evthread_use_pthreads(void); #define EVTHREAD_USE_PTHREADS_IMPLEMENTED #endif

所有的函数都是成功则返回0,失败则返回-1;

如果你需要用其他的线程库,你需要定义自己的函数来实现
 
   
  • Locks
  • locking
  • unlocking
  • lock allocation
  • lock destruction
  • Conditions
  • condition variable creation
  • condition variable destruction
  • waiting on a condition variable
  • signaling/broadcasting to a condition variable
  • Threads
  • thread ID detection
并用以下函数告诉libevent
#define EVTHREAD_WRITE 0x04 #define EVTHREAD_READ 0x08 #define EVTHREAD_TRY 0x10 #define EVTHREAD_LOCKTYPE_RECURSIVE 1 #define EVTHREAD_LOCKTYPE_READWRITE 2 #define EVTHREAD_LOCK_API_VERSION 1 struct evthread_lock_callbacks { int lock_api_version; unsigned supported_locktypes; void *(*alloc)(unsigned locktype); void (*free)(void *lock, unsigned locktype); int (*lock)(unsigned mode, void *lock); int (*unlock)(unsigned mode, void *lock); }; int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *); void evthread_set_id_callback(unsigned long (*id_fn)(void)); struct evthread_condition_callbacks { int condition_api_version; void *(*alloc_condition)(unsigned condtype); void (*free_condition)(void *cond); int (*signal_condition)(void *cond, int broadcast); int (*wait_condition)(void *cond, void *lock, const struct timeval *timeout); }; int evthread_set_condition_callbacks( const struct evthread_condition_callbacks *);

公认的锁类型

0

常规的,不需要递归的锁

EVTHREAD_LOCKTYPE_RECURSIVE

递归锁。同一个线程可以多次进入,不会被阻塞。

EVTHREAD_LOCKTYPE_READWRITE 读写锁。如果是进行读操作,则允许多个线程进入。但是写操作则只能有一个线程进入。写操作排斥所有读操作。

公认的锁模式:

EVTHREAD_READ

EVTHREAD_WRITE

EVTHREAD_TRY

5.调试锁使用

libevent有一个可选的锁调试功能,它封装了锁调用,用来捕获典型的锁错误。包括:
释放本来没有获得的锁。
重锁一个非递归锁。
如果出现其中一种错误,libevent从失败断言退出。
void evthread_enable_lock_debugging(void); #define evthread_enable_lock_debuging() evthread_enable_lock_debugging()

6.调试事件使用

使用event时libevent会向我们报告一些常见的错误。包括:
把一个未初始化的event结构体当成已经初始化来使用。
尝试重新初始化一个未定的event。
void event_enable_debug_mode(void);

必须在创建event_base 之前调用此函数。

使用调试模式,当使用大量由event_assign()创建的event时程序内存可能耗尽,因为libevent没有办法知道哪个event不再需要。要避免出现这种内存耗尽的情况,你必须显示的告诉libevent哪个event不再需要。
void event_debug_unassign(struct event *ev);
如果不是调试模式,此函数不起任何作用。
#include 
     
       #include 
      
        #include 
        
        void cb(evutil_socket_t fd, 
        short what, 
        void *ptr) { 
         /* We pass 'NULL' as the callback pointer for the heap allocated * event, and we pass the event itself as the callback pointer * for the stack-allocated event. */ 
        struct event *ev = ptr; 
        if (ev) event_debug_unassign(ev); } 
        /* Here's a simple mainloop that waits until fd1 and fd2 are both * ready to read. */ 
        void mainloop(evutil_socket_t fd1, evutil_socket_t fd2, 
        int debug_mode) { 
        struct event_base *base; 
        struct event event_on_stack, *event_on_heap; 
        if (debug_mode) event_enable_debug_mode(); base = event_base_new(); event_on_heap = event_new(base, fd1, EV_READ, cb, NULL); event_assign(&event_on_stack, base, fd2, EV_READ, cb, &event_on_stack); event_add(event_on_heap, NULL); event_add(&event_on_stack, NULL); event_base_dispatch(base); event_free(event_on_heap); event_base_free(base); } 
        
       
     


7.检测libevent版本
有时候我们需要检测libevent版本,以便于:
1.已经安装的libevent版本对于我们程序来说足够好。
2.调试时显示libevent版本
3.检测libevent,并警告用户相关bugs。
#define LIBEVENT_VERSION_NUMBER 0x0 #define LIBEVENT_VERSION "2.0.3-alpha" const char *event_get_version(void); ev_uint32_t event_get_version_number(void);

8.释放全局libevent结构

即使你已经释放了所有用libevent分配的对象,但还是遗留一些全局分配的对象。这通常不是个问题,一旦程序退出,它们都将被清除。但是有时候这些对象会让一下调试工具以为内存泄漏。如果想要libevent释放所有的数据对象,可以用以下函数:
void libevent_global_shutdown(void);

该函数不会释放任何你用libevent分配的内存。














版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/229528.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午4:30
下一篇 2026年3月16日 下午4:31


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号