Linux-Network-Programming
Sample codes for Linux network programming based on socket and I/O multiplexing in C++11, revealing the essence of computer network communication through packet capture.
五种 I/O 模型
- 一个输入操作通常包括两个阶段
- 等待数据准备好,从网络中到达时复制到内核的某个缓冲区
- 从内核缓冲区向进程缓冲区复制数据
- 同步 I/O 操作的四种模型都会导致请求进程阻塞,直到 I/O 操作完成,它们第二阶段都一样,主要区别在第一阶段
- blocking I/O(阻塞 I/O):最常见的模型,Linux 中默认所有 socket 都是阻塞的。进程调用 recvfrom,内核等待数据到达,用户线程阻塞,内核接收到数据后,将其拷贝到进程缓冲区,并返回结果,此时用户才解除阻塞状态。整个 I/O 请求过程中,用户线程都是阻塞的,不能做任何事,CPU 资源利用率低
- non-blocking I/O(非阻塞 I/O):即轮询内核。进程调用 recvfrom 时没有数据返回,内核不阻塞用户,而是返回一个 EWOULDBLOCK 错误,虽然没读取任何数据,但用户能马上得到返回结果。用户收到错误码,知道内核还没准备好,会不断发送请求直到内核准备好数据,此时内核再把数据拷贝到进程缓冲区然后返回。轮询会消耗大量 CPU 资源,一般很少直接用非阻塞 I/O,而是在其他 I/O 模型中使用这一特性
- I/O multiplexing(I/O 多路复用):最常用的 I/O 模型,可以理解为某些进程需要的一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个 I/O 条件就绪(即输入已准备好被读取,或描述符已能承接更多的输出),它就通知进程。I/O 多路复用建立在内核提供的 select 函数的基础上,用于避免非阻塞 I/O 的轮询等待问题。用户先将要进行 I/O 操作的文件描述符放到 select 中,select 会阻塞,直到当文件描述符可读时返回。select 的优势在于可以直接用单线程监听多个文件描述符,如果用阻塞 I/O 则需要多线程才能实现此目的。 select 会阻塞线程(但不阻塞 socket),所以 I/O 多路复用模型也叫异步阻塞 I/O 模型,但不算真正的异步 I/O
- signal-driven I/O(信号驱动式 I/O):通过 sigaction 函数安装信号处理函数,如果数据不可用,进程继续工作而不会阻塞,当数据准备好时,内核为该进程产生一个 SIGIO 信号,随后即可以在信号处理函数中调用 recvfrom 读取数据,并通知主循环数据已准备好待处理,也可以立即通知主循环让它读取数据报。这种模型的优势在于等待数据到达期间不被阻塞
- 异步 I/O 操作
- asynchronous I/O(异步 I/O):通知内核启动某个操作,内核在完成操作后通知用户。信号驱动模型是内核通知用户可以启动 I/O 操作的时机,而异步 I/O 是通知完成 I/O 操作的时机。异步 I/O 相比于 I/O 多路复用并不常用,I/O 多路复用加上多线程任务处理架构能满足大多数高性能并发程序要求
- 同步异步的区别:同步发出调用时,没得到结果就不返回,异步 I/O 发出调用不会马上得到返回结果。简单来说,同步 I/O 必须做完一件事才会做下一件事,会阻塞线程的 I/O 操作就是同步 I/O,因此前四种都是同步 I/O