Linux网络开始收发包之前需要做的事情

1、创建ksoftirqd内核进程

2、网络子系统初始化

3、内核网络协议栈注册

4、网卡驱动初始化

5、启动网卡

以上5步就是我们在收发包之前内核要做的事情。

1、创建ksoftirqd内核进程

ksoftirqd内核进程也称为 per_cpu进程(每一个cpu核都有一个)。其由smpboot_register_percpu_thread函数创建。
在kernel/smpboot.c中调用smpboot_register_percpu_thread函数

int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread)
{unsigned int cpu;int ret = 0;get_online_cpus();mutex_lock(&smpboot_threads_lock);for_each_online_cpu(cpu) {ret = __smpboot_create_thread(plug_thread, cpu);if (ret) {smpboot_destroy_threads(plug_thread);goto out;}smpboot_unpark_thread(plug_thread, cpu);}list_add(&plug_thread->list, &hotplug_threads);
out:mutex_unlock(&smpboot_threads_lock);put_online_cpus();return ret;
}
EXPORT_SYMBOL_GPL(smpboot_register_percpu_thread);

在这里我们要搞懂俩个事情,这个函数是被谁调用的,它最终做了什么,至于里面的细节暂时先不谈。
一、这个函数是谁调用的:
下面一张截图是其调用流程

early_initcall(spawn_ksoftirqd) 中调用smpboot_register_percpu_thread函数。内核启动过程中会有start_kernel --> rest_init这个流程,在reset_init会创建一个内核线程,执行kernel_init,early_initcall需要在SMP初始化之前被调用,其在do_pre_smp_initcalls函数中被调用。early_initcall宏的调用调用时刻有点复杂,暂时先不深入了解。
ksoftirq进程其实是由kthreadd进程创建的,其是所有Linux内核线程的鼻祖,swapper进程是init进程和kthreadd进程的父进程,是Linux启动的第一个进程;

kernel_init --> kernel_init_freeable --> do_pre_smp_initcalls|---> do_basic_setup --> do_initcalls

二、这个函数执行完后发生了什么:
1、为每一个cpu核都创建了一个ksoftirqd进程*(kthread_create_on_cpu),回调函数是smpboot_thread_fn。
追踪代码发现实际上是调用到了__kthread_create_on_node函数创建ksoftirqd进程*,创建的过程如图所示。

分配一个kthread_create_info结构体,并初始化它,然后加入到kthread_create_list全局链表中,此链表是内核线程链表,所有的内核线程都在里面。并使用wake_up_process唤醒ksoftirqd进程。

2、线程被唤醒后会执行回调函数,在回调函数smpboot_thread_fn会阻塞休眠,一旦有软中断需要处理(进程run),就会调用softirq_threads->thread_fn。

3、在softirq_threads->thread_fn中开始执行软中断处理函数。

下面是完整的代码流程,涉及到/kernel/softirq.c和/kernel/smpboot.c。

