用户角度:网卡-网卡之间通信
内核角度:socket-socket之间数据输入输出
文件:数据是已确定在磁盘上的,但是
网络:只是连接了一个端口,是否有数据到达,未确定
网络IO:需要先查,确定有数据才发起真正IO操作,2步
文件IO:直接读,1步
假设一个服务同时连接多个socket
Q:怎么判断什么时候socket有数据到达?
Q:是哪一个socket的数据到达?
用户进程层面轮询,拿fd一个一个扔给内核帮忙查,查到了告诉我,我去拷贝
内核层面轮询,用户进程扔一堆给内核,让内核轮询,哪个有结果了,通知去拷贝 (select,poll)
A.每个socket连接都返回一个fd,linux一切皆文件
B.用户进程无法直接问网卡是否有数据,需要拿sokcet连接分配的fd问内核,内核帮我们去查,得到内核返回的结果,再进行下一步
C.用户进程问内核,用户态到内核态切换,剩下交给内核处理
一次IO生命周期一般涉及两个阶段与两个对象
对象:用户进程和内核进程,
阶段:网卡-内核阶段,内核-用户阶段(针对接收,发送反之)
发送数据: 用户空间->内核->网卡-网络
接收数据: 网络->网卡->内核->用户空间
A.用户进程创建一个socket连接,然后开始把fd传给内核,开始监听数据
B.socket有一个接收缓冲区:recive buffer和写缓冲区writh buffer, 一个数据包到达网卡(NIC)时,首先会以DMA(存储器直接访问, 允许在 外部设备 和 存储器 之间直接读写数据)的方式把网卡的帧写入到内存,然后向CPU发送一个软中断,告诉内核去处理下,内核这是就会用select,poll,epoll_wait等来收包
C.内核监测到哪个fd有数据包到了,再通知用户,用户再调用read()系统调用读取数据
A:用户进程在创建socket连接时会返回一个fd,然后将fd加入到内核监听集合fds,有数据包到,内核会收到软中断信号,去遍历fds,看是哪个fd数据到达,然后标记好通知用户进程,
B:用户进程等到通知后,再次发起read()系统调用。内核去拷贝该socket的数据到用户进程缓存区,再通知用户进程,可以访问数据了
如何批量处理socket数据到达,常见5种IO模型:
阻塞IO
非阻塞IO
多路复用IO
信号驱动IO
异步IO
kernel会检查socket是否有数据到达,如果fd数据还没到达,kernel会一直阻塞,直到有数据才返回。
这是同步阻塞(BIO)模型
kernel检查fd的数据到了没,如果fd数据还没到达,返回error给kernel。然后过段时间,kernel再来问,不停轮询,直到有数据,结果返回给用户。
这是同步非阻塞(NIO)模型:是用户进程不停循环fds问kernel有没数据。
多个IO可以注册到一个复用器上,用户进程问kernel,一组fds的数据到了没,fds数据都没到达,kernel一等。任一fd数据到达,返回结果通知用户进程拷贝这个fd的数据。
循环fds有:select,poll这两种多路复用模型。
另一种:epoll则是把每个fd都注册一个回调函数,当一个fd有数据到达,回调通知这个fd的进程,过来读数据
在信号驱动IO模型中,当⽤户线程发起⼀个IO请求操作,会给对应的socket注册⼀个信号函数,然后⽤户线程会继续执⾏,当内核数据就绪时会发送⼀个信号给⽤户线程,⽤户线程接收到信号之后,便在信号函数中调⽤IO读写操作来进⾏实际的IO请求操作。这个⼀般⽤于UDP中,对TCP套接⼝⼏乎是没⽤的,原因是该信号产⽣得过于频繁,并且该信号的出现并没有告诉我们发⽣了什么事情
该模型很少使用
这是同步非阻塞(NIO)模型
前4中都可以归结为:同步IO
从整个IO过程来看,他们都是顺序执行的,进程主动等待且向内核检查状态
当进程发起一个IO操作,马上得到内核返回,可以先去处理其他事情了,注意数据可能不是马上得到;内核会一直查询fd数据是否到达,到达之后,顺便把数据拷贝到用户和内核都能访问得内存共享缓存区,然后告诉用户进程,可以直接拿到数据了。
实际上是信号驱动+拷贝数据。
这是异步非阻塞模型(AIO)
读写操作由内核完成,完成后内核将数据放到指定用户进程的缓冲区,通知应用程序来取
扩展:
Windows的IOCP实现了真正的异步IO
Linux系统下,异步IO模型在2.6版本才引入,目前并不完善,其底层实现仍使用epoll
大多数的高并发服务器端的程序,一般都是基于Linux系统的,因此大多还采用IO多路复用模型
卡那里的问题:用户进程-内核-socket
A. 用户进程卡在内核这里(同步)
B. 内核卡在socket这里(阻塞)
卡那里的问题:用户进程-内核-socket
A. 用户进程卡在内核这里(同步)
B. 内核不卡在socket这里(非阻塞,kernel定时轮询)
select,poll
(3)AIO:异步非阻塞
异步 IO 是基于事件和回调机制实现
epoll
先理解这几个概念:
异步:用户进程-(内核-socket)(用户马上可以得到内核查询socket有没数据的结果,然后去处理其他事情)
同步:用户拿fd问内核,这个fd有数据了没?内核查看fd的数据,有,返回给用户。若内核查到fd没数据,让用户进程等着,先不返回。定期再次查fd,循环往复,直到有数据才返回给用户进程。用户进程收到消息,再次发起读取系统调用,让内核拷贝数据到用户缓存区。
异步:用户拿fd问内核,这个fd有数据了没?内核查看fd的数据,有,返回给用户,没有返回err。用户若得到err,就先不等了,先去处理其他事情。剩下得事情交给内核了(fd数据到达,会主动通知用户)
返回结果得时机和复制fd数据的时机
同步是内核告诉用户进程数据来了。用户需再次发起调用,内核去复制fd的数据到用户缓存区
异步是内核直接fd的数据都给复制到了用户缓存区了,然后通知用户进程好了(缓存共享技术)。
(1)内核有没第一时间获取查询结果。内核-fd之间
阻塞:是指内核查fd数据是否到达,有数据才返回结果,否则一直在等待;
非阻塞:是指内核查fd数据是否到达,有数据返回结果,没数据返回err