二、系统初始化

Linux驱动,内核协议栈等等模块在具备接收网卡数据包之前,要做很多的准备工作才行。比如要提前创建好ksoftirqd内核线程,要注册好各个协议对应的处理函数,网络设备子系统要提前初始化好,网卡要启动好。只有这些都Ready之后,我们才能真正开始接收数据包。那么我们现在来看看这些准备工作都是怎么做的。

Linux的子系统、模块均定义了一定的启动级别,在start_kernel函数中,按顺序启动

/* initcalls are now grouped by functionality into separate * subsections. Ordering inside the subsections is determined* by link order. * For backwards compatibility, initcall() puts the call in * the device init subsection.** The `id' arg to __define_initcall() is needed so that multiple initcalls* can point at the same handler without causing duplicate-symbol build errors.*/#define __define_initcall(fn, id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" #id ".init"))) = fn; \LTO_REFERENCE_INITCALL(__initcall_##fn##id)/** Early initcalls run before initializing SMP.** Only for built-in code, not modules.*/
#define early_initcall(fn)      __define_initcall(fn, early)/** A "pure" initcall has no dependencies on anything else, and purely* initializes variables that couldn't be statically initialized.** This only exists for built-in code, not for modules.* Keep main.c:initcall_level_names[] in sync.*/
#define pure_initcall(fn)       __define_initcall(fn, 0)#define core_initcall(fn)       __define_initcall(fn, 1)
#define core_initcall_sync(fn)      __define_initcall(fn, 1s)
#define postcore_initcall(fn)       __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)
#define arch_initcall(fn)       __define_initcall(fn, 3)
#define arch_initcall_sync(fn)      __define_initcall(fn, 3s)
#define subsys_initcall(fn)     __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)
#define fs_initcall(fn)         __define_initcall(fn, 5)
#define fs_initcall_sync(fn)        __define_initcall(fn, 5s)
#define rootfs_initcall(fn)     __define_initcall(fn, rootfs)
#define device_initcall(fn)     __define_initcall(fn, 6)
#define device_initcall_sync(fn)    __define_initcall(fn, 6s)
#define late_initcall(fn)       __define_initcall(fn, 7)
#define late_initcall_sync(fn)      __define_initcall(fn, 7s)#define __initcall(fn) device_initcall(fn)

2.1 创建ksoftirqd内核线程

Linux的软中断都是在专门的内核线程(ksoftirqd)中进行的,因此我们非常有必要看一下这些进程是怎么初始化的,这样我们才能在后面更准确地了解收包过程。该进程数量不是1个,而是N个,其中N等于你的机器的核数。

系统初始化的时候执行spawn_ksoftirq -> smpboot_register_percpu_thread->smpboot_register_percpu_thread_cpumask->__smpboot_create_thread,

该函数创建出softirqd内核线程(位于kernel/softirq.c, 线程主函数smpboot_thread_fn)。

相关代码如下:

//file: kernel/softirq.c
static struct smp_hotplug_thread softirq_threads = {.store          = &ksoftirqd,.thread_should_run  = ksoftirqd_should_run,.thread_fn      = run_ksoftirqd,.thread_comm        = "ksoftirqd/%u",};
static __init int spawn_ksoftirqd(void){register_cpu_notifier(&cpu_nfb); // 为每个CPU创建一个处理软件中断的线程BUG_ON(smpboot_register_percpu_thread(&softirq_threads));return 0;
}
early_initcall(spawn_ksoftirqd);  // 将函数放至对应级别的初始化位置//file : kernel/smp_boot.c
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);   // 执行注册的软件中断函数}}
}

当ksoftirqd被创建出来以后,它就会进入自己的线程循环函数ksoftirqd_should_run和run_ksoftirqd了。不停地判断有没有软中断需要被处理。这里需要注意的一点是,软中断不仅仅只有网络软中断,还有其它类型。

//file: include/linux/interrupt.h
enum{HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,BLOCK_SOFTIRQ,BLOCK_IOPOLL_SOFTIRQ,TASKLET_SOFTIRQ,SCHED_SOFTIRQ,HRTIMER_SOFTIRQ,RCU_SOFTIRQ,
};