在/kernel/softirq.c中初始化softirq_threads结构体,并调用smpboot_register_percpu_thread
static struct smp_hotplug_thread softirq_threads = {.store         = &ksoftirqd,.thread_should_run    = ksoftirqd_should_run,.thread_fn      = run_ksoftirqd,.thread_comm       = "ksoftirqd/%u",
};
BUG_ON(smpboot_register_percpu_thread(&softirq_threads));/kernel/smpboot.c
int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread)
{unsigned int cpu;int ret = 0;get_online_cpus();mutex_lock(&smpboot_threads_lock);for_each_online_cpu(cpu) {ret = __smpboot_create_thread(plug_thread, cpu);if (ret) {smpboot_destroy_threads(plug_thread);goto out;}smpboot_unpark_thread(plug_thread, cpu);}list_add(&plug_thread->list, &hotplug_threads);
out:mutex_unlock(&smpboot_threads_lock);put_online_cpus();return ret;
}
EXPORT_SYMBOL_GPL(smpboot_register_percpu_thread);__smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
{struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);struct smpboot_thread_data *td;if (tsk)return 0;td = kzalloc_node(sizeof(*td), GFP_KERNEL, cpu_to_node(cpu));if (!td)return -ENOMEM;td->cpu = cpu;td->ht = ht;在这里创建kthread进程,实际最终调用的是上面讲的__kthread_create_on_node函数,回调函数为smpboot_thread_fn。tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu,ht->thread_comm);if (IS_ERR(tsk)) {kfree(td);return PTR_ERR(tsk);}kthread_set_per_cpu(tsk, cpu);/** Park the thread so that it could start right on the CPU* when it is available.*/kthread_park(tsk);get_task_struct(tsk);*per_cpu_ptr(ht->store, cpu) = tsk;if (ht->create) {/** Make sure that the task has actually scheduled out* into park position, before calling the create* callback. At least the migration thread callback* requires that the task is off the runqueue.*/if (!wait_task_inactive(tsk, TASK_PARKED))WARN_ON(1);elseht->create(cpu);}return 0;
}在回调函数smpboot_thread_fn会一直循环去检测是否需要执行软中断处理,一旦有软中断需要处理,就会调用smp_hotplug_thread->thread_fn。
static int smpboot_thread_fn(void *data)
{struct smpboot_thread_data *td = data;struct smp_hotplug_thread *ht = td->ht;while (1) {set_current_state(TASK_INTERRUPTIBLE);preempt_disable();if (kthread_should_stop()) {__set_current_state(TASK_RUNNING);preempt_enable();/* cleanup must mirror setup */if (ht->cleanup && td->status != HP_THREAD_NONE)ht->cleanup(td->cpu, cpu_online(td->cpu));kfree(td);return 0;}if (kthread_should_park()) {__set_current_state(TASK_RUNNING);preempt_enable();if (ht->park && td->status == HP_THREAD_ACTIVE) {BUG_ON(td->cpu != smp_processor_id());ht->park(td->cpu);td->status = HP_THREAD_PARKED;}kthread_parkme();/* We might have been woken for stop */continue;}BUG_ON(td->cpu != smp_processor_id());/* Check for state change setup */switch (td->status) {case HP_THREAD_NONE:__set_current_state(TASK_RUNNING);preempt_enable();if (ht->setup)ht->setup(td->cpu);td->status = HP_THREAD_ACTIVE;continue;case HP_THREAD_PARKED:__set_current_state(TASK_RUNNING);preempt_enable();if (ht->unpark)ht->unpark(td->cpu);td->status = HP_THREAD_ACTIVE;continue;}if (!ht->thread_should_run(td->cpu)) {preempt_enable_no_resched();schedule();} else {__set_current_state(TASK_RUNNING);preempt_enable();ht->thread_fn(td->cpu);}}
}
static void run_ksoftirqd(unsigned int cpu)
{local_irq_disable();if (local_softirq_pending()) {/** We can safely run softirq on inline stack, as we are not deep* in the task stack here.*/__do_softirq();local_irq_enable();cond_resched();return;}local_irq_enable();
}

从数据结构的角度来看,总共涉及到三个结构体和一个链表,他们的关系如截图所示。

