一 中断

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

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

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

  硬件中断延后再进行处理

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

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

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

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

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

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

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

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

下面看一下这个过程:

加入软中断类型:

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
{//链表中下一个taskletstruct 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 内核学习笔记)

    操作系统进程学习(Linux 内核学习笔记) 进程优先级 并非所有进程都具有相同的重要性.除了大多数我们所熟悉的进程优先级之外,进程还有不同的关键度类别,以满足不同需求.首先进程比较粗糙的划分,进程可 ...

  2. Linux内核学习(五):linux kernel源码结构以及makefile分析

    Linux内核学习(五):linux kernel源码结构以及makefile分析 前面我们知道了linux内核镜像的生成.加载以及加载工具uboot. 这里我们来看看linux内核的源码的宏观东西, ...

  3. 我的Linux内核学习笔记

    在开始今天的内容之前,其实有一些题外话可以和大家分享一下.自从工作以来,我个人一直都有一个观点.那就是怎么样利用简单的代码来说明开发中的问题,或者是解释软件中的原理,这是一个很高的学问.有些道理看上去 ...

  4. Linux内核学习笔记

    1.vanbreaker的专栏 2.LinuxKernel Exploration 3.DroidPhone的专栏 4.Linux内核研究以及学习文档和ARM学习以及研究的开放文档   [力荐] 5. ...

  5. Linux内核学习笔记之网卡驱动的详细分析:RTL8139

    学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让 人有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏 ...

  6. Linux内核学习笔记(十)中断处理的下半部(Bottom Halve)

    为什么需要下半部 中断处理程序有如下局限性: 中断处理程序是异步中断,被其中断执行的代码(包括别的中断处理程序)可能正在执行非常重要的任务,为了避免被中断进程停止过长时间,中断处理程序的执行应该越快越 ...

  7. 20135316王剑桥Linux内核学习笔记第三周

    20135316王剑桥 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC 1000029000 三个法宝:存储程序计算机.函数调 ...

  8. linux内核学习笔记【一】临时内核页表 Provisional kernel Page Tables

    最近开始学习linux内核,看了<深入理解linux内核>,开始写点学习收获.内核版本为2.6.11 临时全局目录(provisional page global directory)是在 ...

  9. [Linux][内核学习笔记]--CFS调度器

    文章目录 1. 进程的状态转换 2. 内核调度器的发展 3. 调度策略 4. 与调度相关的系统调用 5. 优先级 6. CFS调度器的实现 6.1 相关结构体 6.1.1 sched_entity 结 ...

最新文章

  1. 2021年大数据Spark(二十二):内核原理
  2. tar常见文件解压法
  3. oracle 创建数据库 表空间 用户 授权和toad导入导出数据库
  4. 【C++】C++的拷贝控制
  5. 【PC工具】几个电脑录屏相关软件,手机投屏电脑,电脑显示手机摄像头图像,必须好用无广告!...
  6. python实时得到鼠标的位置
  7. [译]基于GPU的体渲染高级技术之raycasting算法
  8. 增加新分类daily——“每天学了啥?”
  9. GridView生成序号
  10. WordPress广告管理插件Adning Advertising1.5.8汉化版
  11. windows下搭建SSH隧道内网映射
  12. 策略模式探究(二)多个门禁对接使用策略模式
  13. 莫古力最新服务器,《最终幻想14》将调整现有人口平均化策略
  14. 全球高分辨率(10m和30m)土地覆盖数据分享
  15. 项羽ol网站服务器,《项羽ol》4千万平米无缝地图带你飞
  16. 补丁问题(WannaCry)补丁问题
  17. 关于整除符号 / 的运用
  18. Java swing的功能测试类库 FEST-Swing
  19. Numpy给数组增加维度的操作
  20. 十堰一中2021高考 成绩查询,速看!十堰一中新高考攻略

热门文章

  1. gis里怎么把两个不同坐标系的图叠在一起_坐标系那些事儿
  2. php中怎么让图片没有缓存文件,PHP页面文件缓存,PHP图片缓存实例
  3. jpa 动态sql拼接_MyBatis还是JPA?终于有答案了!
  4. php文本域输出_如何在文本分类任务中Fine-Tune BERT
  5. java tts引擎_Android TTS系列二——如何开发一款系统级tts引擎?
  6. 集合 Collection 与迭代器 Iterator
  7. Spark基础学习笔记20:RDD持久化、存储级别与缓存
  8. 如何让VsCode自动格式化代码?
  9. 【codevs2301】【BZOJ2186】沙拉公主的困惑,数论练习之逆元与φ
  10. 软化边硬化边_启示录:做完线雕第三天特别丑、边哭边讲线雕有什么副作用?...