Linux数据报文接收发送总结4相关推荐

  1. Linux数据报文接收发送总结1

    0. 引 如下简单的一段在代码,我们在Linux上运行:同时再运行一服务端的回显: #include <stdio.h> #include <sys/types.h> #inc ...

  2. Linux数据报文接收发送总结6

    2.3 协议栈注册 内核实现了网络层的ip协议,也实现了传输层的tcp协议和udp协议.这些协议对应的实现函数分别是ip_rcv(),tcp_v4_rcv()和udp_rcv().和我们平时写代码的方 ...

  3. Linux数据报文接收发送总结5

    2.2 网络子系统初始化 linux内核通过调用subsys_initcall来初始化各个子系统,在源代码目录里你可以grep出许多对这个函数的调用.这里我们要说的是网络子系统的初始化,会执行到net ...

  4. Linux数据报文接收发送总结3

    1.3 协议分层 大概了解了网卡驱动.硬中断.软中断和ksoftirqd线程之后,我们在这几个概念的基础上给出一个内核收包的路径示意: 当网卡上收到数据以后,Linux中第一个工作的模块是网络驱动.网 ...

  5. Linux数据报文接收发送总结2

    1. 准备工作 此处重点介绍基础概念,为后面介绍数据包收发打下基础.本次代码层面基于Linux 4.4 Kernel. 1.1 系统调用 Linux的系统运行分为用户态和内核态,内核态控制着系统资源. ...

  6. Linux数据报文接收发送总结7

    2.4 网卡驱动初始化 每一个驱动程序(不仅仅只是网卡驱动)会使用 module_init 向内核注册一个初始化函数,当驱动被加载时,内核会调用这个函数.比如igb网卡驱动的代码位于drivers/n ...

  7. linux网络报文接收发送浅析_Docker容器网络-基础篇

    Docker的技术依赖于Linux内核的虚拟化技术的发展,Docker使用到的网络技术有Network Namespace.Veth设备对.Iptables/Netfilter.网桥.路由等.接下来, ...

  8. Linux SYN报文接收及发送SYNACK报文

    注:本文分析基于3.10.0-693.el7内核版本,即CentOS 7.4 在分析connect()系统调用时,我们已经发送SYN报文,所以服务端就需要作出回应了.我们依然只分析TCP层的操作.SY ...

  9. linux IPv4报文处理浅析

    在<linux网络报文接收发送浅析>一文中介绍了数据链路层关于网络报文的处理. 对于接收到的报文,如果不被丢弃.不被网桥转发,会调用netif_receive_skb()提交给IP层: 而 ...

最新文章

  1. 逻辑覆盖测试(三)条件覆盖
  2. win7定时关机命令_电脑两个小技巧:新手设置电脑定时关机 VS 定时开机的方法详解!...
  3. Android TV开发焦点动作控制小技巧
  4. jpql hql_无需部署即可测试JPQL / HQL
  5. 如何实现按钮水平居中
  6. struts2自定义标签_Struts 2 –没有为动作和结果输入定义结果
  7. 中老年人旅行有多狂野
  8. 软考初级程序员---题目(三)
  9. 全国资格考试网报平台证件照片审核处理系统报错:打开照片文件类型还是JPG或JPEG格式,请选择JPG或JPEG格式照片文件。
  10. Web服务器环境的搭建
  11. JS密码校验规则前台验证(不能连续字符(如123、abc)连续3位或3位以上)(不能相同字符(如111、aaa)连续3位或3位以上)
  12. docker 容器Exited【数字报错】问题
  13. 【洛谷P1486】郁闷的出纳员【树状数组】
  14. 一声叹息,jdk竟然有4个random
  15. 使用initial-letter实现首字下沉效果,感兴趣的同学欢迎猛击:
  16. VB6.0安装鼠标滚轮
  17. ptcms需要php版本,PTCMS小说最新版4.28源码完美功能更新安装教程
  18. 大数据领域的杰出公司(国内外1)
  19. phpyun模板使用foreach循环
  20. t检验是基于t分布的

热门文章

  1. sqlite第三方类库FMDB的使用
  2. SharePoint场管理-PowerShell(一)
  3. 意识到自己的无知这就是进步
  4. R语言应用统计1 主成分分析
  5. VS调试ReactOS源码环境搭建5 - VS通过串口连接虚拟机
  6. using 指令是不需要的和其他两个C#错误
  7. java异常处理学习总结
  8. 图解TC++3.0开发教程
  9. table表格细边框
  10. Android的第一个程序