1、Fast Path
Linux TCP/IP协议栈中,TCP曾有两条路径处理输入数据包:"Fast Path"、"Slow Path",Fast Path是内核优化TCP处理输入数据包方式,他是根据协议头来预定数据包的去向,Fast Path处理的条件是:
(1)、收到的数据段中包含的是数据,不是ACK。
(2)、数据段是顺序传送数据中的一个完整数据段,接受顺序正确。
(3)、收到数据段的套接字状态是ESTABLISHED。
满足以上条件数据段会加入到prequeue队列中,只是用户程序会被唤醒,比Slow Path处理省略了很多步骤,提高数据包的接受效率,prequeue队列定义在struct ucopy数据结构中。
/* Data for direct copy to user */
struct {
struct sk_buff_head prequeue; //prequeue队列
struct task_struct *task; //打开套接字的应用程序
struct iovec *iov; //数据存放缓冲区结构
int memory; //prequeu队列中数据长度
int len; //prequeue队列上缓冲区个数
#ifdef CONFIG_NET_DMA //DMA处理
/* members for async copy */
struct dma_chan *dma_chan;
int wakeup;
struct dma_pinned_list *pinned_list;
dma_cookie_t dma_cookie;
#endif
} ucopy;
2、Fast Path的初始化
Fast Path的初始化函数是tcp_prequeue_init,其实就是初始化struct ucopy结构体,struct ucopy结构体是struct tcp_sock的一个元素,所以Fast Path的初始化是在应用层打开AF_INET地址族上SOCK_STREAM类型套接字是调用tcp_v4_init_sock初始化套接字是调用,以下是tcp_prequeu_init函数:
static inline void tcp_prequeue_init(struct tcp_sock *tp)
{
tp->ucopy.task = NULL; //应用层处理数据包的进程
tp->ucopy.len = 0; //prequeue队列上缓冲区个数
tp->ucopy.memory = 0; //prequeue队列上数据包长度
skb_queue_head_init(&tp->ucopy.prequeue); //队列初始化
#ifdef CONFIG_NET_DMA
tp->ucopy.dma_chan = NULL;
tp->ucopy.wakeup = 0;
tp->ucopy.pinned_list = NULL;
tp->ucopy.dma_cookie = 0;
#endif
}
3、prequeue队列处理
将数据包加入到prequeue队列的函数是tcp_prequeue,加入prequeu队列的前提是应用层有进程打开了套接字在等待接受数据,通过struct ucopy数据域的task来判断应用层是否有套接字等待接受数据,加入prequeue队列后要更新队列中数据包的长度memory,如果应用层没有打开的套接字等待接受数据就返回0,加入prequeue队列失败。
static inline int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
//task域为空说明用户进程没
//有打开的套接字等待接受数据就返回0
if (sysctl_tcp_low_latency || !tp->ucopy.task)
return 0;
//将数据包加入到prequeue队列中
__skb_queue_tail(&tp->ucopy.prequeue, skb);
//更新prequeue队列中数据包的长度
tp->ucopy.memory += skb->truesize;
...
}
将数据段加入到prequeue队列后要判断队列中数据包的总长度是否大于接受缓冲区的长度,如果prequeue队列中的数据包中长度已经大于接受缓冲区长度,就要调用sk_backlog_rcv函数将prequeue队列中所有数据段转移到backlog queue队列中,由Slow Path路径函数处理,当prequeue队列中数据段转移完毕,将memory域置为0。
...
//如果prequeu队列长度超过接受缓冲区长度就
//将preuque队列中的数据包加入到 Slow Path队列中处理
if (tp->ucopy.memory > sk->sk_rcvbuf) {
struct sk_buff *skb1;
BUG_ON(sock_owned_by_user(sk));
//循环将prequeue队列中数据通过sk_backlog_rcv加入到backlog queue队列中
while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
sk_backlog_rcv(sk, skb1);
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPPREQUEUEDROPPED);
}
//prequeue队列长度置为0
tp->ucopy.memory = 0;
//prequeue队列中只要有一个数据包就要唤醒用户进程
...
当prequeue队列中数据段长度没有小于接受缓冲区就唤醒用户进程接受数据包,tcp_prequeue返回1表示加入prequeue队列成功,返回0表示加入失败。
以下是完整代码:
static inline int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
//task域为空说明用户进程没
//有打开的套接字等待接受数据就返回0
if (sysctl_tcp_low_latency || !tp->ucopy.task)
return 0;
//将数据包加入到prequeue队列中
__skb_queue_tail(&tp->ucopy.prequeue, skb);
//更新prequeue队列中数据包的长度
tp->ucopy.memory += skb->truesize;
//如果prequeu队列长度超过接受缓冲区长度就
//将preuque队列中的数据包加入到 Slow Path队列中处理
if (tp->ucopy.memory > sk->sk_rcvbuf) {
struct sk_buff *skb1;
BUG_ON(sock_owned_by_user(sk));
//循环将prequeue队列中数据通过sk_backlog_rcv加入到backlog queue队列中
while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
sk_backlog_rcv(sk, skb1);
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPPREQUEUEDROPPED);
}
//prequeue队列长度置为0
tp->ucopy.memory = 0;
//prequeue队列中只要有一个数据包就要唤醒用户进程
} else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
//唤醒用户进程接受数据包
wake_up_interruptible_sync_poll(sk_sleep(sk),
POLLIN | POLLRDNORM | POLLRDBAND);
if (!inet_csk_ack_scheduled(sk))
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
(3 * tcp_rto_min(sk)) / 4,
TCP_RTO_MAX);
}
return 1;
}
首先要在Linux环境下安装libpcap.
fctix-qt5 的源码有两个地方可以下载:wget https://download.fcitx-im.org/fcitx-qt5/fcitx-qt5-1.0.5.tar.xztar -xJf fcitx-qt5-1.0.5.tar.xzgit clone http://github.com/fcitx/fcitx-qt5.git6、7、8行 根据自己的安装位置进行修改。#...
全局查找LoaderTask(),里面有个loadAllApps方法loadAllApps方法里的 mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);就是luncher加载时添加的在里面的for循环里mBgAllAppsList.add(new AppInfo(app, user, quietMode), app)前会进行循环写一个方法在add前加上 if(DmConfig.isHidden
文章目录Tensorflow基础图、会话和Tensor常量、变量及占位符随机张量创建变量与变量的初始化Tensorflow 变量的保存与恢复placeholder占位符的使用fetch的用法参考链接Tensorflow基础简单整理图、会话、tensor、变量、feed和fetch的基本操作。学习资料的话网上有很多,推荐下北大的那个。使用图(graphs)来表示计算任务、在被称之为会话(Ses...
C6748烧写方法:Procedure to Flash and boot theLCDKhttp://processors.wiki.ti.com/index.php/L138/C6748_Development_Kit_(LCDK)#Procedure_to_Flash_and_boot_the_LCDK(参考官方网址)串口烧写方式
本文转载自【Houdini官方入门教程翻译】概述——文件管理文章目录概述项目目录场景文件 | .HIP数字资产 | .HDAHoudini Apprentice 和 Houdini Indie 的文件备份File SOP节点文件依赖路径 [$HIP / $JOB]Pre-flight 面板硬盘空间管理Interoperability(导入导出)概述想要成为一个成功的艺术家,了解如何管理你在Houdini创建的所有文件是非常重要的。一个典型的场景文件可能在磁盘上有外部依赖项,管理这些依赖项非常重要,特别
当我尝试在IntelliJ中创建项目时,我在此行收到以下错误:Sentence sent = new Sentence();sent.emptySegments();错误:Error:(151, 10) java: cannot access javax.xml.bind.RootElementclass file for javax.xml.bind.RootElement not foundS...
(翻译)BehaviorTree.CPP教程一:创建树 :https://www.behaviortree.dev/tutorial_01_first_tree/如何创建一个行为树行为树与状态机类似,只不过行为树是在正确的时间和条件下调用回调的一种机制。此外,我们将交替使用“回调”和“标记”这两个词。在这些回调中发生什么由你决定。在本系列教程中,大多数情况下Action只会在控制台打印一些信息,但请记住,真正的“production”代码可能会做一些更复杂的事情。如何创建自己的..
手把手教你在ubuntu下搭建ffmpeg+sdl2开发环境一:ffmpeg 开发环境搭建1:开发环境搭建1.1:安装必要软件apt-get install autoconf automake build-essential libass-dev libfreetype6-dev libtheora-dev libtool libvorbis-dev pkg-config texinfo ...
项目中使用swiper插件嵌套video标签正常的swiper插件里面嵌套video标签,如下写法就够了,在ios 和PC端上完全没有问题,但是在安卓机上,播放视频后,视频的层级会居上,覆盖住下面的层,导致左右滑块被遮挡,并且滑动video标签也无法滑动。<div class="swiper-container video-box"> <div class="swi...
概要:本文内容包含Linux源码树结构分析、Linux Makefile分析、Kconfig文件分析、Linux内核配置选项分析。这些知识是为了理解内核文件的组织形式,为具体移植内核做知识准备。一,Linux源码树结构分析对Linux源码树下个子目录内包含的内容进行列表罗列:arch:体系结构相关的代码,每一个子目录代表一种架构 block:块设备的通用函数 crypot:常用加密和散列算法、压缩和CRC校核算法 fs:Linux支持的文件系统,每一个子目录代表一种文件系统 inc.
服务器装有杀毒软件,并处在监制状态. 想上传成功必须双方都关了杀毒软件.灰鸽子是国内一款著名后门。比起前辈冰河、黑洞来,灰鸽子可以说是国内后门的集大成者。其丰富而强大的功能、灵活多变的操作、良好的隐藏性使其他后门都相形见绌。客户端简易便捷的操作使刚入门的初学者都能充当黑客。当使用在合法情况下时,灰鸽子是一款优秀的远程控制软件。但如果拿它做一些非法的事,灰鸽子就成了很强大的黑客工