libev学习系列之四:ev_loop事件循环

libev学习系列之四:ev_loop事件循环


版本说明

版本 作者 日期 备注
0.1 ZY 2019.5.31 初稿

目录

一、前言

这个是基于官网资料以及这里

https://segmentfault.com/a/1190000006173864

基本上ev_loop你可以理解为一个大的循环,之前我们需要自己在线程里创建一个while循环,然后做一些多路复用监听等等,而这里的ev_loop你可以简单理解为就是代替这个循环的,当它开始之后,它会监听你设置的之前我们截图的watcher到这个loop中,该loop代替那个循环来监听你之前需要循环监听的事件,当该事件发生时,就会调用你在注册这个事件时绑定的回调函数。

二、描述

下面这个是官网的描述:

在这里插入图片描述

下面是参考网址中的内容:

event loop 用一个结构体struct ev_loop *描述。Libev 支持两类 loop,一是 default loop,支持 child process event;动态创建的 event loops 就不支持这个功能

struct ev_loop *ev_default_loop (unsigned int flags);

初始化 default loops。如果已经初始化了,那么直接返回并且忽略 flags。注意这个函数并不是线程安全的。只有这个 loop 可以处理ev_child事件。

struct ev_loop *ev_loop_new (unsigned int flags);

这个函数是线程安全的。一般而言,每个 thread 使用一个 loop。以下说明 flag 项的各个值:

  • EVFLAG_AUTO:默认值,常用
  • EVFLAG_NOENV:指定 libev 不使用LIBEV_FLAGS环境变量。常用于调试和测试
  • EVFLAG_FORKCHECK:与ev_loop_fork()相关
  • EVFLAG_NOINOTIFY:在ev_stat监听中不使用inotify API
  • EVFLAG_SIGNALFD:在ev_signal监听中使用signalfd API
  • EVFLAG_NOSIGMASK:使 libev 避免修改 signal mask。这样的话,你要使 signal 是非阻塞的。在未来的 libev 中,这个 mask 将会是默认值。
  • EVBACKEND_SELECT:通用后端
  • EVBACKEND_POLL:除了 Windows 之外的所有后端都可以用
  • EVBACKEND_EPOLL:Linux 后端
  • EVBACKEND_KQUEUE:大多数 BSD 的后端
  • EVBACKEND_DEVPOLL:Solaris 8 后端
  • EVBACKEND_PORT:Solaris 10 后端
void ev_loop_destroy (struct ev_loop *loop);

销毁ev_loop。注意这里要将所有的 IO 清除光之后再调用,因为这个函数并不中止所有活跃(active)的 IO。部分 IO 不会被清除,比如 signal。这些需要手动清除。这个函数一般和ev_loop_new一起出现在同一个线程中。

void ev_loop_fork (struct ev_loop *loop);

这个函数导致ev_run的子过程重设已有的 backend 的 kernel state。重用父进程创建的 loop。可以和pthread_atfork()配合使用。

对于上面flag值的选用其实就是我之前说要有一定socket、多路复用等基础知识的原因了,如果你不是很清楚的话,使用默认值即可。

三、例子

上面基本上已经说得很清晰了,你有child且要在子进程使用libev那你只能使用默认的loop,而如果你的进程需要在多线程中分别使用loop,那么最好在各个线程中创建各自的loop来保证线程安全。我一般使用子进程的情况较少,多是多线程方式使用,所以我一般的用法如下:

static void timeout_upload_cb(EV_P_ ev_timer *w_, int revents)
{
	...
}

static void *thread_device_info_upload()
{
	g_loop = ev_loop_new(EVFLAG_AUTO);
	
    ev_timer_init(&g_timer_watcher, timeout_upload_cb, TIMEOUT, TEST_REPEAT_TIME);

	ev_timer_start(g_loop, &g_timer_watcher);

	ev_run(g_loop, 0);

	return NULL;
}

其中g_loop就是我在该线程下创建的loop:static struct ev_loop *g_loop = NULL;

为了保证退出时调用ev_break(g_loop, EVBREAK_ALL); 所以一般是将loop设为全局变量。

当然,你也可以像

https://www.cnblogs.com/wunaozai/p/3950249.html

里面的例子这样来使用:

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/unistd.h>
#include <ev.h>


void io_action(struct ev_loop *main_loop,ev_io *io_w,int e)
{
    int rst;
    char buf[1024];
    memset(buf,0,sizeof(buf));
    puts("In IO action");
    read(STDIN_FILENO,buf,sizeof(buf));
    buf[1023]='\0';
    printf("String: %s\n",buf);
    ev_io_stop(main_loop,io_w);
}

void timer_action(struct ev_loop *main_loop,ev_timer *time_w,int e)
{
    puts("In Time action");
    ev_timer_stop(main_loop,time_w);
}

void signal_action(struct ev_loop *main_loop,ev_signal *signal_w,int e)
{
    puts("In Signal action");
    ev_signal_stop(main_loop,signal_w);
    ev_break(main_loop,EVBREAK_ALL);
}

int main(int argc,char **argv)
{
    ev_io io_w;
    ev_timer timer_w;
    ev_signal signal_w;
    struct ev_loop *main_loop = ev_default_loop(0);

    ev_init(&io_w,io_action);
    ev_io_set(&io_w,STDIN_FILENO,EV_READ);

    ev_init(&timer_w,timer_action);
    ev_timer_set(&timer_w,2,0);

    ev_init(&signal_w,signal_action);
    ev_signal_set(&signal_w,SIGINT);

    ev_io_start(main_loop,&io_w);
    ev_timer_start(main_loop,&timer_w);
    ev_signal_start(main_loop,&signal_w);

    ev_run(main_loop,0);
    return 0;
}
发布了119 篇原创文章 · 获赞 138 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/weixin_39510813/article/details/90713030
今日推荐