一 中断

硬件通过中断与操作系统进行通信,通过对硬件驱动程序处注册中断处理程序,快速响应硬件的中断。

硬件中断优先级很高,打断当前正在执行的程序。有两种情况:

  硬件中断在中断处理程序中处理

  硬件中断延后再进行处理

  这个具体硬件相关,在中断处理程序中处理,打断了当前正在执行的程序;所有中断都将被屏蔽;如果占用时间太长不合适,

造成系统交互性,反应能力都会受到影响。 需要在其中判断平衡:

如果一个任务对时间非常敏感,将其放在中断处理程序中执行;

如果一个人和和硬件相关,将其放在中断处理程序中执行;

如果一个任务要保证不被其他中断打断,将其放在中断处理程序中执行;

其余情况考虑延后机制中执行——下半部。

二 中断推后执行机制—— 软中断

软中断是在编译期间静态分配的,在程序执行前将软中断假如到表中。

下面看一下这个过程:

加入软中断类型:

Linux3.5.3代码:

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, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS };

//软中断表:

static struct softirq_action softirq_vec[NR_SOFTIRQS]

//软中断结构体

struct softirq_action
{
void (*action)(struct softirq_action *);
};

 

注册软中断处理函数:

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
//关联表中对应类型
  softirq_vec[nr].action = action;
}

 

触发软中断:

void raise_softirq(unsigned int nr)
{
unsigned long flags; //停止但保存中断标志
local_irq_save(flags); //将相应软中断挂起状态
raise_softirq_irqoff(nr); //恢复中断
local_irq_restore(flags);
}

 

执行软中断:

void irq_exit(void)
{
  invoke_softirq(); //do_softirq();
} void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu; //获取CPU软中断状态标志位 32位代表最多32个软中断
pending = local_softirq_pending(); restart:
/* Reset the pending bitmask before enabling irqs */
set_softirq_pending(0);
local_irq_enable();
h = softirq_vec;
do {
//被触发则执行软中断处理程序
if (pending & 1) {
          h->action(h);
} //下一个软中断
h++; //下一个软中断状态标志位
pending >>= 1;
} while (pending); local_irq_disable();
pending = local_softirq_pending();
if (pending && --max_restart)
goto restart; if (pending)
wakeup_softirqd();
lockdep_softirq_exit();
    __local_bh_enable(SOFTIRQ_OFFSET);
}

软中断的基本结构如下图表示:

    

三  中断推后执行机制——tasklet

软中断中表中有一种类型是:TASKLET_SOFTIRQ

Tasklet就是利用软中断实现中断推后处理机制。通常使用较多的是tasklet而不是软中断。

Tasklet数据结构: 

struct tasklet_struct
{
//链表中下一个tasklet
struct tasklet_struct *next; //tasklet状态
unsigned long state; //引用计数器
atomic_t count; //tasklet处理函数
void (*func)(unsigned long); //处理函数参数
    unsigned long data; };

state:

enum
{
  TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */
  TASKLET_STATE_RUN /* Tasklet is running (SMP only) */
};

count:为0允许激活执行

声明tasklet:可以动态或者静态方式

静态:

#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } #define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

动态:

void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}

  同时需要编写tasklet处理函数。

调度tasklet:

void tasklet_hi_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = NULL;
*__this_cpu_read(tasklet_vec.tail) = t;
__this_cpu_write(tasklet_vec.tail, &(t->next));
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags);
}

执行tasklet处理程序:

继续看上面调度tasklet程序执行:

 inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
if (!in_interrupt())
wakeup_softirqd();
} //使用ksoftirqd内核线程来处理
static void wakeup_softirqd(void)
{
/* Interrupts are disabled: no need to stop preemption */
struct task_struct *tsk = __this_cpu_read(ksoftirqd);
if (tsk && tsk->state != TASK_RUNNING)
wake_up_process(tsk);
}

Ksoftirqd内核线程:

