linux内核中断实践4:软中断

news/2024/7/8 1:39:03

前言

       本次实验也是在定时器处理函数中进行的,修改了Interrupt.h (include\linux)和文件Softirq.c (kernel)文件,用来增加新的软中断。

        实验结果可靠,没问题。        

        如果使用硬件中断的话需要配置设备树,然后从设备树获取irq号,然后就是使用request_irq函数。剩下的就都一样了。

Linux实现底半部的机制主要有tasklet、工作队列、软中断和线程化irq。

一 软中断(Softirq)

        软中断(Softirq)也是一种传统的底半部处理机制,它的执行时机通常是顶半部返回的时候,tasklet是基于软中断实现的,因此也运行于软中断上下文。

        在Linux内核中,用softirq_action结构体表征一个软中断,这个结构体包含软中断处理函数指针和传递给该函数的参数。使用open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。

/* softirq mask and active fields moved to irq_cpustat_t in
 * asm/hardirq.h to get better cache usage.  KAO
 */

struct softirq_action
{
	void	(*action)(struct softirq_action *);
};
extern void open_softirq(int nr, void (*action)(struct softirq_action *));
extern void raise_softirq(unsigned int nr);

    

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
   frequency threaded job scheduling. For almost all the purposes
   tasklets are more than enough. F.e. all serial device BHs et
   al. should be converted to tasklets, not to softirqs.
 */

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
};

        大概意思,也是不要使用softirqs,用tasklets就够了。

        软中断和tasklet运行于软中断上下文,仍然属于原子上下文的一种,而工作队列则运行于进程上下文。因此,在软中断和tasklet处理函数中不允许睡眠,而在工作队列处理函数中允许睡眠。local_bh_disable()和local_bh_enable()是内核中用于禁止和使能软中断及tasklet底半部机制的函数。

static inline void local_bh_disable(void)
{
	__local_bh_disable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
}

/*
 * Special-case - softirqs can safely be enabled in
 * cond_resched_softirq(), or by __do_softirq(),
 * without processing still-pending softirqs:
 */
void _local_bh_enable(void)
{
	WARN_ON_ONCE(in_irq());
	__local_bh_enable(SOFTIRQ_DISABLE_OFFSET);
}
EXPORT_SYMBOL(_local_bh_enable);

static void __local_bh_enable(unsigned int cnt)
{
	WARN_ON_ONCE(!irqs_disabled());

	if (softirq_count() == (cnt & SOFTIRQ_MASK))
		trace_softirqs_on(_RET_IP_);
	preempt_count_sub(cnt);
}

/*
 * This one is for softirq.c-internal use,
 * where hardirqs are disabled legitimately:
 */
#ifdef CONFIG_TRACE_IRQFLAGS
void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
{
	unsigned long flags;

	WARN_ON_ONCE(in_irq());

	raw_local_irq_save(flags);
	/*
	 * The preempt tracer hooks into preempt_count_add and will break
	 * lockdep because it calls back into lockdep after SOFTIRQ_OFFSET
	 * is set and before current->softirq_enabled is cleared.
	 * We must manually increment preempt_count here and manually
	 * call the trace_preempt_off later.
	 */
	__preempt_count_add(cnt);
	/*
	 * Were softirqs turned off above:
	 */
	if (softirq_count() == (cnt & SOFTIRQ_MASK))
		trace_softirqs_off(ip);
	raw_local_irq_restore(flags);

	if (preempt_count() == cnt) {
#ifdef CONFIG_DEBUG_PREEMPT
		current->preempt_disable_ip = get_parent_ip(CALLER_ADDR1);
#endif
		trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1));
	}
}
EXPORT_SYMBOL(__local_bh_disable_ip);
#endif /* CONFIG_TRACE_IRQFLAGS */

        内核中采用softirq的地方包括HI_SOFTIRQ、TIMER_SOFTIRQ、NET_TX_SOFTIRQ、NET_RX_SOFTIRQ、SCSI_SOFTIRQ、TASKLET_SOFTIRQ等,一般来说,驱动的编写者不会也不宜直接使用softirq。

schedule_work(&my_wq);     /* 调度工作队列执行 */

/**
 * schedule_work - put work task in global workqueue
 * @work: job to be done
 *
 * Returns %false if @work was already on the kernel-global workqueue and
 * %true otherwise.
 *
 * This puts a job in the kernel-global workqueue if it was not already
 * queued and leaves it in the same position on the kernel-global
 * workqueue otherwise.
 */
static inline bool schedule_work(struct work_struct *work)
{
	return queue_work(system_wq, work);
}

        与tasklet实践对应的使用工作队列处理中断底半部的设备驱动程序模板如下所示(仅包含与中断相关的部分)。

二 在一个定时器中测试工作队列实例

假如要设计自己的softirq

首先,在Interrupt.h (include\linux)文件,找到TIMER_SOFTIRQ定义的地方,在气候添加

