软中断

软中断的分配时静态的(即在编译时定义),而tasklet的分配和初始化能够在执行时进行。

软中断(即便是同一种类型的软中断)能够并发地运行在多个CPU上。

因此,软中断是可重入函数并且必须明白地使用自旋锁保护其数据结构。

tasklet不必操心这些问题。由于内核对tasklet的运行进行了更加严格的控制。

同样类型的tasklet总是被串行运行。

换句话说就是:不能在两个CPU上同一时候执行同样类型的tasklet。可是,类型不同的tasklet能够在几个CPU上并发执行。

tasklet的串行化使tasklet函数不必是可重入的

软中断           下标    说明

HI_SOFTIRQ      0      处理高优先级的tasklet

TIMER_SOFTIRQ   1      和时钟中断相关的tasklet

NET_TX_SOFTIRQ  2      把数据包传送到网卡

NET_RX_SOFTIRQ  3      从网卡接收数据包

SCSI_SOFTIRQ    4      SCSI命令的后台中断处理

TASKLET_SOFTIRQ 5      处理常规tasklet

thread_info->preempt_count

preempt_count字段

位              描写叙述

0~7             抢占计数器(max value=255)

8~15            软中断计数器(max value=255)

16~27           硬中断计数器(max value=255)

28              PREEMPT_ACTIVE标志

第一个计数器记录显式禁用本地CPU内核抢占的次数。值等于0表示同意内核抢占。

第二个计数器表示可延迟函数被禁用的程度(值为0表示可延迟函数处于激活状态)。

第三个计数器表示在本地CPU上中断处理程序的嵌套数。

处理软中断

open_softirq()     函数处理软中断的初始化

raise_softirq()    函数用来激活软中断

  1. inline void raise_softirq_irqoff(unsigned int nr)
  2. {
  3. __raise_softirq_irqoff(nr);
  4.  
  5. /*
  6. * If we're in an interrupt or softirq, we're done
  7. * (this also catches softirq-disabled code). We will
  8. * actually run the softirq once we return from
  9. * the irq or softirq.
  10. *
  11. * Otherwise we wake up ksoftirqd to make sure we
  12. * schedule the softirq soon.
  13. */
  14. if (!in_interrupt())
  15. wakeup_softirqd();
  16. }
  17.  
  18. void raise_softirq(unsigned int nr)
  19. {
  20. unsigned long flags;
  21.  
  22. local_irq_save(flags);
  23. raise_softirq_irqoff(nr);
  24. local_irq_restore(flags);
  25. }

raise_softirq()函数运行以下的操作:

1、运行local_irq_save宏以保存eflags寄存器IF标志状态并禁用本地CPU上的中断。

2、把软中断标记为挂起状态。这是通过设置本地CPU的软中断掩码中与下标nr相关的位来实现的

3、假设in_interrupt()产生为1的值,则跳转到5。

这样的情况说明:要么已经在中断上下文中调用了raise_softirq()。要么当前禁用了软中断。

4、否则,就在须要的时候去调用wakeup_softirqd()以唤醒本地CPU的ksoftirqd内核线程。

5、运行local_irq_restore宏。恢复在第1步保存的IF标志的状态。

在下面几种情况周期性地检查是否有软中断要运行:

a、当内核调用local_bh_enable()函数激活本地CPU的软中断时

b、当do_IRQ()完毕了I/O中断的处理时或调用irq_exit()宏时

c、假设系统使用I/O APIC。则当smp_apic_timer_interrupt()函数处理完本地定时器中断时

d、在多处理器系统中。当CPU处理完被CALL_FUNCTION_VECTOR处理器间中断所触发的函数时

e、当一个特殊的ksoftirqd/n内核线程被唤醒时

ksoftirqd内核线程

