这一节开始学习操作系统相关原理，把大学时候老师教的东西再从新吃回来。本节涉及如下知识点：

- CPU密集型
- I/O密集型
- 同步，异步


# 一.CPU密集型与I/O密集型

关于这两个概念其实就是对某类资源消耗比较多的程序称为`XXX密集型(bound)`

比如app_A运行占用的CPU 90%居高不下，我们说属于CPU密集型；如果说它处理一些运算，大量计算或者图片处理耗时过长，cpu占用率高，我们也称之cpu密集型； 

如果app_B运行时频繁地（相对来说CPU等其它资源较空闲时）读取或写入磁盘我们就称之为是“I/O密集型”或者“磁盘密集型”；如果静态图片访问的web应用程序，它可能就是“I/O密集型”或者“磁盘密集型”； 

如果app_C在大用户量并发访问时占用更多的带宽而CPU、磁盘等其它资源较空闲，就可以称之为是“带宽密集型”；

**知道属于那种密集型我们就能找出其性能瓶颈，对症下药了。** 那接下来我们从概念上了解它们。

## 1. CPU密集型
[CPU密集型(CPU-bound)](https://en.wikipedia.org/wiki/CPU-bound)， 在多重程序系统中，大部分时间用来做计算、逻辑判断等CPU动作的程序称之CPU bound。

CPU bound 指的是系统的 硬盘/内存 效能 相对 CPU 的效能 要好很多，此时，系统运作，大部分的状况是 CPU Loading 100%，CPU 要读/写 I/O (硬盘/内存)，I/O在很短的时间就可以完成，而 CPU 还有许多运算要处理，CPU Loading 很高。

## 2. I/O密集型

I/O bound 指的是系统的CPU效能相对硬盘/内存的效能要好很多，此时，系统运作，大部分的状况是 CPU 在等 I/O (硬盘/内存) 的读/写，此时 CPU Loading 不高。


在[Nginx比较Apache：事件驱动适合于IO密集型服务，多进程或线程适合于CPU密集型服务](http://codinglife.sinaapp.com/?p=40) 一文中作者这样说：

>事件驱动适合于IO密集型服务，多进程或线程适合于CPU密集型服务，它们各有各的优势，并不存在谁取代谁的倾向。

## 3.Node之与I/O bound, CPU bound

对于Node来说，**Node擅长I/O密集型，I/O密集的优势主要在于Node利用事件循环的处理能力，而不是启动每一个线程为每一个请求服务，资源占用极少**, 那么Node是否不擅长CPU密集型业务，在《深入浅出nodejs》一书中，作者测试使用各种脚本语言并进行了n = 40的斐波那契数列计算，得出的结果中Node表现的也不错，都比python高了， 这里我也分别使用python和node 在不使用缓存的基础上测试如下：

Python: 耗时32454毫秒，等了好久...

	#!/usr/bin/env python
	# encoding: utf-8

	import datetime

	fib = lambda n: 1 if n <= 2 else fib(n-1)+fib(n-2)

	if __name__ == '__main__':
	    n = 40
	    s = datetime.datetime.now()
	    print fib(n)
	    e = datetime.datetime.now()
	    print(int((e-s).total_seconds()*1000))
	    # outPrint: 102334155
	    # use time: 32454  毫秒

NodeJs: 耗时1853毫秒，等了一会

	function Fib(n) { 
	    return n < 2 ? n : (Fib(n - 1) + Fib(n - 2)); 
	}

	var timebgein = (new Date()).getTime();
	console.log(Fib(40));
	var timeend = (new Date()).getTime();
	console.log("执行时间是: " + eval(timeend - timebgein) + "毫秒")

看来V8引擎还是挺强悍的, 如果基于浏览器测试那段js程序则耗时37478毫秒。。。

作者说：

>CPU密集型应用给Node带来的挑战主要是：由于JavaScript单线程的原因，如果有长时间运行的计算（比如大循环），将会导致CPU时间片不能释放，使得后续I/O无法发起。但是适当调整和分解大型运算任务为多个小任务，使得运算能够适时释放，不阻塞I/O调用的发起，这样既可同时享受到并行异步I/O的好处，又能充分利用CPU。

这里推荐阅读：[Node.js软肋之CPU密集型任务](http://www.infoq.com/cn/articles/nodejs-weakness-cpu-intensive-tasks/)，关于此问题后期专题解决。

# 二.网络编程

人们总结出了四种典型的网络编程策略：

- 服务器为每个客户端请求分配一个线程/进程，使用阻塞式I/O。Java就是这种策略，Apache也是，这种策略还是很多交互式应用的首选。因为阻塞，这种策略很难实现高性能，但非常简单，可以实现复杂的交互逻辑。
- 服务器用一个线程处理所有客户端请求，使用非阻塞的I/O及事件机制。node.js采用的就是这种策略。这种策略实现起来比较简单，方便移植，也能提供足够的性能，但无法充分利用多核CPU资源。
- 服务器会分配多个线程来处理请求，但每个线程只处理其中一组客户端的请求，使用非阻塞的I/O及事件机制。这是对第二种策略的简单改进，在多线程并发上容易出现bug。
- 服务器会分配多个线程来处理请求，但每个线程只处理其中一组客户端的请求，使用异步I/O。这种策略在支持异步I/O的操作系统上性能非常高，但实现起来很难，主要用在windows平台上。

## 1. nodejs网络编程
因为大多数网站的服务器端都不会做太多的计算，它们只是接收请求，交给其它服务（比如文件系统或数据库），然后等着结果返回再发给客户端。所以聪明的Node.js针对这一事实采用了第二种策略，它不会为每个接入请求繁衍出一个线程，而是用一个主线程处理所有请求。避开了创建、销毁线程以及在线程间切换所需的开销和复杂性。这个主线程是一个非常快速的event loop，它接收请求，把需要长时间处理的操作交出去，然后继续接收新的请求，服务其他用户。下图描绘了Node.js程序的请求处理流程：

![](http://beginman.qiniudn.com/nodeJs.png)

主线程event loop收到客户端的请求后，将请求对象、响应对象以及回调函数交给与请求对应的函数处理。这个函数可以将需要长期运行的I/O或本地API调用交给内部线程池处理，在线程池中的线程处理完后，通过回调函数将结果返回给主线程，然后由主线程将响应发送给客户端。那么event loop是如何实现这一流程的呢？这要归功于Node.js平台的V8引擎和libuv。

## 2.理解阻塞非阻塞与同步异步

知乎上这样一个Question: [怎样理解阻塞非阻塞与同步异步的区别？](http://www.zhihu.com/question/19732473) 回答的非常好，由于自己造诣不深，就不误人子弟了，直接看上面的链接就ok了。接下来就研究node的异步机制吧。