Linux网络开始收发包之前需要做的事情——创建ksoftirqd内核进程相关推荐

  1. linux 内核vxlan收发包流程

    1.vxlan口收包处理流程 前面几章详细介绍了vxlan接口的使用,而且已经知道vxlan是 MAC IN UDP中的封装,因此,在解封装之前,一切按照原有流程走,在此复习一下内核收发包流程(驱动层 ...

  2. ixgbe网卡驱动 Ⅳ----收发包流程详解

    目录 1 网卡队列收包流程概述 2 ixgbe_ring 结构 3 ixgbe 驱动收包流程 3.1 硬件中断入口 ixgbe_msix_clean_rings/ixgbe_intr 3.2 软中断入 ...

  3. Linux网络数据包的揭秘以及常见的调优方式总结

    Linux网络数据包的揭秘以及常见的调优方式总结 (网易游戏运维平台) 关注我们,获一手游戏运维方案 lott 网易游戏业务 SRE, 专注于业务运维的质量和效率 , 喜欢研究 Linux 系统原理. ...

  4. RK3399平台开发系列讲解(网络篇)7.11、图解Linux网络包接收过程

    文章目录 一.Linux网络收包概况 二.Linux启动:网络接收准备工作 2.1 创建ksoftirqd内核进程 2.2 网络子系统初始化 2.3 协议栈注册 2.4 网卡驱动初始化 2.5 启动网 ...

  5. 深入理解 Cilium 的 eBPF(XDP)收发包路径:数据包在Linux网络协议栈中的路径

    Table of Contents 1 为什么要关注 eBPF? 1.1 网络成为瓶颈 1.2 eBPF 无处不在 1.3 性能就是金钱 2 eBPF 是什么? 3 为什么 eBPF 如此强大? 3. ...

  6. linux内核网络协议栈--数据包的网卡驱动收发包过程(二十五)

    网卡 网卡工作在物理层和数据链路层,主要由PHY/MAC芯片.Tx/Rx FIFO.DMA等组成,其中网线通过变压器接PHY芯片.PHY芯片通过MII接MAC芯片.MAC芯片接PCI总线 PHY芯片主 ...

  7. Linux 网络层收发包流程及 Netfilter 框架浅析

    本文作者:sivenzhang,腾讯 IEG 测试开发工程师 1. 前言 本文主要对 Linux 系统内核协议栈中网络层接收,发送以及转发数据包的流程进行简要介绍,同时对 Netfilter 数据包过 ...

  8. Linux网络协议栈:网卡收包分析

    Table of Contents 网卡收包 一,框架 二,初始化 三,驱动收包 四,内核处理 参考文章 推荐阅读 网卡收包 内核网络模块如何初始化? 内核如何通过网卡驱动收发数据包? 驱动收到的数据 ...

  9. Linux内核UDP收包为什么效率低?能做什么优化?

    现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK. 但是,即便Linux内核协议栈收包 ...

最新文章

  1. Centos6.5安装/运行/启动/登录docker
  2. c++构建工具之xmake使用实例
  3. 通过相似性或相异指数的数值分布比较群落Beta多样性高低
  4. Windows下cmd常用命令【5分钟掌握】
  5. 常用来进行钢结构节点输出的软件是什么_Revit中如何处理钢结构节点连接
  6. 阿里P7大牛亲自教你!BAT这种大厂履历意味着什么?积累总结
  7. MAC OS 命令行使用详解
  8. linux cpu频率软件,linux cpu频率控制
  9. jquery替换节点
  10. c语言例题 3/100
  11. linux bzip2 命令,Linux bzip2 命令的使用
  12. BZOJ 5442: [Ceoi2018]Global warming
  13. 挣脱注意力经济:为什么应该练习数字极简主义?
  14. git 将暂存区文件提交_git 暂存区
  15. Activiti表结构
  16. matlab画图线型、符号、颜色
  17. 大学计算机成绩构成,大学计算机平时成绩占多少?
  18. AML(Automation Markup Language)简单介绍
  19. 电子器件——钽电容的简介
  20. 异质化社群量化研究4丨RATE OF CHANGE WITH BANDS

热门文章

  1. 星巴克招人!要技术大拿。
  2. 记一次npm login失败的经历(npm WARN Username...)
  3. C# 字符串中去掉换行符、间隔符等特殊字符
  4. 4个万兆光口+8千兆combo光电复用口+16千兆电口万兆三层网管型工业以太网交换机HY5700-854XG8GC16GT
  5. 直播带货“老三”,抖音背上「KPI」了
  6. 基于MATLAB的矩阵基础(附例题与代码)
  7. PostgreSQL开源界的“活雷锋”
  8. 蜗牛星际 完美安装 ESXI6.7 全面教程(一)
  9. mysql绿色版本的安装
  10. 2021年中国皮革行业市场现状分析,PU合成革趋势明显,汽车皮革是关键需求「图」