软中断才被触发频率很高,在处理过程中还会重新触发软中断;执行会导致用户空间进程无法获得处理时间处于饥饿状态;

对重新触发的软中断立即处理,会导致占据处理时间过长;不进行立即处理不合适;

对此解决方法:

  l  只要还有被触发并等待处理和过程中重新触发的软中断的软中断,本次执行就要负责处理;软中断立即处理,用户空间得不到执行时间。

  l  不处理过程中触发的软中断,放到下一个中断执行时机时处理。软中断得不到立即处理,系统空闲时造成不合理;保证用户空间得到执行时间。

两种方式有存在问题,只能在这其中采取这种的方式:

内核使用线程处理软中断,线程优先级较低,可以被抢占;能够保证软中断被处理,也能保证用户空间程序得到执行时间。

每个CPU上有存在这样一个线程:ksoftirqd/0或者ksoftirqd/1……

static __init int spawn_ksoftirqd(void)
{
void *cpu = (void *)(long)smp_processor_id();
int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
……
return 0;
} early_initcall(spawn_ksoftirqd);
static int __cpuinit cpu_callback(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
struct task_struct *p; switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
p = kthread_create_on_node(run_ksoftirqd,
hcpu,
cpu_to_node(hotcpu),
"ksoftirqd/%d", hotcpu);
kthread_bind(p, hotcpu);
per_cpu(ksoftirqd, hotcpu) = p;
break;
……
}

四 中断推后执行机制——工作队列

工作队列(work queue)通过内核线程将中断下半部分程序推后执行到线程中执行,工作队列可以创建线程来处理相应任务。

工作队列创建的线程为工作者线程:worker thread;系统提供默认的线程来处理工作者队列。

  

      

工作者线程数据结构:

struct workqueue_struct {
unsigned int flags; /* W: WQ_* flags */
union {
struct cpu_workqueue_struct __percpu *pcpu;
struct cpu_workqueue_struct *single;
unsigned long v;
} cpu_wq; /* I: cwq's */
struct list_head list; /* W: list of all workqueues */
……
}

CPU工作队列数据结构:

struct cpu_workqueue_struct {
//每个CPU工作队列信息
struct global_cwq *gcwq;

//每个CPU工作队列
struct workqueue_struct *wq;
……
};

工作数据结构:

struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; };

声明工作队列:

静态:

#define DECLARE_WORK(n, f)                             \
struct work_struct n = __WORK_INITIALIZER(n, f)

动态:

#define INIT_WORK(_work, _func)                                   \
do { \
__INIT_WORK((_work), (_func), 0); \
} while (0)  

需要编写工作队列处理函数:

typedef void (*work_func_t)(struct work_struct *work);

调度工作队列:

int schedule_work(struct work_struct *work)
{
return queue_work(system_wq, work);
}
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
ret = queue_work_on(get_cpu(), wq, work);
}

唤醒工作者队列线程处理。

执行工作者队列处理程序:

static int worker_thread(void *__worker)
{
do {
struct work_struct *work =
list_first_entry(&gcwq->worklist,
struct work_struct, entry);
process_one_work(worker, work);
} while (keep_working(gcwq)); }

可以创建新的工作者队列和线程来处理。

平衡是个很关键的问题!

