nio,epoll,多路复用
IO BIO NIO
网络 socket IO BIO NIO 多路复用器 ——> 学习 nginx redis netty tomcat
linux strace -ff捕捉到java一个线程还是多线程 -o捕捉内容写入临时文件 追踪捕捉进程线程调用的系统内核调用
strace -ff -o out /usr/java/j2sdk1.4.2/bin/java TestSocket
jps 能看到服务和PID
socket四元组,本地IP端口号,异地IP端口号
set nu 显示行数
vi
bind()系统内核调用方法
man 2 socket
java中的new ServerSocket 转化为 linux的几个方法——>socket,bind,listen,accept(3,阻塞了
28756主线程在监听
3监听的状态,8090端口
java jdk用C语言开发的,离不开C代码,java写的代码最终是用linux内核解释
tail -f out.28756 显示文件最后十行
nc localhost 8090 客户端链接
lsof 看进程状态
accept 3 返回了5连接上为文件描述符
new Thread ——>linux中为clone()=28994线程
cd /proc/28756/task 虚拟目录进程ID号
jps看到线程
cd task看到了进程中的线程,有一些内存共享
线程栈或进程栈是共享的
nc localhost 8090 多连接了一个线程29041
vi out.29041
recv(6,子线程在recv接收数据
28756主线程在等待客户端链接
lsof -p 28756 看到有5,6,7三个客户端连接
BIO的过程:
app(tomcat, netty, ngix)(服务端监听, while(true)接收,返回后new Thread)——>内核交互,sokcet(),bind(),listen(),accept(3,---阻塞,----接到客户端后抛出一个线程返回 = 5
——> recv(5,阻塞
int listen(int sokfd, int backlog最大连接数)
BIO弊端:每客户端 每线程,会抛出很多连接会有很多线程,C10K问题,需要有1万个线程,java线程就是一个操作系统进程,几个CPU的话,CPU有很多调度,浪费资源,连接数比较多的时候。4核心就4个连接8个连接模型可以满足
【阻塞】是致命的,需要阻塞变为非阻塞
程序员依赖kernel linux ——>内核要提供非阻塞的能力, kernel nonblock
socketNIO 引入java nio包
NIO——>java new
——>linux nonblocking
serversocketchannel ss = serversocketchannel.open();
ss.bind();
ss.configureBlocking();
socketchannel client = ss.accept()不会阻塞直接返回,返回两种状态-1还是文件描述符
读也不会阻塞 返回 = 5 -1
read(5, 返回 N 0 -1
非阻塞——>accept,read,write, -1 不会卡住
NIO C10K ,一个线程接收读取遍历,或者4个CPU各负责2500个线程
NIO C10K
弊端 1个线程 用户态,内核态切换的过程成本高,1万个连接,1个人发来了数据,但是你在一个循环里,全量遍历O(n)的复杂度系统调用
程序和内核离得比较远,交互成本高
计算机软件工程学,批量
多路复用器,1万条路复用一个电话
int select()内核 允许程序监控多个文件描述符,等待一个或多个文件描述符达到可用的状态
多路复用select,epoll
过程:
socket,listen
while(true)
先调一个select(3)
accept(3) = 5 ——> select(3, 5)
accept(3) = 6 ——> recv(5) ——> select(3, 5, 6)
select(3,5,6,7,8,9,10) O(1)
recv(5) O(m)
多路复用器(select ,poll, epoll),只能给app返回状态,不能够读取数据
程序自己需要调用 R/W 你的IO
软件工程学——>同步【IO】模型,无论是在当前线程还是其他线程,程序自己读写(NIO,BIO),java里的IO模型多是同步的
异步【IO】模型,没有阻塞
调用了内核的一个方法(callback)(内核帮你把数据读写)IOCP程序里不需要维护读写的过程
select 1024fd
poll没有数量限制,本质是一样的 select poll是一类
EPOLL 多路复用器,同步模型
select poll弊端:
每循环都重复传递数据
每次调用内核需要遍历用户传递的参数
本质是内核没有留存过数据
解决方法内核开辟空间,减少传递参数,加上中断,中断事件,调用的时候直接给结果集不用传递
man epoll
epoll_create(2),epoll_ctl(2)
man epoll_create
return value 返回一个文件描述符
epoll_ctl(epfd, op)
epoll_waite
app
socket, bind, listen
epoll_create = 5描述符内核开辟了一个空间为5
kernel 5
——>epoll_ctl(5, ADD, 3监听的
——>while(true) epoll_wait(5,*指针数据集合) //select(3,5,6,7,8)
——>accept(3) = 6新的客户端6
——>epoll_ctl(5, ADD, 6, read)
——>read(5)
kernel ——>5fd
3,accept——>epoll_wait()
异步为一种事件上的异步,整个的IO模型是同步的
从上到下贯穿的一个问题资源利用率的问题
java中包装了selector多路复用器为linux中的select,poll,epoll三种
ServerSocket
selecter.open()
server.register(selecter, selectKey.op_accept)
selector.select()就是select或者epoll_wait
启动的时候选参数可以设置为什么多路复用器
ngix根据内核优选选择是poll还是epoll
yum install streace
redis,nginx都用epoll
service nginx stop
strace -ff -o out ./nginx
ps -fe | grep nginx
vi out.29385
bind(6) = 0 监听
vi out.29386
vi out.29387 worker
epoll_create()
epoll_ctl()
epoll_wait()
strace -ff -o out /opt/bigdata/redis5/bin/redis
vi out.
epoll_create() - bind() - fcntl() - epoll_ctl()
lsof -p $$ 文件描述符
lsof -p 3947