每一个ksoftirqd/n内核线程都执行ksoftirqd()函数,该函数实际上执行下列的循环:

  1. static int ksoftirqd(void * __bind_cpu)
  2. {
  3. set_user_nice(current, 19);
  4. current->flags |= PF_NOFREEZE;
  5.  
  6. set_current_state(TASK_INTERRUPTIBLE);
  7.  
  8. while (!kthread_should_stop()) {
  9. if (!local_softirq_pending())
  10. schedule();
  11.  
  12. __set_current_state(TASK_RUNNING);
  13.  
  14. while (local_softirq_pending()) {
  15. /* Preempt disable stops cpu going offline.
  16. If already offline, we'll be on wrong CPU:
  17. don't process */
  18. preempt_disable();
  19. if (cpu_is_offline((long)__bind_cpu))
  20. goto wait_to_die;
  21. do_softirq();
  22. preempt_enable();
  23. cond_resched();
  24. }
  25.  
  26. set_current_state(TASK_INTERRUPTIBLE);
  27. }
  28. __set_current_state(TASK_RUNNING);
  29. return 0;
  30.  
  31. wait_to_die:
  32. preempt_enable();
  33. /* Wait for kthread_stop */
  34. set_current_state(TASK_INTERRUPTIBLE);
  35. while (!kthread_should_stop()) {
  36. schedule();
  37. set_current_state(TASK_INTERRUPTIBLE);
  38. }
  39. __set_current_state(TASK_RUNNING);
  40. return 0;
  41. }

当内核线程被唤醒时,就检查local_softirq_pending()中的软中断位掩码并在必要时调用do_softirq()。假设没有挂起的软中断。函数把当前进程状态置为TASK_INTERRUPTIBLE,随后。假设当前进程须要(当前thread_info的TIF_NEED_RESCHED标志被设置)就调用con_resched()函数来实现进程切换。

tasklet

tasklet是I/O驱动程序中实现可延迟函数的首选方法。

tasklet建立在两个叫做HI_SOFTIRQ和TASKLET_SOFTIRQ的软中断之上。

几个tasklet能够与同一个软中断相关联,每一个tasklet运行自己的函数。

  1. struct tasklet_struct
  2. {
  3. struct tasklet_struct *next;
  4. unsigned long state;
  5. atomic_t count;
  6. void (*func)(unsigned long);
  7. unsigned long data;
  8. };
  9. struct tasklet_head
  10. {
  11. struct tasklet_struct *head;
  12. struct tasklet_struct **tail;
  13. };
  14. static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
  15. static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);

tasklet描写叙述符的字段

next     指向链表中下一个描写叙述符的指针

state    tasklet的状态

count    锁计数器

func     指向tasklet函数的指针

data     一个无符号长整数。能够由tasklet函数使用

tasklet描写叙述的state字段含有两个标志:

TASKLET_STATE_SCHED

该标志被设置时,表示tasklet是挂起的。也意味着tasklet描写叙述符被插入到tasklet_vec和tasklet_hi_vec数组的当中一个链表

TASKLET_STATE_RUN

该标志被设置时。表示tasklet正在被执行;在单处理器系统上不使用这个标志,由于没有必要检查特定的tasklet是否在执行

为了激活tasklet,依据tasklet的优先级,调用tasklet_schedule()或tasklet_hi_schedule()函数。

  1. inline void raise_softirq_irqoff(unsigned int nr)
  2. {
  3. __raise_softirq_irqoff(nr);
  4.  
  5. /*
  6. * If we're in an interrupt or softirq, we're done
  7. * (this also catches softirq-disabled code). We will
  8. * actually run the softirq once we return from
  9. * the irq or softirq.
  10. *
  11. * Otherwise we wake up ksoftirqd to make sure we
  12. * schedule the softirq soon.
  13. */
  14. if (!in_interrupt())
  15. wakeup_softirqd();
  16. }
  17.  
  18. void __tasklet_schedule(struct tasklet_struct *t)
  19. {
  20. unsigned long flags;
  21.  
  22. local_irq_save(flags);
  23. t->next = NULL;
  24. *__get_cpu_var(tasklet_vec).tail = t;
  25. __get_cpu_var(tasklet_vec).tail = &(t->next);
  26. raise_softirq_irqoff(TASKLET_SOFTIRQ);
  27. local_irq_restore(flags);
  28. }
  29. static inline void tasklet_schedule(struct tasklet_struct *t)
  30. {
  31. if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
  32. __tasklet_schedule(t);
  33. }

这两个函数很类似,当中每一个运行下列操作:

1、检查TASKLET_STATE_SCHED标志;假设设置则返回

2、调用local_irq_save保存IF标志的状态并禁用本地中断

3、在tasklet_vec或者tasklet_hi_vec指向的链表的起始处添加tasklet描写叙述符

