基础概念
阻塞
请求资源得不到满足停下来等待
同步 异步
同步 : 多个线程不能同时进行,一个个来
异步: 多个线程可以同时开工
i/o
两个状态
- 读入/写出数据的过程
- 等待读入/写出数据的过程
阻塞io
用户线程运行在用户空间,数据没有到达用户空间,则用户线程被阻塞在io上等待数据或拷贝数据上。
- 等待数据或拷贝数据??
非阻塞io
用户数据拷贝到用户空间以后,去通知用户线程去执行
同步阻塞io
等待数据或拷贝数据,线程都在阻塞
等待数据:线程采用死循环轮训
拷贝数据:线程阻塞
没有同步非阻塞io
io
- 哪些地方用到:
tomcat、redis、kafka、nginx、mysql、libuv、netty - 怎么实现
只要运行在linux 基于linux - 学习路径
io->网络通信io(socket) ->bio ->nio 多路复用 ->netty
io建立连接 与请求
分两种:
- 一种 建立连接没有请求
- 建立连接有请求
java的线程
调用内核的系统调用,当做一个轻量级进程,他们会以进程id来表示
操作系统层网络io处理对应的三步
fd:3
端口:8090
了解socket 中的阻塞
strace linux指令 追踪java线程
tcp->socket
socket 得到一个fd->bind 绑定fd 到端口->listen监听fd->accept 返回clone线程fd->clone->recv 等待资源返回到fd
accept代表 客户端过来的请求是本机ip并且 是8090端口号,交给server 3处理
当有请求访问8090端口后
主线程克隆一个子线程将此客户端交给子线程处理 8281子线程返回
clone多个子线程
- .accept() 等待客户端请求 接收用户端连接 会阻塞
- inputStreamReader(in); 如果客户端不发送数据 会阻塞
5是代表客户端请求
操作系统内核升级
(学习计组原理)
网络通信io演变过程
bio 同步阻塞
会位每一个连接分配一个线程
主线程 clone一个线程,将此socket对应到此线程阻塞,每一个socket对应一个线程
优势
可以接收很多的连接
弊端
- 线程内存浪费
- cpu调度消耗
- 根源
阻塞(blocking) accept 、recv
同步非阻塞nio
注册特定的io事件,在发生特定事件时,系统再通知我们,核心对象selecter。注册事件发生时,从selector中获得selectionKey,找到相应的selectableChannel以获得以获得客户端数据。
- 非阻塞指io事件本身不阻塞,获取io事件select()是阻塞的,这时候io操作还没有发生所以我们认为他不阻塞。
- 本质是延迟io操作到真正发生io的时候。
核心
- channels
- buffers
- selectors
非核心 - pipe
- filelock
channels
- stream单向 channels双向
- 不必关心数据源具体的物理结构。
- 用于在字节缓冲区和位于通道另一侧的实体(文件或套接字)传输数据。
主要实现
- filechannel 文件读写
- datagramChannel(udp)
- socketChannel(tcp)
- serverSocketChannel(坚挺新进来的tcp连接)
buffer
selector
单线程处理多个channel
一次轮询返回一个fd
文件描述符FD 一切皆文件
一个用户线程fd
java:流
io多路复用技术
可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的操作。
多路复用主要有三种技术:select,poll,epoll
“复用”指的是复用同一个recv调用
多路复用器
用一次系统调用找到所有io状态 尽量少的recv
允许一个程序监控多个fd
返回一个状态
select poll
select
一次轮询返回多个fd
select 1024个fd数量限制
poll
没有 限制
优势
通过一次系统调用,把fds传递给内核,内核进行遍历,减少了系统调用次数
弊端
重复传递fd 解决:内核开辟空间存储传来的fd 和 返回区
每次select、poll 都要遍历全量fd 解决:中断 callback
epoll 非阻塞
利用多核
空间换时间
- 在Linux内核中构建了一个文件系统 mmap就绪队列,该文件系统采用红黑树来构建,红黑树在查询、新增、删除的效率极高,保障了在存在大量活跃连接的情况下的性能。
- 其次Epoll红黑树上采用事件异步唤醒,内核监听I/O,事件发生后内核搜索红黑树并将对应节点数据放入异步唤醒的事件队列中。这就避免了无差别的轮询,不会因为连接数增加而导致性能的快速下降
- 数据从用户空间到内核空间采用mmap存储I/O映射来加速。该方法是目前Linux进程间通信中传递最快,消耗最小,传递数据过程不涉及系统调用的方法。这点大大提升了存在大量FD时数据拷贝的消耗。
- 有返回区 请求数据
函数 3个
create(int)就绪队列大小
ctl()控制fd增删改查
int nready=wait(fd,事件,大小,time)多长时间取一次 返回多个准备好了
水平触发 边缘触发
水平触发 一次性读完
边缘触发 多次读取
epoll 6种做法
- 单线程处理accept 多线程出来recv/send
- 多线程处理accept 多线程处理 recv/send
- 多线程epoll-wait()不区分accept与、recv/send
多进程 session独立 前后不关联 - 多进程epollwait()不区分 accept与recv/send nginx
linux 系统调用查看指令
man 2 系统调用
tail -f out 8211
netstat -natp