MYTEST_SOFTIRQ,如下图

         再看看open_softirq函数原型:就是将传入的函数指针赋值非nr为索引的数组的原来的指针。假如这nr已经被赋值了,就覆盖了原来的值。假如我们给nr一个TIMER_SOFTIRQ,可能定时器就不能工作了,系统也可能会死掉。

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
	softirq_vec[nr].action = action;
}

softirq_vec就是软中断数组,这说明整个系统中最多,也就NR_SOFTIRQS数量个软中断。其中的MYTEST_SOFTIRQ是我刚加的。

enum
{
	HI_SOFTIRQ=0,
	TIMER_SOFTIRQ,
	MYTEST_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] __cacheline_aligned_in_smp;

然后为了让软中断函数能工作,需要在定时器中断中调用:

raise_softirq(MYTEST_SOFTIRQ);

完整测试源码:csi_softirq.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>

#define DEBUG_SQ(format, ...)                                                                     \
  printk ("%s,line=%d:" format "\n", __func__, __LINE__, ##__VA_ARGS__)

struct cssq_dev_{
	struct timer_list timer;
};
struct cssq_dev_ global_cssq_dev;

void cssq_action(struct softirq_action *sq)
{
	static int count = 0;
	DEBUG_SQ("count = %d",++count);
}

static void cssq_timer_function(unsigned long arg)
{
	struct cssq_dev_ *cssq_dev = (struct cssq_dev_ *)arg;
	raise_softirq(MYTEST_SOFTIRQ);
	DEBUG_SQ("");
	mod_timer(&cssq_dev->timer,jiffies + msecs_to_jiffies(1000));
}

static int __init cssq_init(void)
{
	struct cssq_dev_ *cssq_dev = &global_cssq_dev;
	init_timer(&cssq_dev->timer);
	cssq_dev->timer.data = (unsigned long)cssq_dev;
	cssq_dev->timer.function = cssq_timer_function;	

	open_softirq(MYTEST_SOFTIRQ,cssq_action);
	
	mod_timer(&cssq_dev->timer,jiffies + msecs_to_jiffies(1000));
	DEBUG_SQ("init ok");	
	return 0;
}

static void __exit cssq_exit(void)
{
	struct cssq_dev_ *cssq_dev = &global_cssq_dev;
	del_timer_sync(&cssq_dev->timer);
	DEBUG_SQ("exit ok");	
}

module_init(cssq_init);
module_exit(cssq_exit);
MODULE_LICENSE("GPL");

Makefile文件

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
KERN_DIR = /home/lkmao/imx/linux/linux-imx

FILE_NAME=csi_softirq
obj-m += $(FILE_NAME).o

all:
    make -C $(KERN_DIR) M=$(shell pwd) modules

.PHONY:clean
clean:
    make -C $(KERN_DIR) M=$(shell pwd) clean

测试:

        首先编译一遍内核,因为修改了Interrupt.h (include\linux)文件,这个编译的很久。并使用新编译的内核启动系统,前提的能起来。如果起不来,记得将Interrupt.h (include\linux)文件修改回原来的状态。就洗洗睡吧。

        系统如果成功启动了,执行make,编译自己的模块csi_softirq.ko。编译的时候有警告:

lkmao@ubuntu:/big/csi_driver/csi_softirq$ make
make -C /home/lkmao/imx/linux/linux-imx M=/big/csi_driver/csi_softirq modules
make[1]: Entering directory '/home/lkmao/imx/linux/linux-imx'
  CC [M]  /big/csi_driver/csi_softirq/csi_softirq.o
  Building modules, stage 2.
  MODPOST 1 modules
WARNING: "open_softirq" [/big/csi_driver/csi_softirq/csi_softirq.ko] undefined!
WARNING: "raise_softirq" [/big/csi_driver/csi_softirq/csi_softirq.ko] undefined!
  CC      /big/csi_driver/csi_softirq/csi_softirq.mod.o
  LD [M]  /big/csi_driver/csi_softirq/csi_softirq.ko
make[1]: Leaving directory '/home/lkmao/imx/linux/linux-imx'
sudo cp csi_softirq.ko /big/nfsroot/jiaocheng_rootfs/home/root/
[sudo] lkmao 的密码:
lkmao@ubuntu:/big/csi_driver/csi_softirq$

        然后insmod。

root@hehe:~# insmod csi_softirq.ko
[  110.834509] csi_softirq: Unknown symbol raise_softirq (err 0)
[  110.840304] csi_softirq: Unknown symbol open_softirq (err 0)
insmod: ERROR: could not insert module csi_softirq.ko: Unknown symbol in module
root@hehe:~#

        这说明内核不让用啊,这两个函数没有被符号导出,好的,修改Softirq.c (kernel) ,将这两个符号导出。在Softirq.c (kernel) 文件尾部添加如下内容:

EXPORT_SYMBOL(open_softirq);
EXPORT_SYMBOL(raise_softirq);

       重新编译内核,然后重新编译模块:然后insmod csi_softirq.ko

root@hehe:~# insmod csi_softirq.ko
[  102.263731] cssq_init,line=39:init ok
root@hehe:~# [  103.263319] cssq_timer_function,line=25:
[  103.267270] cssq_action,line=18:count = 1
[  104.263304] cssq_timer_function,line=25:
[  104.267245] cssq_action,line=18:count = 2
[  105.263302] cssq_timer_function,line=25:
[  105.267239] cssq_action,line=18:count = 3
[  106.263316] cssq_timer_function,line=25:
[  106.267268] cssq_action,line=18:count = 4
[  107.263313] cssq_timer_function,line=25:
[  107.267264] cssq_action,line=18:count = 5
[  108.263298] cssq_timer_function,line=25:
[  108.267239] cssq_action,line=18:count = 6
[  109.263301] cssq_timer_function,line=25:
[  109.267240] cssq_action,line=18:count = 7
[  110.263297] cssq_timer_function,line=25:
[  110.267237] cssq_action,line=18:count = 8
[  111.263297] cssq_timer_function,line=25:
[  111.267236] cssq_action,line=18:count = 9
[  112.263311] cssq_timer_function,line=25:
[  112.267253] cssq_action,line=18:count = 10
... ...

rmmod csi_wq.ko

... ...
[  123.267266] cssq_action,line=18:count = 21
[  124.263311] cssq_timer_function,line=25:
[  124.267265] cssq_action,line=18:count = 22
root@hehe:~# rmmod csi_wq.ko
[  124.979652] cssq_exit,line=47:exit ok
root@hehe:~#

总结

        测试完毕后,记得将Interrupt.h (include\linux)和文件Softirq.c (kernel)修改回原来的状态。


http://lihuaxi.xjx100.cn/news/109682.html

相关文章

Linux LCD屏幕驱动调参实操

Linux LCD屏幕驱动调参实操 初探 Linux下的 LCD 驱动文中提到过&#xff0c; IMX6ULL的 eLCDIF接口驱动程序已经有半导体厂家NXP编写好了&#xff0c;并且不同分辨率LCD屏的eLCDIF接口驱动代码都是一样的&#xff0c;因此LCD驱动部分无需修改。只需要根据所使用的LCD来调整设备…

RocketMQ5.0 Broker主备自动切换模式消息不丢失部署方案

RocketMQ5.0主备自动切换模式下消息的不丢失方案如何进行部署&#xff1f;下面就来看一下 1. 下载RocketMQ5.0 首先我们去官网将RocketMQ5.0的版本下载下来。 $ wget https://dlcdn.apache.org/rocketmq/5.0.0/rocketmq-all-5.0.0-bin-release.zip 复制代码 下载完成后解压到…

React路由动画切换

目录 1.下载 2.配置 3.引入css 4.结合animate库 1.下载 cnpm install react-transition-group --save 2.配置 在路由配置文件/src/App.js文件下中导入动画组件 引入 import {TransitionGroup, CSSTransition} from react-transition-group 在路由组件模板中&#xff…

字节跳动后端面经(16)

redis主从是怎么做的 redis里面的字符串底层是什么结构 redis里的整型&#xff1f; Redis的跳表 缓存雪崩&#xff0c;缓存击穿&#xff0c;缓存穿透 mysql范围查询是怎么实现的&#xff1f; 什么是共享锁&#xff1f; 联合索引是什么&#xff1f;为什么会索引失效&#xff1f;…

为什么企业们更偏好使用华为云CDN?

互联网已经成为我们生活的基础&#xff0c;我们的生活离开不了服务器&#xff0c;数据库&#xff0c;企业更离不开&#xff0c;我们现在只要想到吃穿住喝&#xff0c;就会马上想到各种app&#xff0c;先在软件上游览一番&#xff0c;然后选择自己心仪的产品或商品下单&#xff…

GBase8s SYSBldRelease 函数

从连接到支持显式事务日志记录的 GBase 8s 数据库的会话中&#xff0c;可以通过发出调用内置 SYSBldPrepare( ) 的 SQL 函数注册或注销 DataBlade 模块。另一个内置函数&#xff0c;SYSBldRelease( )&#xff0c;返回本地数据库中 SYSBldPrepare( ) 函数的版本字符串。 通过 SQ…

基于SSH的网上订餐系统设计与实现

目录 摘 要 I 第一章 引言 1 1.1背景和意义 1 1.2国内外现状 1 第二章 系统开发平台 3 2.1开发工具介绍 3 2.2主要开发技术介绍 4 2.2.1 JavaEE平台 4 2.2.2 SSH技术介绍 5 2.2.3 MySQL数据库 7 2.3系统环境需求 8 第三章 需求分析 9 3.1可行性分析 9 3.1.1经济可行性 9 3.1.2…

基于网络系统管理大赛 Centos7.9部署VxLAN实验

本期目录 概述实验环境参数单播模式组播模式概述 VXLAN(Virtual eXtensible LAN,虚拟可扩展的局域网),是一种虚拟化隧道通信技术。它是一种overlay(覆盖网络)技术,通过三层的网络搭建虚拟的二层网络。 由RFC7348中定义:A framework for overlaying virtualized layer…