nio简单记录
nio相关
nio和epoll之间的关系
linux中是如何收发包的
分层
网络分为四层:应用层--> tcp层--> ip层 --> 数据链路层 --> 物理设备
和操作系统对应关系:
应用层 --> 用户进程
tcp层,ip层 --> 内核进程
数据链路层 --> 网络设备驱动程序 存放于(driver/net/enthernet/intel)
物理设备 --> 网卡网线
网络启动顺序
在网络收发消息的过程中,需要经历过网卡,网络驱动,内核协议栈,中断,软中断等一些列相关过程.在网络初始化的时候需要将这些需要的基础设施启动起来.
1.启动软中断处理线程
2.初始化软终端处理程序映射表
3.网络协议栈注册,不同的协议栈,会有不同的处理函数/
4.网卡驱动初始化,并准备好DMA程序
5.启动网卡
网络数据处理
- 网卡和网络驱动程序是如何获取到网络包的
1.网络中有数据包到达网卡
2.网卡的DMA将数据发送到内存的RingBuffer中,然后想cpu发送通知
3.cpu响应中断,这里知识设置了软中断的事务信息
4.软中断程序发现有中断,屏蔽硬件中断,开始处理软中断
5.适用Poll函数接受软件包,同时将对应的包调用响应的处理程序交给IP处理包UDPorTCP处理包开始处理.以下就是内核态线程开始处理部分了.
- 内核中是如何处理UDP包的
udp内核中是如何处理网络包的,是如何将数据放到socket的缓冲队列中的
1.首先根据网络的SKB标记查找对应的Socket,如果没找到,就直接发送目标不可达的包
2.将当前的消息发送到socket的缓冲接收队列中,如果接收队列是满的,就放到backlog队列中.如果接收队列还是满的,可以丢弃消息.
然后适用系统调用的recv_from系统调用是,试图从socket中获取到数据包.
1.通过从 socket其中的读取缓冲区中的数据,如果缓冲区不为空,就返回数据,如果数据为空,且用户允许等待,就等待.
tcp包是如何处理网路连接
- 调用socket.create原语创建一个socket对象.这个过程会初始化socket,设置协议栈,设置数据队列.返回对应的fd.
- 用户进程更创建的fd找到对应的socket,并从socket的队列中获取到对应数据,队列中数据为空,当前进程会向socket的等待队列中添加一个对象,记录了当前队列的回调函数.然后调用系统中断,中断当前进程,阻塞当前进程.
- 网卡中接收到了客户端发送过来的消息,DMA将数据移动到内核的一块内存中.
- dma发送中断给cpu,通知网络消息到来.
- cpu触发中断,并触发软中断
- 软中断程序读取内核中ringbuffer中的数据,调用相关的协议栈,解析出 网路连接四要素
- 软中断程序根据网络连接四要素找到一个socket,然后将对应解析出来的数据设置到socket的缓冲区中.
- 从等待队列中唤醒对应的进程.这个过程中会触发一次特权切换.
- 总结
这个当中,消息的等待和消息的到来,这个过程中也会有大量的特权切换,一次网络的读取至少有两次网络特权切换,这样会导致系统浪费在这种特权切换中.
epoll是如何接受网络包的?
epoll的实现方式:
listen(8080)
fd1=accept()
fd2=accpet()
efd=epoll.create();
epoll.ctl(efd,fd1)
epoll.ctl(efd.fd2)
epoll.await();//阻塞
1.用户首先创建一个网络连接,监听某个端口
2.accept获取到一个socket,socket对象中会包括很多对象,其中一个是数据接收区;等待的回调区域.
3.调用epoll创建一个Epoll对象.epoll对象中包括三部分:1.就绪队列,2.一个红黑树结构的socket列表,3.等待队列.
4.将一个socek的fd注册到epoll上来,这个步骤中需要完成:1.将当前的fd封装为一个epItem对象,其中会包括了socket的fd,以及生成一个回调函数的地址.2.将这个epitem添加到epoll的socket列表.3.将当前的回调函数添加到socket的等待回调队列中.
5.调用epoll的awit接口,这个时候会查看当前就绪队列是否有以及联通的数据,如果有了就直接返回,如果没有就阻塞.
6.网卡来了网络请求.dma将网络上的数据copy到内核空间中.
7.发送硬件中断给cpu,通知有网络请求到来.
8.cpu响应硬件中断,将当前的任务请求进行封装,生成一个软中断,添加到软中断队列中,这个过程是关中断的.为了防止硬中断出现时间过长,丢失中断.
9.软中线程处理软中断,将当前内核空间中的数据拷贝出来,同时通过协议栈的处理,找出网络四要输,找到对应的socket,然后将对应的数据添加到socket的缓冲区中.然后调用回调处理函数.
10.回调处理函数中第一部是将当前socket添加到就绪socket中.
11.查看当前的epol中等待队列,将当前的线程进行唤醒.
网络中是如何发送包的
系统通过socket发送一条消息处理的过程如下:
1.调用sendTo()系统调用,通过fd找出对应的Socket对象,以及构造msgHdr对象.
2.开始处理tcp层的炒作,申请skb拷贝内存,滑动窗口管理设置tcp的报文头
3.开始ip层次的管理,查找下一跳路由地址,对可能大于mtu的网络包进行分包.同时设置上ip地址.
4.邻居子系统,将当前报文中添加上mac地址.
5.选择可用的RingBuffer元素关联发送的包
6.发送完毕后,发起硬件中断清理网络发送.