4、调用raise_softirq_irqoff()激活TASKLET_SOFTIRQ或HI_SOFTIRQ类型的软中断

5、调用local_irq_restore恢复IF标志的状态

工作队列

可延迟函数和工作队列很相似。它们的差别在于:可延迟函数执行在中断上下文,而工作队列中的函数执行在进程上下文中。执行可堵塞函数(比如:须要訪问磁盘数据块的函数)的唯一方式是在进程上下文中执行。由于,在中断上下文中不可能发生进程切换。可延迟函数和工作队列中的函数都不能訪问进程的用户态地址空间。

  1. struct cpu_workqueue_struct {
  2.  
  3. spinlock_t lock;
  4.  
  5. long remove_sequence; /* Least-recently added (next to run) */
  6. long insert_sequence; /* Next to add */
  7.  
  8. struct list_head worklist;
  9. wait_queue_head_t more_work;
  10. wait_queue_head_t work_done;
  11.  
  12. struct workqueue_struct *wq;
  13. task_t *thread;
  14.  
  15. int run_depth; /* Detect run_workqueue() recursion depth */
  16. } ____cacheline_aligned;
  17.  
  18. /*
  19. * The externally visible workqueue abstraction is an array of
  20. * per-CPU workqueues:
  21. */
  22. struct workqueue_struct {
  23. struct cpu_workqueue_struct cpu_wq[NR_CPUS];
  24. const char *name;
  25. struct list_head list; /* Empty if single thread */
  26. };

cpu_workqueue_struct类型的描写叙述符,字段描写叙述例如以下:

lock             保护该数据结构的自旋锁

remove_sequence  flush_workqueue()使用的序列号

insert_sequence  flush_workqueue()使用的序列号

worklist         挂起链表的头节点

more_work        等待队列。当中的工作者线程因等待很多其它的工作而处于睡眠状态

work_done        等待队列,当中的进程因为等待工作队列被刷新而处于睡眠状态

wq               指向workqueue_struct结构的指针,当中包括该描写叙述符

thread           指向结构中工作线程的进程描写叙述符指针

run_depth        run_workqueue()当前的运行深度

  1. struct work_struct {
  2. unsigned long pending;
  3. struct list_head entry;
  4. void (*func)(void *);
  5. void *data;
  6. void *wq_data;
  7. struct timer_list timer;
  8. };

pending       假设函数已经在工作队列链表中。该字段值设为1,否则设为0

entry         指向挂起函数链表前一个或后一个元素的指针

func          挂起函数的地址

data          传递给挂起函数的參数,是一个指针

wq_data       一般是指向cpu_workqueue_struct描写叙述符的父节点的指针

timer         用于延迟挂起函数运行的软定时器

工作队列函数

create_workqueue()  函数接收一个字符串作为參数,返回新创建工作队列的workqueue_struct描写叙述符的地址。该函数还创建n个工作者线程,并根                     据传递给函数的字符串为工作者线程命名,如foo/0,foo/1等等

destory_workqueue() 函数撤销工作队列

queue_work()        把函数插入工作队列,该函数主要运行以下步骤:

1、检查要插入的函数是否已经在工作队列中,假设是就结束

2、把work_struct描写叙述符加到工作队列链表中。然后把work->pending置为1

3、假设工作者线程在本地CPU的cpu_workqueue_struct描写叙述符的more_work等待队列上睡眠。该函数唤醒这个线程

每一个工作线程在worker_thread()函数内部不断地运行循环操作,因而。线程在绝大多数时间里处于睡眠状态并等待某些工作被插入队列。

工作线程一旦被唤醒就调用run_workqueue()函数,该函数从工作者线程的工作队列链表中删除全部work_struct描写叙述符并运行对应的挂起函数。

因为工作队列函数能够堵塞。因此,能够让工作者线程睡眠,甚至能够让它迁移到还有一个CPU上恢复运行

flush_workqueue()函数接收workqueue_struct描写叙述符的地址,而且在工作队列中的全部挂起函数结束之前使调用进程一直处于堵塞状态。

