linux软件中断和软中断,第一篇:linux中断下半部分之(软中断)

本文详细介绍了Linux内核中断处理中的上半部和下半部概念,以及如何利用软中断实现下半部。中断上半部主要处理紧急任务,下半部则用于执行非紧急但必要的后续操作。软中断作为一种低优先级的异步中断,不能休眠,常用于tasklet的实现。文章通过实例展示了如何注册软中断、触发软中断并在中断处理完成后执行软中断处理函数,从而完成中断下半部的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是下半部

中断是一个很霸道的东西,处理器一旦接收到中断,就会打断正在执行的代码,调用中断处理函数。如果在中断处理函数中没有禁止中断,该中断处理函数执行过程中仍有可能被其他中断打断。出于这样的原因,大家都希望中断处理函数执行得越快越好。

另外,中断上下文中不能阻塞,这也限制了中断上下文中能干的事。

基于上面的原因,内核将整个的中断处理流程分为了上半部和下半部。上半部就是之前所说的中断处理函数,它能最快的响应中断,并且做一些必须在中断响应之后马上要做的事情。而一些需要在中断处理函数后继续执行的操作,内核建议把它放在下半部执行。

拿网卡来举例,在linux内核中,当网卡一旦接受到数据,网卡会通过中断告诉内核处理数据,内核会在网卡中断处理函数(上半部)执行一些网卡硬件的必要设置,因为这是在中断响应后急切要干的事情。接着,内核调用对应的下半部函数来处理网卡接收到的数据,因为数据处理没必要在中断处理函数里面马上执行,可以将中断让出来做更紧迫的事情。

可以有三种方法来实现下半部:软中断、tasklet和等待队列。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

二、软中断

软中断一般很少用于实现下半部,但tasklet是通过软中断实现的,所以先介绍软中断。字面理解,软中断就是软件实现的异步中断,它的优先级比硬中断低,但比普通进程优先级高,同时,它和硬中断一样不能休眠。

软中断是在编译时候静态分配的,要用软中断必须修改内核代码。

在kernel/softirq.c中有这样的一个数组:

51static struct softirq_action

softirq_vec[NR_SOFTIRQS]__cacheline_aligned_in_smp;

内核通过一个softirq_action数组来维护的软中断,NR_SOFTIRQS是当前软中断的个数,待会再看他在哪里定义。

先看一下softirq_action结构体:

265 struct

softirq_action

266 {

267 void (*action)(struct softirq_action

*); //软中断处理函数

268 };

一看发现,结构体里面就一个软中断函数,他的参数就是本身结构体的指针。之所以这样设计,是为了以后的拓展,如果在结构体中添加了新成员,也不需要修改函数接口。在以前的内核,该结构体里面还有一个data的成员,用于传参,不过现在没有了。

接下来看一下如何使用软中断实现下半部

一、要使用软中断,首先就要静态声明软中断:

246 enum

247 {

248 HI_SOFTIRQ=0,

//用于tasklet的软中断,优先级最高,为0

249 TIMER_SOFTIRQ,

//定时器的下半部

250 NET_TX_SOFTIRQ,

//发送网络数据的软中断

251 NET_RX_SOFTIRQ,

//接受网络数据的软中断

252 BLOCK_SOFTIRQ,

253 TASKLET_SOFTIRQ,

//也是用于实现tasklet

254 SCHED_SOFTIRQ,

255 HRTIMER_SOFTIRQ,

256 RCU_SOFTIRQ,

257 //add by xiaobai

2011.1.18

258 XIAOBAI_SOFTIRQ,

//这是我添加的,优先级最低

259

260 NR_SOFTIRQS,

//这个就是上面所说的软中断结构体数组成员个数

261 };

上面通过枚举定义了NR_SOFTIRQS(10)个软中断的索引号,优先级最高是0(HI_SOFTIRQ),最低是我刚添加上去的XIAOBAI_SOFTIRQ,优先级为9。

二、定义了索引号后,还要注册处理程序。

通过函数open_sofuirq来注册软中断处理函数,使软中断索引号与中断处理函数对应。该函数在kernel/softirq.c中定义:

321 void open_softirq(int nr, void

(*action)(struct softirq_action *))

322 {

323 softirq_vec[nr].action =

action;

324 }

其实该函数就是把软中断处理函数的函数指针存放到对应的结构体中,一般的,我们自己写的模块是不能调用这个函数的,为了使用这个函数,我修改了内核:

322 void open_softirq(int nr, void

(*action)(struct softirq_action *))

323 {

324 softirq_vec[nr].action =

action;

325 }

326 EXPORT_SYMBOL(open_softirq);

//这是我添加的,导出符号,这样我编写的程序就能调用

在我的程序中如下调用:

13 void xiaobai_action(struct

softirq_action *t) //软中断处理函数

