下半部笔记


1. 软中断

软中断实现

软中断是在编译期间静态分配,其结构如下所示,结构中包含一个接受该结构体指针作为参数的action函数。

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

在kernel/softirq.c中定义了一个包含32个结构体的数组,每个数组成员都是一个被注册的软中断,数组如下所示:

static struct softirq_action softirq_vec[NR_SOFTIRQS];

软中断处理程序

软中断处理函数action原型如下:

void siftirq_handler(struct softirq_action *);

内核通过如下的方式调用软中断处理函数:

my_softirq->action(my_softirq);

软中断不会抢占其他软中断,唯一可以抢占软中断的是中断处理程序。

软中断的执行

一个注册的软中断必须在被标记后才会被执行,软中断被唤起后,要在 do_softirq() 中执行,在do_softirq()函数中,遍历执行每一个被标记的软中断,如下所示:

u32 pending;
//pending表示32位的标志,用来标记32个软中断,若位设置为1说明该位对应的软中断唤起。
pending = local_softirq_pending(); if(pending){
struct softirq_action *h; set_softirq_pending(0);//重设置待处理的标志 h = softirq_vec;
do{
if(pending&1)
h->action(h);
h++;
pending>>1;
}while(pending);
}

软中断的使用

1)分配索引

在编译期间,通过在<linux/interrupt.h>中定义枚举类型来声明软中断,如下所示,其中软中断按照优先高低自上而下,新插入新的软中断时需要根据想要的优先级插入相应位置。

enum
{
HI_SOFTIRQ=0, //优先级高的tasklet
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ, //正常优先级的tasklet
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS
};

2)注册处理程序

可以通过open_softirq()函数注册软中断处理程序,两个参数:软中断索引号、处理函数。

open_softirq(NET_RX_SOFTIRQ, net_tx_action);

软中断处理程序执行时候,允许相应中断,但不能自己休眠。

在一个处理器运行时候,当前处理器上软中断被禁止。

3)触发中断

raise_softirq()函数可以实现软中断设置为挂起待执行,该函数在运行之前需要先禁止中断,触发后再恢复原来的状态。如果中断本来就已经被禁止,可以采用raise_softirq_irqoff()函数去唤醒中断。

raise_softirq(NET_RX_SOFTIRQ);//需要在使用前关中断,然后再恢复。

raise_softirq_irqoff(NET_RX_SOFTIRQ);//适用于中断本来就已经被禁止的情况

2.tasklet

    tasklet是在软中断基础上实现的,相当于对软中断中的HI_SOFTIRQ、TASKLET_SOFTIRQ的更改,将tasklet链表加入到以上两个软中断的处理函数中执行。
通常情况下,我们使用tasklet而不是软中断,使用软中断的情况屈指可数。

tasklet实现

tasklet结构

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

结构体重state成员,可以取0、TASKLET_STATE_RUN、TASKLET_STATE_SCHED。

TASKLET_STATE_RUN->正在运行

TASKLET_STATE_SCHED->已被调度

调度

调度相当于将未调度的tasklet结构添加到两个链表结构:tasklet_vec(普通优先级)、tasklet_hi_vec(高优先级)。

TASKLET_STATE_SCHED(); //tasklet调度函数

