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

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);
		else
			ht->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();
}

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

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值