> ## Content Index
> Fetch the complete content index at: https://blog.hacking.pub/llms.txt
> Use this file to discover other available public pages before exploring further.

# nodejs 异步回调
- URL: https://blog.hacking.pub/2016/10/08/nodejs-yi-bu-hui-diao/
- Published: 2016-10-08T12:35:15.000Z
- Updated: 2016-10-27T02:56:36.000Z
- Author: chainhelen

## 异步非阻塞

nodejs 的软肋是CPU密集型，但它适应IO密集型服务。事件驱动，异步非阻塞。个人理解，异步与非阻塞细分下来是两个概念。**异步与同步强调的当前线程（或进程）的两个代码块执行是没有拓扑顺序的的**；**阻塞与非阻塞强调的是当前线程（或进程）的状态**。在操作系统层面上来看，有五种网络模式方式可以进行I/O访问。（[具体比较看这里](https://segmentfault.com/a/1190000003063859?hmsr=toutiao.io&utm%5Fmedium=toutiao.io&utm%5Fsource=toutiao.io)）

> - 阻塞 I/O（blocking IO）

- 非阻塞 I/O（nonblocking IO）
- I/O 多路复用（ IO multiplexing）
- 信号驱动 I/O（ signal driven IO）
- 异步 I/O（asynchronous IO）

异步、非阻塞两种方式都可以实现 nodejs 的 I/O并行。

> **非阻塞的方式**，需要当前线程（或进程）不断的询问操作系统: “数据准备好了吗？”，直到系统回答ok。例如linux中，采用read 方法会不断重复检查 I/O的 状态；而select 和 poll 是利用的多路复用技术，检查的是文件描述符，在描述fd集合的方式上有所不同；epoll是对poll的改进。

> **理想的异步I/O**应该是应用程序发起异步调用，而不需要进行轮询，进而处理下一个任务，只需在I/O完成后通过信号或是回调将数据传递给应用程序即可。盗图如下![infoq的图](http://www.infoq.com/resource/articles/nodejs-asynchronous-io/zh/resources/async.png)

##### 参考资料

[初探Node.js的异步I/O实现](http://www.infoq.com/cn/articles/nodejs-asynchronous-io/?ref=blog.hacking.pub)  
[Linux IO模式及 select、poll、epoll详解](https://segmentfault.com/a/1190000003063859?hmsr=toutiao.io&utm%5Fmedium=toutiao.io&utm%5Fsource=toutiao.io)  
[libuv 和 nodejs 的关系是什么？其他语言（如：python）是如何实现异步I/O的？](https://segmentfault.com/q/1010000002397156?ref=blog.hacking.pub)

## libuv 介绍

nodejs底层实现事件驱动的库是libuv，盗图如下：  
![nodejs底层](http://www.infoq.com/resource/articles/nodejs-asynchronous-io/zh/resources/arch.png)  
[libuv源码传送门](https://github.com/libuv/libuv?ref=blog.hacking.pub)可以先下载源码，安装感受一下

```
git clone git@github.com:libuv/libuv.git

```

有两种方式可以使用GCC编译，autools或者GYP工具。下面在linux环境下使用autools作为实例

```
$ sh autogen.sh
$ ./configure
$ make
$ make check
$ make install

```

linux下autogen脚本里依赖automake和libtool工具。如果是ubuntu用户，所以使用之前可以提前使用以下命令，避免有库依赖问题

```
sudo apt-get install make automake libtool

```

###### make check出现问题

make check中途出现如下错误，但是可以不理会。从[传送门1](https://github.com/libuv/libuv/issues/380?ref=blog.hacking.pub)、[传送门2](https://github.com/libuv/libuv/issues/380?ref=blog.hacking.pub)，上可以该问题知道是网络本身问题，个人怀疑是墙的原因。  
![error](http://image.hacking.pub/s/3bwau9onthl3i2i/test.png)

###### make install 成功

注意中间那段话  
![make_install](http://image.hacking.pub/s/bjayuepxltudayb/make_install.png?dl=0)

###### 尝试写一个程序 helloworld.c

```c
#include <stdio.h>
#include <uv.h>

int main(){
    uv_loop_t *loop = uv_loop_new();
    printf("now quitting.\n");
    uv_run(loop, UV_RUN_DEFAULT);
    return 0;
}

```

编译报错

```c
g++ helloworld.c -o helloworld
/tmp/ccJnLb1A.o：在函数‘main’中：
helloworld.c:(.text+0x9)：对‘uv_loop_new’未定义的引用
helloworld.c:(.text+0x28)：对‘uv_run’未定义的引用
collect2: error: ld returned 1 exit status

```

从错误中可以知道是库的引用问题，从make install 结果来看可以加指定参数-L库路径/usr/local/lib;指定参数-l指定库名，尽管库名是libuv，但是链接的时候要去掉前面的lib，即-luv;

```
gcc helloworld.c  -L/usr/local/lib/ -luv -o helloworld

```

编译成功但是运行错误

```
./helloworld 
./helloworld: error while loading shared libraries: libuv.so.1: cannot open shared object file: No such file or directory

```

添加环境变量 LD\_LIBRARY\_PATH

```
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

```

ok，运行成功

###### 补充一点：

[LIBRARY\_PATH和LD\_LIBRARY\_PATH的区别](https://typecodes.com/cseries/gcclderrlibrarypath.html?ref=blog.hacking.pub)  
**1**. Linux gcc编译链接时的动态库搜索路径(不会递归搜索)

```
1、gcc编译、链接命令中的-L选项；
2、gcc的环境变量的LIBRARY_PATH（多个路径用冒号分割）；
3、gcc默认动态库目录：/lib:/usr/lib:usr/lib64:/usr/local/lib。

```

**2**.执行二进制文件时的动态库搜索路径

```
1、编译目标代码时指定的动态库搜索路径：用选项-Wl,rpath和include指定的动态库的搜索路径，比如gcc -Wl,-rpath,include -L. -ldltest hello.c，在执行文件时会搜索路径`./include`；
2、环境变量LD_LIBRARY_PATH（多个路径用冒号分割）；
3、在 /etc/ld.so.conf.d/ 目录下的配置文件指定的动态库绝对路径（通过ldconfig生效，一般是非root用户时使用）；
4、gcc默认动态库目录：/lib:/usr/lib:usr/lib64:/usr/local/lib等。

```

其中可以用 查看gcc编译连接搜索的路径

```
ld --verbose | grep SEARCH_DIR

```