Linux数据报文接收发送总结4
二、系統(tǒng)初始化
?
Linux驅(qū)動(dòng),內(nèi)核協(xié)議棧等等模塊在具備接收網(wǎng)卡數(shù)據(jù)包之前,要做很多的準(zhǔn)備工作才行。比如要提前創(chuàng)建好ksoftirqd內(nèi)核線程,要注冊(cè)好各個(gè)協(xié)議對(duì)應(yīng)的處理函數(shù),網(wǎng)絡(luò)設(shè)備子系統(tǒng)要提前初始化好,網(wǎng)卡要啟動(dòng)好。只有這些都Ready之后,我們才能真正開始接收數(shù)據(jù)包。那么我們現(xiàn)在來看看這些準(zhǔn)備工作都是怎么做的。
?
Linux的子系統(tǒng)、模塊均定義了一定的啟動(dòng)級(jí)別,在start_kernel函數(shù)中,按順序啟動(dòng)
/* 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 創(chuàng)建ksoftirqd內(nèi)核線程
?
Linux的軟中斷都是在專門的內(nèi)核線程(ksoftirqd)中進(jìn)行的,因此我們非常有必要看一下這些進(jìn)程是怎么初始化的,這樣我們才能在后面更準(zhǔn)確地了解收包過程。該進(jìn)程數(shù)量不是1個(gè),而是N個(gè),其中N等于你的機(jī)器的核數(shù)。
?
系統(tǒng)初始化的時(shí)候執(zhí)行spawn_ksoftirq -> smpboot_register_percpu_thread->smpboot_register_percpu_thread_cpumask->__smpboot_create_thread,
該函數(shù)創(chuàng)建出softirqd內(nèi)核線程(位于kernel/softirq.c, 線程主函數(shù)smpboot_thread_fn)。
?
相關(guān)代碼如下:
//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); // 為每個(gè)CPU創(chuàng)建一個(gè)處理軟件中斷的線程BUG_ON(smpboot_register_percpu_thread(&softirq_threads));return 0; } early_initcall(spawn_ksoftirqd); // 將函數(shù)放至對(duì)應(yīng)級(jí)別的初始化位置//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)) { // 檢測(cè)軟件是否有可運(yùn)行軟中斷preempt_enable_no_resched();schedule();} else {__set_current_state(TASK_RUNNING);preempt_enable();ht->thread_fn(td->cpu); // 執(zhí)行注冊(cè)的軟件中斷函數(shù)}} }當(dāng)ksoftirqd被創(chuàng)建出來以后,它就會(huì)進(jìn)入自己的線程循環(huán)函數(shù)ksoftirqd_should_run和run_ksoftirqd了。不停地判斷有沒有軟中斷需要被處理。這里需要注意的一點(diǎn)是,軟中斷不僅僅只有網(wǎng)絡(luò)軟中斷,還有其它類型。
//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, };?
?
總結(jié)
以上是生活随笔為你收集整理的Linux数据报文接收发送总结4的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux数据报文接收发送总结3
- 下一篇: 三 虚拟机安装Deepin