中断学习之下半部深入理解

对于软中断,实现还是很完美的。每一个中断上半部都对应一个软中断,可以在多核上并发进行。以块设备软中断为例,初始化如下

blk_dev_init
	=>for_each_possible_cpu(i)//每个CPU都部署
		INIT_LIST_HEAD(&per_cpu(blk_cpu_done, i));
	=>open_softirq(BLOCK_SOFTIRQ, blk_done_softirq, NULL);
		=>softirq_vec[nr].data = data;
		=>softirq_vec[nr].action = action;

上半部交接

blk_complete_request
	=>cpu_list = &__get_cpu_var(blk_cpu_done);
	=>list_add_tail(&req->donelist, cpu_list);//每个完成的req都加到队列里面
	=>raise_softirq_irqoff(BLOCK_SOFTIRQ);//唤醒软中断
		=>__raise_softirq_irqoff(nr);
			=>or_softirq_pending(1UL << (nr)); //置pending位
			=>wakeup_softirqd()
				=>struct task_struct *tsk = __get_cpu_var(ksoftirqd);
				=>wake_up_process(tsk);//唤醒ksoftirqd

软中断粉墨登场

ksoftirqd
	=>set_current_state(TASK_INTERRUPTIBLE);
	=>while (!kthread_should_stop())
		preempt_disable();//关闭抢占计数
		if (!local_softirq_pending()) //如果没有pending计数
			schedule();//切换出去
			preempt_disable();
		__set_current_state(TASK_RUNNING);
		while (local_softirq_pending())//有pending计数的话
			do_softirq();
				=>if (local_softirq_pending())
					=>curctx = current_thread_info();//切换到软中断上下文
					irqctx = softirq_ctx[smp_processor_id()];
					irqctx->tinfo.task = curctx->task;
					irqctx->tinfo.previous_esp = current_stack_pointer;

					/* build the stack frame on the softirq stack */
					isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
					=>call    __do_softirq
						=>h = softirq_vec;
						=>do {//依次执行软中断向量
							if (pending & 1) {
								h->action(h);
									=>blk_done_softirq//对于块设备来说,就是该函数
										=>cpu_list = &__get_cpu_var(blk_cpu_done);
										list_replace_init(cpu_list, &local_list);
										=>while (!list_empty(&local_list)) {//依次执行每个req
											struct request *rq = list_entry(local_list.next, struct request, donelist);

											list_del_init(&rq->donelist);
											rq->q->softirq_done_fn(rq);
												=>scsi_softirq_done
										}
								rcu_bh_qsctr_inc(cpu);
							}
							h++;
							pending >>= 1;
						} while (pending);
			preempt_enable_no_resched();
			cond_resched();
			preempt_disable();
		preempt_enable();
		set_current_state(TASK_INTERRUPTIBLE);
	=>__set_current_state(TASK_RUNNING);

软中断成本很高,linux也就定义了6种,块设备和网口是典型,中断频繁触发,软中断随之频繁,但是任务本身不重。如果中断不是超级频繁,下半部不是超级复杂。那么,tasklet是个很好的选择。

tasklet不管有多少个cpu,1种类型只有1个,没有队列。以键盘中断为例,定义如下:

DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);

键盘中断交接给tasklet

request_irq(EC3104_IRQ_SER4, ec3104_keyb_interrupt, 0, "keyboard", NULL);
	=>ec3104_keyb_interrupt
		=>e5_receive(k);
			=>handle_keyboard_event(k->packet[1]);
				=>tasklet_schedule(&keyboard_tasklet);

tasklet执行

tasklet_schedule(&keyboard_tasklet);
	=>if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))//当tasklet已经处于调度状态但是还没有执行时,新来的同类型的tasklet丢弃掉,因为tasklet只有1个全局变量
		__tasklet_schedule(t);
			=>t->next = __get_cpu_var(tasklet_vec).list;//将tasklet加入到tasklet里面,这里面的tasklet是不同类型tasklet的集合
			__get_cpu_var(tasklet_vec).list = t;
			=>raise_softirq_irqoff(TASKLET_SOFTIRQ);
				=......=>tasklet_action
					=>list = __get_cpu_var(tasklet_vec).list;//把队列里面的tasklet全部摘除
					__get_cpu_var(tasklet_vec).list = NULL;
					=>while (list)//串行执行所有的tasklet
						struct tasklet_struct *t = list;
						list = list->next;
						if (tasklet_trylock(t))
							tasklet_trylock
								=>return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);//是否处于RUN状态
							test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)//取消tasklet的调度状态,这个时候如果新来一个同类型的tasklet,则能挂上去
							t->func(t->data);//执行tasklet
							tasklet_unlock(t);//解除RUN状态
							continue;//回到while (list)继续串行执行下一个tasklet
						local_irq_disable();//如果tasklet处于RUN状态,则把队列重新挂回去
						t->next = __get_cpu_var(tasklet_vec).list;
						__get_cpu_var(tasklet_vec).list = t;
						__raise_softirq_irqoff(TASKLET_SOFTIRQ);
						local_irq_enable();

由于tasklet只有1个,处于调度态的话只能有1个,所以不需要重入。所以在键盘,鼠标等处理任务不是很繁重的情况下很合适。

如果中断不是很繁重,但是下半部需要处理的任务很重很繁琐,不是那么紧急的话,wq就更合适了。

linux软中断机制分析
https://blog.csdn.net/andylauren/article/details/70233378


linux 内核软中断详解
http://blog.sae.sina.com.cn/archives/4103


总结一下linux软中断的实现
http://blog.chinaunix.net/uid-145571-id-2798845.html


kernel中断分析七——tasklet
https://blog.csdn.net/yin262/article/details/54289502


深入分析Tasklet机制

https://blog.csdn.net/shenwanjiang111/article/details/52247760

Nginx服务器上软中断过高问题如何解决

http://os.51cto.com/art/201310/414482.htm

ORIG_EAX

https://blog.csdn.net/zuihoudebingwen/article/details/9115599

struct pt_regs 中存的内容

https://blog.csdn.net/abo8888882006/article/details/5379727

软中断与硬中断    作者的博客不错

http://blog.chinaunix.net/uid-7396260-id-2056718.html

Linux下软中断过程(详细)源码总结
https://blog.csdn.net/luckiers/article/details/123868625

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值