(笔记)Linux内核学习(五)之中断推后处理机制的更多相关文章

  1. (笔记)Linux内核学习(八)之定时器和时间管理

    一 内核中的时间观念 内核在硬件的帮助下计算和管理时间.硬件为内核提供一个系统定时器用以计算流逝的时间.系 统定时器以某种频率自行触发,产生时钟中断,进入内核时钟中断处理程序中进行处理. 墙上时间和系 ...

  2. (笔记)Linux内核学习(九)之内核内存管理方式

    一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页 ...

  3. (笔记)Linux内核学习(二)之进程

    一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器. 内核调度的对象是线程而不是进程.对 ...

  4. (笔记)Linux内核学习(四)之系统调用

    一 用户空间和内核空间 Linux内核将这4G字节虚拟地址空间的空间分为两部分: l  将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”. l  ...

  5. (笔记)Linux内核学习(一)之内核介绍

    内核与操作系统: 内核是操作系统的核心部分,包含了系统运行的核心过程,决定系统的性能,操作系统启动内核被装入到RAM中: 操作系统与底层硬件设备交互和为运行应用程序提供执行环境. Linux内核与微内 ...

  6. (笔记)Linux内核学习(三)之进程调度

    进程调度: 在可运行态进程之间分配有限处理器时间资源的内核子系统. 一 调度策略 1 进程类型 I/O消耗型进程:大部分时间用来提交I/O请求或是等待I/O请求,经常处于可运行状态,但运行时间短,等待 ...

  7. (笔记)Linux内核学习(十)之虚拟文件系统概念

    虚拟文件系统 虚拟文件系统:内核子系统VFS,VFS是内核中文件系统的抽象层,为用户空间提供文件系统相关接口: 通过虚拟文件系统,程序可以利用标准Linux文件系统调用在不同的文件系统中进行交互和操作 ...

  8. (笔记)Linux内核学习(六)之并发和同步概念

    一 临界区和竞争条件 临界区:访问和操作共享数据的代码段. 竞争条件:多个执行线程处于同一个临界区中. 处于竞争条件:造成访问的数据或者资源不一致状态: 对资源i的访问:ProcessA和B访问后得到 ...

  9. (笔记)Linux内核学习(七)之内核同步机制和实现方式

    一 原子操作 指令以原子的方式执行——执行过程不被打断. 1 原子整数操作 原子操作函数接收的操作数类型——atomic_t //定义 atomic_t v;//初始化 atomic_t u = AT ...

随机推荐

  1. php数据库常用函数

    //打开mysqlmysql_connect( $host, $user, $pwd ) or die('error');$host => localhost //数据库地址$user => ...

  2. 利用Formdata实现form提交文件上传不跳转页面

    作者:幻月九十链接:https://www.zhihu.com/question/19631256/answer/119911045来源:知乎著作权归作者所有,转载请联系作者获得授权. $('form ...

  3. Linux WAS7 启动异常

    启动server1异常,信息如下: [root@cmiecmceprd02 bin]# ./startServer.sh server1ADMU0116I: Tool information is b ...

  4. C语言 串 顺序结构 实现

    一个能够自动扩容的顺序结构的串 ArrString (GCC编译). /** * @brief C语言 串 顺序结构 实现 * @author wid * @date 2013-11-01 * * @ ...

  5. python __del__

    python __del__ 转自:http://blog.csdn.net/bbdxf/article/details/25774763 最近学习<Python参考手册>学到Class部 ...

  6. jenkins2 插件安装

    文章来自:http://www.ciandcd.com 文中的代码来自可以从github下载: https://github.com/ciandcd Jenkins的安装包和插件在7个国家有20多个镜 ...

  7. 说不尽的MVVM(3) – 从通知属性说起

    上篇我们体验了一个从事件处理程序到MVVM程序的转变,在最后也留下了一个问题:RaisePropertyChanged的原理是什么?今天我们来一探究竟. 通过上节做的小例子我们知道,仅仅修改ViewM ...

  8. 在ASP.NET MVC5中建置以角色为基础的授权机制

    在前一篇贴文中,已探索过如何在MVC5中自定ASP.NET Identity,接下来要来试试在MVC5中如何运用 ASP.NET Identity来设定一个以 "角色"为基础的授权 ...

  9. atitit.api设计 方法 指南 手册 v2 q929.docx

    atitit.api设计 方法 指南 手册 v2 q929.docx atitit.api设计原则与方法 1. 归一化(锤子钉子理论)1 1.1. 链式方法2 1.2. 规则5:建立返回值类型2 1. ...

  10. ES6类与模块

    class Animal { // 构造方法,实例化的时候会被调用,如果不指定,那么会有一个不带参数的默认构造函数 constructor(name, color) { this.name = nam ...