libuv异步实现分析

我们都知道libuv是异步非阻塞的,那么它究竟是如何实现异步的呢?

Libuv中文编程指南上如是说

libuv提供的文件操作和 socket operations 并不相同. 套接字操作使用了操作系统本身提供了非阻塞操作,而文件操作内部使用了阻塞函数,但是 libuv 是在线程池中调用这些函数, 并在应用程序需要交互时通知在事件循环中注册的监视器.

参考: http://www.infoq.com/cn/articles/nodejs-asynchronous-io

 

也就是说libuv除了借助于操作系统自身的分阻塞操作外,还使用了阻塞 + 多线程的实现方式。

下面通过分析libuv自带的例子来分析libuv异步的实现方式。

 

对于套接字操作,先看看libuv中自带的用例echo-server.c,其中这样的代码:

HELPER_IMPL(udp4_echo_server) {
 loop = uv_default_loop();
 
  if(udp4_echo_start(TEST_PORT))
   return 1;
 
 uv_run(loop, UV_RUN_DEFAULT);
  return 0;
}


这里uv_run里面包含了异步事件polling和处理的循环。

 

再看udp4_echo_start函数

static int udp4_echo_start(int port) {
  intr;
 
 server = (uv_handle_t*)&udpServer;
 serverType = UDP;
 
  r =uv_udp_init(loop, &udpServer);
//这里将loop赋值给了udpServer的loop成员,然后后续会对loop进行成员变量的值进行设定。在uv_run中会根据loop获取其在udpServer中的双链表成员所在的节点,进行处理然后删除。
  if(r) {
   fprintf(stderr, "uv_udp_init: %s\n", uv_strerror(r));
   return 1;
  }
 
  r =uv_udp_recv_start(&udpServer, echo_alloc, on_recv);
  if(r) {
   fprintf(stderr, "uv_udp_recv_start: %s\n", uv_strerror(r));
   return 1;
  }
 
 return 0;
}


 追寻代码有如下的调用过程:

uv_run->uv__run_pending-> uv__udp_io

 uv__udp_io[udp.c]->uv__udp_recvmsg->会调用handle的recv_cb实际上->用户设置的callback

用户的回调是被调用了,那设置套接字非阻塞呢?

 

再详细看看上面的例子,找到如下的代码处理流程:

[echo-server.c]udp4_echo_start->uv_udp_recv_start->[uv-common.c]uv__udp_recv_start->[udp.c] uv__udp_maybe_deferred_bind->uv__udp_bind->uv__socket->[core.c]调用uv__nonblock设置套接字非阻塞。。。

 

 

针对文件操作,虽然使用了阻塞函数,但是将阻塞操作放到了线程池中。

 

看看Test-fs.c中的例子,找到如下的函数调用链:

 

(创建线程池中的各个线程只需初始化一次

Test-fs.c->uv_fs_open->fs.c  INIT->POST ->uv__work_submit ->threadpool.c->uv__work_submit->uv_once->init_once->uv_thread_create)

 

Test-fs.c->uv_fs_open->fs.c  INIT->POST ->uv__work_submit-> post

在post中将work_req也就是uv__work挂到threadpool.c模块的staict双向链表wq中。

后续,线程池中的线程从wq中捞出任务进行处理。这样实际上是多线程进行了处理。处理完成之后会调用uv__fs_done,在uv__fs_done中调用用户设置的回调函数。


本人享有博客文章的版权,转载请标明出处http://blog.csdn.net/baidu20008


你可能感兴趣的:(文件系统,套接字,异步回调,libuv异步实现)