问题
<h2>问题</h2>
<ol>
<li>
<p>队列的本质是什么?涉及几部分内存?
本质是 ring buffer,涉及 2 部分内存:一个是 ring buffer,一个是 DMA 相关。</p>
</li>
<li>
<p>网卡收到数据后会 DMA 到内存,这个内存,是否就是上面申请的那个?</p>
</li>
<li>
<p>request_irq 如何选择中断绑定的 CPU ?和中断亲和性是什么关系?</p>
</li>
<li>
<p>有机会好好梳理下网卡的中断设置</p>
</li>
<li>
<p>local_bh_disable() 作用?
主要就是增加软中断计数,可以避免执行软中断</p>
</li>
<li>
<p>调用 send() 成功返回,是否意味着数据已经成功发送给对端?
从代码来看,会触发发送,但对方不一定收到,可能在中途被丢包了。</p>
</li>
<li>本机通信时,如果数据量大于 lo 的 MTU 是否会分包?</li>
</ol>
<h2>收包过程</h2>
<ol>
<li>
<p>数据包到达网卡,网卡将数据 DMA 到 ring buffer,触发硬件中断 -- 硬件中断和队列的关系?硬件中断具体做了什么?ring buffer 是指针数组吗?</p>
<p>ring buffer 并不是简单的指针数组(但效果上似乎是的),实际申请了 2 个环形队列数组,但似乎都没有直接存放数据包数据。
中断如果占用太长的时间,会导致其它中断响应不过来,因此中断处理应该非常简单。
每个队列都会申请一个中断,中断触发时,执行 igb_msix_ring() 函数,它是在 igb_main.c 中定义的,相关函数如下:</p>
</li>
</ol>
<pre><code class="language-c">// 总的来说,就是获取中断关联的 q_vector 中的 napi,将其加入到每 CPU 的 sd-&gt;poll_list 中
static irqreturn_t igb_msix_ring(int irq, void *data)
{
struct igb_q_vector *q_vector = data; // 每个 q_vector 上面都有一个 napi
/* Write the ITR value calculated from the previous interrupt. */
igb_write_itr(q_vector);
napi_schedule(&amp;q_vector-&gt;napi);
return IRQ_HANDLED;
}
void __napi_schedule(struct napi_struct *n)
{
unsigned long flags;
local_irq_save(flags);
____napi_schedule(&amp;__get_cpu_var(softnet_data), n);
local_irq_restore(flags);
}
static inline void ____napi_schedule(struct softnet_data *sd,
struct napi_struct *napi)
{
list_add_tail(&amp;napi-&gt;poll_list, &amp;sd-&gt;poll_list); // 将 napi 加入到 sd-&gt;poll_list 中
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}
// 对于 q_vector-&gt;napi 初始化如下(此函数会根据队列数进行 for 循环调用)
static int igb_alloc_q_vector(struct igb_adapter *adapter,
int v_count, int v_idx,
int txr_count, int txr_idx,
int rxr_count, int rxr_idx)
{
// ...
/* allocate q_vector and rings */
q_vector = kzalloc(size, GFP_KERNEL);
if (!q_vector)
return -ENOMEM;
/* initialize NAPI */
netif_napi_add(adapter-&gt;netdev, &amp;q_vector-&gt;napi,
igb_poll, 64); // 在此初始化,这里就是对应的 poll 函数
// ...
}
// 来看看 poll 函数
/**
* igb_poll - NAPI Rx polling callback
* @napi: napi polling structure
* @budget: count of how many packets we should handle
**/
static int igb_poll(struct napi_struct *napi, int budget)
{
struct igb_q_vector *q_vector = container_of(napi,
struct igb_q_vector,
napi); // 获取到原始的 q_vector
bool clean_complete = true;
#ifdef CONFIG_IGB_DCA
if (q_vector-&gt;adapter-&gt;flags &amp; IGB_FLAG_DCA_ENABLED)
igb_update_dca(q_vector);
#endif
if (q_vector-&gt;tx.ring)
clean_complete = igb_clean_tx_irq(q_vector); //
if (q_vector-&gt;rx.ring)
clean_complete &amp;= igb_clean_rx_irq(q_vector, budget);
/* If all work not completed, return budget and keep polling */
if (!clean_complete)
return budget;
/* If not enough Rx work done, exit the polling mode */
napi_complete(napi);
igb_ring_irq_enable(q_vector);
return 0;
}</code></pre>
<p>> 中断响应分为上半部和下半部。上半部应该是中断处理函数,是需要马上要处理的;下半部是可以慢慢处理的,一般是:软中断,tasklet 等机制。</p>
<ol>
<li>
<p>硬件中断处理函数,登录并触发软中断 -- 具体是如何处理的,每次都会触发软中断吗?poll_list 到底如何理解?硬件中断是如何设置的?/proc/interrupts 中记录的是软中断吗?</p>
<p>硬中断处理函数如上,主要就是将 q_vector->napi 加入到每 CPU 的 sd->poll_list 中,并触发软中断。从代码的角度,几乎肯定会触发软中断。
硬件中断的具体细节,还有待进一步分析。
/proc/interrupts 记录的是 irq 的触发情况,这并不是软中断(似乎就是硬中断),一般来说,软中断是指 softirq 进程处理的中断。</p>
</li>
<li>
<p>软中断函数执行,执行 poll 函数 -- 软中断具体是什么时候执行?</p>
<p>软中断执行的是固定函数 net_rx_action,里面获取当前 CPU 的 softnet_data,从 softnet_data->poll_list 中依次获取 napi 对象 n,并调用 n->poll() 进行具体处理。
硬中断处理函数仅触发软中断,只是在当前 CPU 的 NET_RX_SOFTIRQ 的标记位置 1,没有看到主动触发调用。
软中断触发的时机:1、硬中断处理结束退出时,会检查激活 softirqd;2、</p>
</li>
<li>
<p>poll 函数是网卡注册的,控制走协议栈 -- poll 函数具体做了什么?如果控制走协议栈?</p>
</li>
<li>
<p>在哪里控制发给应用层?如果应用层收包不及时会怎么样?</p>
</li>
<li>所有具体的收包流程都是在软中断来处理的吗?如果数据包过多,会不会影响其它的中断功能?</li>
</ol>
<h2>其它</h2>
<ol>
<li>发包过程是如何的?由谁来检查是否有包要发?</li>
</ol>