当前位置: 首页 > 科技观察

一个Linux网络数据包从中断到接收的生命

时间:2023-03-19 21:09:23 科技观察

Linux既然要说,那就说说一个数据包的整个生命周期吧。触发中断在非虚拟化环境下,网卡通过DMARingqueuebuffer将数据包写入内核的rx_ring,触发中断。如果在虚拟化环境下,VMM配置GICITS(InterruptTranslationService),建立物理中断和虚拟中断的映射,完成中断虚拟化,让网卡直接向VM发送中断,通过IO虚拟化,网卡直接通过IOMMU传输数据包。写入虚拟机内核的rx_ringTopHalfCPU接收到中断后,调用网卡ISR,也就是所谓的中断处理程序,分配sk_buf并合并到input_pkt_queue中(如果队列满则丢弃)并发送一个软中断NET_RX_SOFTIRQ,可以调度,例如taskletBottomHalfsk_buf从input_pkt_queue转移到process_queue,根据协议类型调用网络层协议的handlerip_rcv进行包头检查,ip_router_input()进行路由,判断local/forwarding/discardingtcp_v4_rcv进行包头检查,tcp_v4_lookup查询对应的socket和连接,如果正常,tcp_prequeue将skb放入socket接收队列,然后socket唤醒进程kqueuewhere它位于。因为epoll没有论文,下面说说kqueue是怎么做的。kqueue会根据socket绑定knot列表(每个监听的kqueue可能会创建一个knot),通过指向kqueue的反向指针获取knot,将knot添加到kqueue的就绪队列的尾部。如果此时恰好有一个进程在监听,就会唤醒这个进程,扫描kqueue,从就绪队列中获取所有的事件,从而知道所有就绪的sockets。被唤醒的进程调用socketrecv系统调用。如果是TCP,则调用tcp_recvmsg从sk_buffer中拷贝数据。batchnetif_receive_skb_list()Linux的NAPI会不断延迟处理软中断,等待它积累足够的skb进行轮询,一次处理所有的skb。SKBskb不直接存储消息,而是存储指针。只需要移动指针即可完成解包,消息本身不需要修改。上层的协议栈会在处理当前层的同时设置下一层的头指针并移动数据指针。同时skb本身也是一个双向链表实现的队列。qlen是链表元素的长度,lock是添加元素时的锁。skb结构讲的是指针的使用。在进行操作系统实验室时,这是一个令人印象深刻的技巧。也是C指针异常的地方。#definelist_entry(ptr,type,field)\container_of(ptr,type,field)#definecontainer_of(ptr,type,field)\((type*)((void*)(ptr)-(u64)(&(((type*)(0))->field))))(u64)(&(((type*)(0))->field))))指的是字段在结构体类型中的偏移量。通过减去这个偏移量,我们可以找出一个对象所在的上层类型对象。地址是容器。一般来说,我们会用下面的方法让链表节点把数据包裹起来。structpage_list_node{structpagep;structlist_node*prev;structlist_node*next;};但是可以通过指针操作将数据包裹到链表节点structlist_head{structlist_head*prev;structlist_head*next;};在的情况下,可以借助成员偏移量和list_entry(somenode,structpage,list_node)知道容器对象的位置;list_head本身可以存在于任何对象上,但它们的条目可以根据参数指向不同的类型。感觉有点一般。内容来自SJTU,IPADSOS-16-Network