/*
* 检查tasklet状态是否为TASKLET_STATE_SCHED,是的话已被调度,直接返回
* 调用 __TASKLET_STATE_SCHED()函数
* 保存中断状态,然后禁止中断状态
* 将被调用的tasklet添加到tasklet链表
* 唤醒软中断HI_SOFTIRQ或者TASKLET_SOFTIRQ
* 恢复中断状态并返回

使用tasklet

1) 声明自己的tasklet

DECLEAR_TASKLET(name, func, data)   //声明后tasklet处于激活状态
DECLEAR_TASKLET_DISABLE(name, func, data)//声明后tasklet处于禁止状态

2) 编写tasklet处理程序

void tasklet_handler(unsigned long data)

因为tasklet是靠软中断实现的,因此不能睡眠,也就是说在tasklet处理函数中不能使用信号量或者其他阻塞式函数。

3)调度自己的tasklet

我们可以通过tasklet_schedule()函数并传递给他相应的tasklet指针来调度,如下所示:

tasklet_schedule(&mytasklet); //将tasklet指针传过去,来调度

要注意:tasklet总在调度他的处理器上执行。

工作队列

工作队列可以把工作推后,交由一个内个线程去执行,这个下半部分总是会在进程上下文中执行。

实现

工作队列最基本的是表现形式是把需要推后执行的任务交给特定的通用线程(工作队列也可以通过驱动程序创建工作者线程来处理推后工作,但是多数情况直接采用系统缺省的工作者线程来做推后工作)

数据结构

1) 表示线程的数据结构

struct workqueue_struct{
struct ypu_workqueue_struct cpu_wq[NR_CPUS];//数组每一项对应一个处理器
struct list_head list;
const char *name;
int singlethread;
int freezeable;
int rt;
}
struct cpu_workqueue_struct{
spinlock_t lock;// 锁来保护这种结构
struct list_head worklist;//工作列表
wait_queue_head_t more_work;
struct workqueue_struct * wq; // 关联工作队列结构
task_t *thread; }

注意:每一个工作者类型都关联一个自己的workqueue_struct.在该结构体里给每一个处理器(内的工作者线程)分配一个cpu_workqueue_struct。

  1. 表示工作的数据结构

所有的工作者线程都是通过普通的内核线程实现的,他们都执行worker_thread()函数。在他们初始化完成以后,每个函数执行一个死循环并进入休眠,当有操作被传入队列里的时候,线程就会被唤醒,以执行这些操作。

工作用work_struct结构体表示:

  struct work_struct{
atomic_long_t data; //64位原子操作整数
struct list_head enty;
work_func_t func;
}

这些结构体被连成链表,在每个处理器上的每种类型的队列都对应一个这样的链表。

使用工作队列

1)创建推后的工作

首先需要做的是创建一些需要推后完成的实际工作,通过宏DECLEAR_WORK在编译时静态创建结构体,如下所示:

DECLEAR_WORK(name; void(*func)(void*),void * data);
//这样会静态的创建一个名为name,处理函数为func,参数为data的结构体。

也可以在运行时通过指针创建一个工作,如下所示:

INIT_WORK(struct work_struct *work, void(*func), void *data);
//动态初始化一个由work指向的工作

2)工作队列处理函数

void work_handler(void *data);//工作队列处理函数原型

这个函数会有工作者线程执行,因此函数运行于进程上下文中,默认情况下,允许相应中断,不能持有任和锁。

需要注意的是,尽管操作函数运行于进程上下文中,但是他不能访问用户空间。

3)对工作进行调度

可以通过调用函数schedule_work() 把处理函数交给缺省的events工作线程

schedule_work(&work);

work马上就会被调度,一旦其所在的处理器上的工作者线程被唤醒,他就会被执行。

若不想work马上就工作,二十希望他进行一段延迟再执行,可以通过:

`schedule_delay_work(&work, delay);
//此时,直到delay的节拍时钟用完之后才会执行work

4)刷新操作

进入队列的工作会在工作者线程的下一次被唤醒时候执行,在继续下一步工作之前,需要保证一些操作已经执行完毕。对于模块来说这一点很重要,在卸载之前,他可能需要调用以下函数。而在内核部分,为了防止竞争条件的出现,也可能需要确保不再持有处理工作。

出于以上目的,内核准备了一个用于刷新指定工作队列的函数

void flush_schedule_work(void);

函数会一直等待,直到队列中所有对象都被执行以后才会返回。

5)创建新的工作队列

如果缺省的队列不能满足你的工作要求,需要创建新的工作队列与相应的工作者进程。由于这么做会在每个处理器上都创建一个工作者线程,所以只有在你明确了必须要自己创建一套线程来提高性能的情况下再创建自己的额工作队列。

这部分用的情况较少,需要的话再细看。

Linux内核设计笔记8——下半部的更多相关文章

  1. Linux内核设计笔记10——内核同步

    Linux内核同步笔记 几个基本概念 - 临界区(critical region):访问和操作共享数据的代码段: - 原子操作:操作在执行中不被打断,要么不执行,要么执行完: - 竞争条件: 两个线程 ...

  2. Linux内核设计笔记12——内存管理

    内存管理学习笔记 页 页是内核管理内存的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转化为物理地址的硬件)通常以页为单位进行处理,从虚拟内存的角度看,页就是最小单位. struct page{ ...

  3. Linux内核设计笔记7——中断

    中断与中断处理 何为中断? 一种由设备发向处理器的电信号 中断不与处理器时钟同步,随时可以发生,内核随时可能因为中断到来而被打断. 每一个中断都有唯一一个数字标志,称之为中断线(IRQ) 异常是由软件 ...

  4. Linux内核设计笔记14——块I/O层

    块I/O层 基本概念 系统中可以随机访问固定大小数据片的硬件设备称做块设备,这些固定大小的数据片称之为块.还有一种基本的设备称之为字符设备,其需要按照顺序访问,比如键盘. 扇区:块设备中最小的寻址单元 ...

  5. Linux内核设计笔记11——定时器

    定时器与时间管理笔记 内核中的时间 时钟中断:内核中的系统定时器以某种频率触发中断,该频率可以通过编程预定. 节拍率HZ:时钟中断的频率称为节拍率. 节拍:相邻两次中断的时间间隔称为节拍,1/节拍率. ...

  6. Linux内核设计笔记13——虚拟文件系统

    虚拟文件系统 内核在它的底层文件系统系统接口上建立一个抽象层,该抽象层使Linux可以支持各种文件系统,即便他们在功能和行为上存在很大差异. VFS抽象层定义了各个文件系统都支持的基本的.概念上的接口 ...

  7. 《Linux内核设计与实现》读书笔记(八)- 中断下半部的处理

    在前一章也提到过,之所以中断会分成上下两部分,是由于中断对时限的要求非常高,需要尽快的响应硬件. 主要内容: 中断下半部处理 实现中断下半部的机制 总结中断下半部的实现 中断实现示例 1. 中断下半部 ...

  8. 《Linux内核设计与实现》读书笔记 - 目录 (完结)

    读完这本书回过头才发现, 第一篇笔记居然是 2012年8月发的, 将近一年半的时间才看完这本书(汗!!!). 为了方便以后查看, 做个<Linux内核设计与实现>读书笔记 的目录: < ...

  9. 《Linux内核设计与实现》读书笔记(十七)- 设备与模块

    本章主要讨论与linux的设备驱动和设备管理的相关的4个内核成分,设备类型,模块,内核对象,sysfs. 主要内容: 设备类型 内核模块 内核对象 sysfs 总结 1. 设备类型 linux中主要由 ...

随机推荐

  1. SpringBoot整合Swagger2以及生产环境的安全问题处理

    1.创建springboot项目 https://www.cnblogs.com/i-tao/p/8878562.html 这里我们使用多环境配置: application-dev.yml(开发环境) ...

  2. Java Web项目里 classpath 具体指哪个路径

    classpath路径指什么 只知道把配置文件如:mybatis.xml.spring-web.xml.applicationContext.xml等放到src目录(就是存放代码.java文件的目录) ...

  3. MySQL 半同步重要状态参数说明

    如果配置了MySQL半同步复制,可以在主库查询半同步状态,命令为: SHOW GLOBAL STATUS LIKE '%semi%'; 参数说明      Rpl_semi_sync_master_c ...

  4. wdcp v3 pureftpd 无法登录问题解决

    wdcp v3 新建站点和ftp账号 单位无法登录ftp 在端口中也确实可以看到有进行在登录状态 错误原因: 防火墙端口没有开启该端口范围  20000-30000 这时候发现 改端口为20078  ...

  5. 网站http配置https -- 阿里云 nginx

    通过阿里云领取免费证书可将网站配置为https 步骤为下: 登陆阿里云点击sll证书,然后点击购买证书 选择免费的 然后立即购买 购买后会让你填写一些域名信息 然后提交签发证书 签发后点击下方下载 选 ...

  6. 月薪30-50K的大数据工程师们,他们背后是如何学习的

    ​ 这两天小编去了解了下大数据开发相关职位的薪资,主要有hadoop工程师,数据挖掘工程师.大数据算法工程师等,从平均薪资来看,目前大数据相关岗位的月薪均在2万以上,随着项目经验的增长工资会越来越高. ...

  7. QWT编译与配置-Windows/Linux环境

    QWT编译与配置-Windows/Linux环境 QWT和FFTW两种开源组件是常用的工程软件支持组件,QWT可以提供丰富的绘图组件功能,FFTW是优秀数字波形分析软件.本文使用基于LGPL版权协议的 ...

  8. Python 爬虫 七夕福利

    祝大家七夕愉快 妹子图 import requests from lxml import etree import os def headers(refere):#图片的下载可能和头部的referer ...

  9. N对数的排列问题 HDU - 2554

    N对数的排列问题 HDU - 2554 有N对双胞胎,他们的年龄分别是1,2,3,……,N岁,他们手拉手排成一队到野外去玩,要经过一根独木桥,为了安全起见,要求年龄大的和年龄小的排在一起,好让年龄大的 ...

  10. Docker开篇之基础概念篇

    What--什么是容器? 容器技术,是一种操作系统层的虚拟化(Operating system-level virtualization),它将应用软件系统打包成一个软件容器(Container),内 ...