软中断&tasklet&工作队列的更多相关文章

  1. Linux内核中的软中断、tasklet和工作队列具体解释

    [TOC] 本文基于Linux2.6.32内核版本号. 引言 软中断.tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的"下半部"(bottom ...

  2. Linux软中断、tasklet和工作队列

    Linux内核中的软中断.tasklet和工作队列详解 引言 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的“下半部”(bottom half)演变而来 ...

  3. [Linux内核]软中断、tasklet、工作队列

    转自:http://www.cnblogs.com/li-hao/archive/2012/01/12/2321084.html 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制, ...

  4. Linux中断管理 (2)软中断和tasklet

    目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机制> <Linux中断管理 (2)软中断和tasklet> <Linux中断管 ...

  5. linux内核--软中断与tasklet

    硬件中断通常都需要在最短的时间内执行完毕,如果将所有硬件中断相关的处理都放在硬件中断处理程序中,那么就达不到这个目的. 通过linux提供的软中断和tasklet,可以将硬件中断处理程序中可以延迟处理 ...

  6. 软中断和tasklet介绍

    今天看了下tasklet,重点分析了其和软中断的关系,特此记录 关于软中断,在之前的中断文章中已经有所介绍,这里就不多说了,只是说明下,系统中默认支持32种软中断,而实际上系统定义的软中断仅有以下几种 ...

  7. 内核软中断之tasklet机制

    1. 软中断IRQ简介 软中断(SoftIRQ)是内核提供的一种基于中断的延时机制, Linux内核定义的软中断有以下几种: enum { HI_SOFTIRQ=0, /*高优先级的tasklet*/ ...

  8. Linux中断分层--软中断和tasklet

    1. Linux中断分层 (1)上半部:当中断发生时,它进行相应的硬件读写,并“登记”该中断.通常由中断处理程序充当上半部.(一般情况下,上半部不可被打断) (2)下半部:在系统空闲的时候,对上半部“ ...

  9. tasklet和工作队列

    tasklet机制和工作队列 http://blog.chinaunix.net/uid-28236237-id-3450753.html tasklet原理 http://www.kuqin.com ...

随机推荐

  1. C# Eval在asp.net中的用法及作用

    Eval( " ")和Bind( " ") 这两种一个单向绑定,一个双向绑定,bind是双向绑定,但需数据源支持 ASP.NET 2.0改善了模板中的数据绑定操 ...

  2. 运行edX Devstack

    前一篇文章,我们安装完成了edX Devstack,本文将介绍edX Devstack的常用命令 6.1. 连接到 Devstack Virtual Machine 为了连接到Devstack vir ...

  3. FLV封装格式及分析器工具

    http://blog.csdn.net/leixiaohua1020/article/details/17934487 FLV封装原理 FLV格式的封装原理,贴上来辅助学习之用.     FLV(F ...

  4. 上海CEC大收购(包括华大九天)

    紫光收购展讯.锐迪科后,上海开始通过扶植CEC培育新势力,CEC已经收购上海澜起,即将收购amlogic.Ominivision,还在与marvell眉来眼去,此外华大九天已经移植上海,加上之前的上海 ...

  5. 【Linux安全】查看是否存在特权用户以及是否存在空口令用户

    查看是否存在特权用户 通过判断uid是否为0来查找系统是否存在特权用户,使用命令awk即可查出. [root@pentester ~]# awk -F: '$3==0 {print $1}' /etc ...

  6. 知识总结: Activity的四种启动模式

    通常情况下,一个应用有一个Task,这个Task就是为了完成某个工作的一系列Activity的集合.而这些Activity又被组织成了堆栈的形式.当一个Activity启动时,就会把它压入该Task的 ...

  7. MySQL information_schema表查询导致内存暴涨

    case:下面的一条sql语句,导致mysql实例内存暴涨: select * from tables where table_name not in(select table_name from p ...

  8. bzoj3165 1568

    1568是3165的弱化版,发的代码是3165的这道题完全没想出来,是看wyl大神的题解http://hi.baidu.com/wyl8899/item/2deafd3a376ef2d46d15e99 ...

  9. POJ_1064_Cable_master_(二分,假定一个解并判断是否可行)

    描述 http://poj.org/problem?id=1064 有n条绳子,长度分别为l[i].如果从它们中切割出k条长度相同的绳子的话,这k条绳子每条最长能有多少? Cable master T ...

  10. C#初始化数组的三种方式

    C#声明数组并初始化,有三种方式. 对于一维数组: using System;using System.Data;using System.Configuration;using System.Web ...