14 {

15 printk("hello

xiaobai!\n");

16 }

。。。。。。。。

48 open_softirq(XIAOBAI_SOFTIRQ,

xiaobai_action);

三、在中断处理函数返回前,触发对应的软中断。

在中断处理函数完成了必要的操作后,就应该调用函数raise_sotfirq触发软中断,让软中断执行中断下半部的操作。

312 void raise_softirq(unsigned int

nr)

313 {

314 unsigned long flags;

315

316

local_irq_save(flags);

317

raise_softirq_irqoff(nr);

318

local_irq_restore(flags);

319 }

所谓的触发软中断,并不是指马上执行该软中断,不然和在中断上执行没什么区别。它的作用只是告诉内核:下次执行软中断的时候,记得执行我这个软中断处理函数。

当然,这个函数也得导出符号后才能调用:

312 void raise_softirq(unsigned int

nr)

313 {

314 unsigned long flags;

315

316

local_irq_save(flags);

317

raise_softirq_irqoff(nr);

318

local_irq_restore(flags);

319 }

320

EXPORT_SYMBOL(raise_softirq);

在我的程序中如下调用:

18 irqreturn_t irq_handler(int irqno, void

*dev_id) //中断处理函数

19 {

20 printk("key down\n");

21

raise_softirq(XIAOBAI_SOFTIRQ);

22 return IRQ_HANDLED;

23 }

经过三步,使用软中断实现下半部就成功了,看一下完整的函数:

1 #include

2 #include

3

4 #include

5

6 #define DEBUG_SWITCH 1

7 #if DEBUG_SWITCH

8 #define P_DEBUG(fmt, args...)

printk("<1>" "[%s]"fmt, __FUNCTI ON__,

##args)

9 #else

10 #define P_DEBUG(fmt, args...)

printk("<7>" "[%s]"fmt, __FUNCTI ON__,

##args)

11 #endif

12

13 void xiaobai_action(struct

softirq_action *t) //软中断处理函数

14 {

15 printk("hello

xiaobai!\n");

16 }

17

18 irqreturn_t irq_handler(int irqno, void

*dev_id) //中断处理函数

19 {

20 printk("key down\n");

21 raise_softirq(XIAOBAI_SOFTIRQ);

//触发软中断

22 return IRQ_HANDLED;

23 }

24

25 static int __init test_init(void)

//模块初始化函数

26 {

27 int ret;

28

29

36 ret = request_irq(IRQ_EINT1,

irq_handler,

37 IRQF_TRIGGER_FALLING, "key INT_EINT1",

NULL);

38 if(ret){

39 P_DEBUG("request irq

failed!\n");

40 return ret;

41 }

42

43

44 open_softirq(XIAOBAI_SOFTIRQ,

xiaobai_action); //注册软中断处理程序

45

46 printk("hello

irq\n");

47 return 0;

48 }

49

50 static void __exit test_exit(void)

//模块卸载函数

51 {

52 free_irq(IRQ_EINT1,

NULL);

53 printk("good bye

irq\n");

54 }

55

56

module_init(test_init);

57

module_exit(test_exit);

58

59

MODULE_LICENSE("GPL");

60 MODULE_AUTHOR("xoao

bai");

61

MODULE_VERSION("v0.1");

注意。在上面的程序,只是为了说明如何实现上下半步,而我的中断上下半步里面的操作是毫无意义的(只是打印)。上下半步的作用我在一开始就有介绍。

接下来验证一下:

[root: 1st]# insmod

test.ko

hello irq

[root: 1st]# key down

//上半部操作

hello xiaobai!

//下半部操作

key down

hello xiaobai!

key down

hello xiaobai!

[root: 1st]# rmmod test

good bye irq

上面介绍,触发软中断函数raise_softirq并不会让软中断处理函数马上执行,它只是打了个标记,等到适合的时候再被实行。如在中断处理函数返回后,内核就会检查软中断是否被触发并执行触发的软中断。

软中断会在do_softirq中被执行,其中核心部分在do_softirq中调用的__do_softirq中:

172 asmlinkage void

__do_softirq(void)

173 {

。。。。。。

194 do {

195 if (pending & 1) {

//如果被触发,调用软中断处理函数

196 int prev_count =

preempt_count();

197

198 h->action(h);

//调用软中断处理函数

199

200 if (unlikely(prev_count !=

preempt_count())) {

201 printk(KERN_ERR "huh, entered softirq

%td %p"

202 "with preempt_count

x,"

203 " exited with x?\n", h -

softirq_vec,

204 h->action, prev_count,

preempt_count());

205 preempt_count() =

prev_count;

206 }

207

208

rcu_bh_qsctr_inc(cpu);

209 }

210 h++;

//下移,获取另一个软中断

211 pending >>=

1;

212 } while (pending);

//大循环内执行,知道所有被触发的软中断都执行完

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值