嵌入式(驱动开发)(中断处理)

news2025/1/14 1:12:26

一、什么是中断

一种硬件上的通知机制,用来通知CPU发生了某种需要立即处理的事件

分为:

  1. 内部中断 CPU执行程序的过程中,发生的一些硬件出错、运算出错事件(如分母为0、溢出等等),不可屏蔽
  2. 外部中断 外设发生某种情况,通过一个引脚的高、低电平变化来通知CPU (如外设产生了数据、某种处理完毕等等)

二、中断处理原理

任何一种中断产生,CPU都会暂停当前执行的程序,跳转到内存固定位置执行一段程序,该程序被称为总的中断服务程序,在该程序中区分中断源,然后进一步调用该中断源对应的处理函数。

中断源对应的处理函数被称为分中断处理程序,一般每一个分中断处理程序对应一个外设产生的中断

写驱动时,如果外设有中断,则需要编写一个函数(分中断处理程序)来处理这种中断

三、中断接口

3.1 中断申请

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
/*
参数:
	irq:所申请的中断号
	handler:该中断号对应的中断处理函数
	flags:中断触发方式或处理方式 
		触发方式:IRQF_TRIGGER_NONE 		//无触发
		 	 	IRQF_TRIGGER_RISING 	//上升沿触发
			 	IRQF_TRIGGER_FALLING  //下降沿触发
				IRQF_TRIGGER_HIGH  	//高电平触发
				IRQF_TRIGGER_LOW 		//低电平触发
		处理方式:
			   	IRQF_DISABLED		//用于快速中断,处理中屏蔽所有中断
				IRQF_SHARED		  //共享中断
		name:中断名 /proc/interrupts
		dev:传递给中断例程的参数,共享中断时用于区分那个设备,一般为对应设备的结构体地址,无共享中断时写NULL
返回值:成功:0 失败:错误码
*/

3.2 中断释放

void free_irq(unsigned int irq, void *dev_id)/*
功能:释放中断号
参数:
	irq:设备号
	dev_id:共享中断时用于区分那个设备一般强转成设备号,无共享中断时写NULL
*/

3.3 中断处理函数原型

typedef irqreturn_t (*irq_handler_t)(int, void *);
/*
参数:
	int:中断号
	void*:对应的申请中断时的dev_id
返回值:
	typedef enum irqreturn irqreturn_t;	//中断返回值类型
	enum irqreturn {
		IRQ_NONE	= (0 << 0),
		IRQ_HANDLED	= (1 << 0),
		IRQ_WAKE_THREAD	= (1 << 1),
	};
	返回IRQ_HANDLED表示处理完了,返回IRQ_NONE在共享中断表示不处理
*/

四、按键驱动

按键原理图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4QNx41B-1668921574240)(.\按键原理图.jpg)]
exynos4412-fs4412.dts中增加节点

mykey2_node {
	compatible = "mykey2,key2";
	key2-gpio = <&gpx1 1 0>;
	interrupt-parent = <&gpx1>;
	interrupts = <1 3>;
};

按键驱动函数

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include "fs4412_key.h"   //自己写的.h用“”引用,库用<>引用
#include <linux/delay.h>


int major = 11;				//主设备号
int minor = 0;				//次设备号
int fs4412key2_num = 1;			//设备数量

struct fs4412key2_dev			//led设备结构体
{
	struct cdev mydev;		//设备结构体

	int gpio;				//设备gpio成员变量
	int irqno;				//中断
	
	struct keyvalue data;	//按键存储的数据
	int newflag;			//新数据到来的标志位
	spinlock_t lock;		//自旋锁
	
	wait_queue_head_t rq;	//读忙等待队列
};

struct fs4412key2_dev *pgmydev = NULL;     //定义一个设备结构体变量,用于调用结构体成员

int fs4412key2_open(struct inode *pnode,struct file *pfile)	//打开文件函数
{																					//inode类型结构体中i_cdev是mydev的地址														
	pfile->private_data = (void *) (container_of(pnode->i_cdev,struct fs4412key2_dev,mydev));//知道成员地址可以得出结构体地址
    return 0;																		
}

int fs4412key2_close(struct inode *pnode,struct file *pfile)
{
	return 0;
}

ssize_t mychar_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	if(count < sizeof(struct keyvalue))
	{
		printk("expect read size is invalde\n");
		return -1;
	}

	spin_lock(&pmydev->lock);		//上锁
	if(!pmydev->newflag)		//无数据时进入
	{
		if(pfile->f_flags & O_NONBLOCK)	
		{	//非阻塞
			spin_unlock(&pmydev->lock);
			printk("O_NONBLOCK NO Data Read\n");
			return -1;
		}
		else
		{	//阻塞
			spin_unlock(&pmydev->lock);
			ret = wait_event_interruptible(pmydev->rq,pmydev->newflag == 1);	//等待条件是pmydev->newflag == 1也就是有数据到来
			if(ret)
			{
				printk("Wake up by signal\n");
				return -ERESTARTSYS;
			}
			spin_lock(&pmydev->lock);
		}
	}
	
	if(count > sizeof(struct keyvalue))				//对读取数据的长度做一个限制
	{
		size = sizoef(struct keyvalue);
	}
	else
	{
		size = count;
	}

	ret = copy_to_user(puser,&pmydev->data,size);	//将内核数据拷贝到用户
	if(ret)
	{
		spin_unlock(&pmydev->lock);
		printk("copy_to_user failed\n");
		return -1;
	}

	pmydev->newflag = 0;			//将数据标志物清零,为下次进入做准备
	
	spin_unlock(&pmydev->lock);		//开锁
	
	return size;
}

unsigned int mychar_poll(struct file *pfile,poll_table *ptb)		//决定什么时候能读数据,这儿好像没用着。app上没些poll函数
{			
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	unsigned int mask = 0;
	poll_wait(pfile,&pmydev->rq,ptb);					//将读队列加入到表里,但是未休眠

	spin_lock(&pmydev->lock);							//上锁
	if(pmydev->newflag)									//当newflag为真是表示有数据,可读打开
	{
		mask |= POLLIN | POLLRDNORM;					//读打开
	}
	spin_unlock(&pmydev->lock);
	
	return mask;
}

struct file_operations myops = {					//设备的操作函数,自己写的子函数必须在这儿与内核函数关联起来才能被调用
        .owner = THIS_MODULE,
        .open = fs4412key2_open,
        .release = fs4412key2_close,
		.read = mychar_read,
		.poll = mychar_poll,
};

irqreturn_t key2_irq_handle(int no,void *arg)			//按键中断服务函数
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;
	int status = 0;

	status1 = gpio_get_value(pmydev->gpio);		//消抖
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	if(status1 != status2)
	{
		return IRQ_NONE;
	}

	status = status1;

	spin_lock(&pmydev->lock);					//上锁
	if(status == pmydev->data.status)			
	{
		spin_unlock(&pmydev->lock);
		return IRQ_NONE;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	pmydev->newflag = 1;
	
	spin_unlock(&pmydev->lock);
	wake_up(&pmydev->rq);

	return IRQ_HANDLED;
}
 
int __init fs4412key2_init(void)				
{
        int ret = 0;
        dev_t devno = MKDEV(major,minor);				//将主次设备号合成一个32位的设备号
		int ret = 0;
        struct device_node *pnode = NULL;				//定义一个变量用于存储设备树中的一个节点

		pnode = of_find_node_by_path ("/mykey2_node");		//从设备树获得key2节点
		if(NULL == pnode)		
		{
			printk("fialed of_find_node_by_path\n");
			return -1;
		}

		pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);//给设备结构体申请一块内存,kmalloc是申请小内存效率高。GFP_KERNEL是可以进行忙等待,因为这个是任务上下文
		if(NULL == pgmydev)			//申请失败
		{
			printk("kmalloc failed\n");
			return -1;
		}
		pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);	//从设备树中提取gpio口
		pgmydev->irqno = irq_of_parse_and_map(pnode,0);			//获得设备树中的中断号并进行映射

        /*申请设备号*/
        ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");	
        if(ret)
        {
                ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");		//手动申请失败时自动申请
                if(ret)
                {
                        printk("get devno failed\n");
                        kfree(pgmydev);							//申请失败时释放掉设备结构体
                        return -1;
                }
                major = MAJOR(devno);
        }
	
	    /*给struct cdev对象指定操作函数集*/
	    cdev_init(&pgmydev->mydev,&myops);				
	
	    /*将struct cdev对象添加到内核对应的数据结构里*/
	    pgmydev->mydev.owner = THIS_MODULE;
        cdev_add(&pgmydev->mydev,devno,fs4412key2_num);
        
		init_waitqueue_head(&pgmydev->rq);		//对等待队列头做初始化
     	spin_lock_init(&pgmydev->lock);			//对自旋锁做初始化,因为是异常上下文所以用自旋锁,他可以忙等待
     
     	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);//中断申请函数,初始化要放在锁初始化后面,要不然进入中断服务程序锁没初始化会出问题,
		if(ret)
		{
			printk("request_irq failed\n");
			kfree(pgmydev);
			pgmaydev = NULL;
			return -1;
		}		
        return 0;
}

void __exit fs4412key2_exit(void)
{
        dev_t devno = MKDEV(major,minor);
		free_irq(pgmydev->irqno,pgmydev);
        cdev_del(&pgmydev->mydev);
        unregister_chrdev_region(devno,fs4412key2_num);
        kfree(pgmydev);
        pgmydev = NULL;
}

MODULE_LICENSE("GPL");
module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

fs4412_key.h

#ifndef FS4412_KEY_H
#define FS4412_KEY_H

enum KEYCODE
{
	KEY2 = 1002,
	KEY3,
	KEY4,
};

enum KEY_STATUS
{
	KEY_DOWN = 0,
	KEY_UP,
};

struct keyvalue
{
	int code;
	int status;
};


#endif

app

#include <stdio.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include "leddrv.h"
#include <sys/stat.h>
#include <sys/ioctl.h>

int main(int argc,char *argv[])
{
	int fd = -1;
	
	if(argc < 2)
	{
		printf("The argument is too few\n");
		return -1;
	}
	
	fd = open(argv[1],O_RDONLY);		//
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 3;
	}
	
	while((ret = read(fd,&keydata,sizeof(keydata))) == sizeof(keydata))
	{
		if(keydata.status == KEY_DOWN)
		{
			printf("Key2 is down!\n");
		}
		else
		{
			printf("Key2 is up!\n");
		}
	}
	
	close(fd);
	fd = -1;
	return 0;
}

一、上半部与下半部

起源:

  1. 中断处理程序执行时间过长引起的问题
  2. 有些设备的中断处理程序必须要处理一些耗时操作

二、下半部机制之tasklet ---- 基于软中断(异常上下文)

6.1 结构体

struct tasklet_struct

{

​ struct tasklet_struct *next;

​ unsigned long state;

​ atomic_t count;

​ void (*func)(unsigned long); //重点关注

​ unsigned long data; //重点关注

};

6.2 定义tasklet的中断底半部处理函数

void tasklet_func(unsigned long data);

6.3 初始化tasklet

DECLARE_TASKLET(name, func, data);
/*
定义变量并初始化
参数:name:中断底半部tasklet的名称
	 Func:中断底半部处理函数的名字
	 data:给中断底半部处理函数传递的参数
*/
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data)

6.4 调度tasklet(上半部运行完后调用下面这个函数,运行上面那个*func函数指针指向的函数 )

void tasklet_schedule(struct tasklet_struct *t)
//参数:t:tasklet的结构体

三、按键驱动之tasklet版

在这里插入图片描述

struct fs4412key2_dev			//led设备结构体
{
	struct cdev mydev;		//设备结构体

	int gpio;				//设备gpio成员变量
	int irqno;				//中断
	
	struct keyvalue data;	//按键存储的数据
	int newflag;			//新数据到来的标志位
	spinlock_t lock;		//自旋锁
	
	wait_queue_head_t rq;	//读忙等待队列
	struct tasklet_struct tsk;
};

在init函添加

在这里插入图片描述

int __init fs4412key2_init(void)				
{
        int ret = 0;
        dev_t devno = MKDEV(major,minor);				//将主次设备号合成一个32位的设备号
		int ret = 0;
        struct device_node *pnode = NULL;				//定义一个变量用于存储设备树中的一个节点

		pnode = of_find_node_by_path ("/mykey2_node");		//从设备树获得key2节点
		if(NULL == pnode)		
		{
			printk("fialed of_find_node_by_path\n");
			return -1;
		}

		pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);//给设备结构体申请一块内存,kmalloc是申请小内存效率高。GFP_KERNEL是可以进行忙等待,因为这个是任务上下文
		if(NULL == pgmydev)			//申请失败
		{
			printk("kmalloc failed\n");
			return -1;
		}
		pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);	//从设备树中提取gpio口
		pgmydev->irqno = irq_of_parse_and_map(pnode,0);			//获得设备树中的中断号并进行映射

        /*申请设备号*/
        ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");	
        if(ret)
        {
                ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");		//手动申请失败时自动申请
                if(ret)
                {
                        printk("get devno failed\n");
                        kfree(pgmydev);							//申请失败时释放掉设备结构体
                        return -1;
                }
                major = MAJOR(devno);
        }
	
	    /*给struct cdev对象指定操作函数集*/
	    cdev_init(&pgmydev->mydev,&myops);				
	
	    /*将struct cdev对象添加到内核对应的数据结构里*/
	    pgmydev->mydev.owner = THIS_MODULE;
        cdev_add(&pgmydev->mydev,devno,fs4412key2_num);
        
		init_waitqueue_head(&pgmydev->rq);		//对等待队列头做初始化
     	spin_lock_init(&pgmydev->lock);			//对自旋锁做初始化,因为是异常上下文所以用自旋锁,他可以忙等待
     
     	tasklet_init(&pgmydev->tsk,bottom_irq_func,(unsigned long)pgmydev);//
     
     	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);//中断申请函数,初始化要放在锁初始化后面,要不然进入中断服务程序锁没初始化会出问题,
		if(ret)
		{
			printk("request_irq failed\n");
			kfree(pgmydev);
			pgmaydev = NULL;
			return -1;
		}		
        return 0;
}

上下中断函数

irqreturn_t key2_irq_handle(int no,void *arg)	//上
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	
	tasklet_schedule(&pmydev->tsk);//进入下半部
}

void bottom_irq_func(unsigned long *arg)			//下半部:一般处理一些耗时操作
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;
	int status = 0;

	status1 = gpio_get_value(pmydev->gpio);		//消抖
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	if(status1 != status2)
	{
		return;
	}

	status = status1;

	spin_lock(&pmydev->lock);					//上锁
	if(status == pmydev->data.status)			
	{
		spin_unlock(&pmydev->lock);
		return;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	pmydev->newflag = 1;
	
	spin_unlock(&pmydev->lock);
	wake_up(&pmydev->rq);

	return;
}

四、下半部机制之workqueue ----- 基于内核线程(任务上下文)

8.1 工作队列结构体:

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

struct work_struct {

​ atomic_long_t data;

​ struct list_head entry;

​ work_func_t func;

#ifdef CONFIG_LOCKDEP

​ struct lockdep_map lockdep_map;

#endif

};

8.2 定义工作队列底半部处理函数

void work_queue_func(struct work_struct *work);

8.3 初始化工作队列

struct work_struct work_queue;

初始化:绑定工作队列及工作队列的底半部处理函数

INIT_WORK(struct work_struct * pwork, _func) ;

参数:pwork:工作队列

​ func:工作队列的底半部处理函数

8.4 工作队列的调度函数

bool schedule_work(struct work_struct *work);

五、按键驱动之workqueue版

struct fs4412key2_dev			//led设备结构体
{
	struct cdev mydev;		//设备结构体

	int gpio;				//设备gpio成员变量
	int irqno;				//中断
	
	struct keyvalue data;	//按键存储的数据
	int newflag;			//新数据到来的标志位
	spinlock_t lock;		//自旋锁
	
	wait_queue_head_t rq;	//读忙等待队列
//	struct tasklet_struct tsk;
	struct work_struct wk;
};
int __init fs4412key2_init(void)				
{
        int ret = 0;
        dev_t devno = MKDEV(major,minor);				//将主次设备号合成一个32位的设备号
		int ret = 0;
        struct device_node *pnode = NULL;				//定义一个变量用于存储设备树中的一个节点

		pnode = of_find_node_by_path ("/mykey2_node");		//从设备树获得key2节点
		if(NULL == pnode)		
		{
			printk("fialed of_find_node_by_path\n");
			return -1;
		}

		pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);//给设备结构体申请一块内存,kmalloc是申请小内存效率高。GFP_KERNEL是可以进行忙等待,因为这个是任务上下文
		if(NULL == pgmydev)			//申请失败
		{
			printk("kmalloc failed\n");
			return -1;
		}
		pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);	//从设备树中提取gpio口
		pgmydev->irqno = irq_of_parse_and_map(pnode,0);			//获得设备树中的中断号并进行映射

        /*申请设备号*/
        ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");	
        if(ret)
        {
                ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");		//手动申请失败时自动申请
                if(ret)
                {
                        printk("get devno failed\n");
                        kfree(pgmydev);							//申请失败时释放掉设备结构体
                        return -1;
                }
                major = MAJOR(devno);
        }
	
	    /*给struct cdev对象指定操作函数集*/
	    cdev_init(&pgmydev->mydev,&myops);				
	
	    /*将struct cdev对象添加到内核对应的数据结构里*/
	    pgmydev->mydev.owner = THIS_MODULE;
        cdev_add(&pgmydev->mydev,devno,fs4412key2_num);
        
		init_waitqueue_head(&pgmydev->rq);		//对等待队列头做初始化
     	spin_lock_init(&pgmydev->lock);			//对自旋锁做初始化,因为是异常上下文所以用自旋锁,他可以忙等待
     
 //    	tasklet_init(&pgmydev->tsk,bottom_irq_func,(unsigned long)pgmydev);//tasklet的
     	INIT_WORK(&pgmydev->wk,);
     	
     	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);//中断申请函数,初始化要放在锁初始化后面,要不然进入中断服务程序锁没初始化会出问题,
		if(ret)
		{
			printk("request_irq failed\n");
			kfree(pgmydev);
			pgmaydev = NULL;
			return -1;
		}		
        return 0;
}

irqreturn_t key2_irq_handle(int no,void *arg)	//上
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	
//	tasklet_schedule(&pmydev->tsk);//进入下半部
	schedule_work(&pmydev->wk);

	return IRQ_HANDLED;
}

//void bottom_irq_func(unsigned long *arg)				//tasklet的
void bottom_irq_func(struct work_struct *pwk)			//下半部:一般处理一些耗时操作 ,参数改
{
	//struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;   //tasklet的
	struct fs4412key2_dev *pmydev = container_of(pwk,struct fs4412key2_dev,wk);  //改
	int status1 = 0;
	int status2 = 0;
	int status = 0;

	status1 = gpio_get_value(pmydev->gpio);		//消抖
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	if(status1 != status2)
	{
		return;
	}

	status = status1;

	spin_lock(&pmydev->lock);					//上锁
	if(status == pmydev->data.status)			
	{
		spin_unlock(&pmydev->lock);
		return;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	pmydev->newflag = 1;
	
	spin_unlock(&pmydev->lock);
	wake_up(&pmydev->rq);

	return;
}

六、下半部机制比较

任务机制

​ workqueue ----- 内核线程 能睡眠 运行时间无限制

异常机制 ------- 不能睡眠 下半部执行时间不宜太长( < 1s)

​ 软中断 ---- 接口不方便

​ tasklet ----- 无具体延后时间要求时

​ 定时器 -----有具体延后时间要求时

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/24412.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot怎么整合第三方缓存技术/EhCache缓存技术使用以及Redis缓存技术使用怎么在SpringBoot中使用

写在前面&#xff1a; 继续记录自己的SpringBoot学习之旅&#xff0c;这次是SpringBoot应用相关知识学习记录。若看不懂则建议先看前几篇博客&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 3.5 整合第三方技术 3.5.1 缓存 3.5.1.1 介绍 缓…

2022/11/21[指针] 多维数组与指针的联系

1、指向数组元素的指针变量 #include<stdio.h> int main() {int* p;int a[3][4] { {1,2,3,4},{5,6,7,8},{9,10,11,12} };int i, j;//将第0行第0列的地址赋给pfor (p a[0]; p < a[0] 12; p)//注意是a[0]{if ((p - a[0]) % 4 0)printf("\n");printf("…

java--Lambda(2)表达式语法

文章目录0 写在前面1 Lambda 表达式的五种形式1.1 不包含参数&#xff1a;1.2 包含一个参数&#xff1a;1.3 有多个参数1.4 表达式主体是不是一个代码块1.5 不声明参数类型2 写在末尾0 写在前面 最基本的 Lambda 表达式&#xff0c;它由三部分组成具体格式是这样子的&#xff…

【Py】使用flask-apscheduler动态调整作业参数(附源码)

之前的项目常使用Apscheduler进行定时任务调度&#xff0c;但最近想通过接口对这些任务进行动态调整&#xff0c;比如调整任务启停、调整任务执行时间、间隔时间等等 flask-apscheduler这个基于flask的库能够满足上面的需求&#xff0c;而且由于基于flask&#xff0c;所以我常…

查题校园免费题库接口

查题校园免费题库接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点…

ButterKnife依赖注入框架源码解析

BuffterKnife 采用 注解 APT技术 APT&#xff1a;Annotation Processor tool 注解处理器&#xff0c;是javac的一个工具&#xff0c;每个处理器都是继承于AbstractProcessor 注解处理器是运行在自己的java虚拟机中 APT如何生成字节码文件&#xff1a; Annotation Processing 不…

李立宗《讲给入门者的深度学习》

14天学习训练营导师课程&#xff1a; 李立宗《讲给入门者的深度学习》 一、什么是深度学习&#xff1f; 1、传统方法、机器学习、深度学习的区别&#xff1f; 以取暖为例&#xff0c;来说明三者的不同之处。 传统方法&#xff1a;通过火炉生火&#xff0c;需要生火、添柴、…

公众号免费题库使用

公众号免费题库使用 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 题库&#xff1a;题库后台&#xff08;点击跳转&#xff09;…

Python实现点选验证码识别, 模拟登陆小破站并自动发弹幕

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 开发环境: Python 3.8 Pycharm 2021.2 谷歌浏览器 谷歌驱动 模块使用: selenium >>> pip install selenium3.141.0 指定版本安装 time 打码平台 如果安装python第三方模块: win R 输入 cmd 点击确定, 输入…

【白话科普】从“熊猫烧香”聊聊计算机病毒

大家还记得2006年在网络上肆虐的“熊猫烧香”病毒吗&#xff1f; 虽然图标是一只小熊猫举着三根香&#xff0c;但是它是一款拥有自动传播、自动感染硬盘能力和强大的破坏能力的病毒&#xff0c;它不但能感染系统中exe&#xff0c;com&#xff0c;pif&#xff0c;src&#xff0c…

STM32实战总结:HAL之I2C

I2C基础知识参考&#xff1a; 嵌入式常见接口协议总结_路溪非溪的博客-CSDN博客 电路图 扩展的I2C接口&#xff0c;可以连接支持I2C的设备。常见于传感器等。 参考手册 目前大部分MCU都带有IIC总线接口&#xff0c;STM32F1也不例外。但是这里我们不使用STM32F1的硬件IIC&#x…

Linux查看磁盘、文件系统、文件夹、文件大小的命令(lsblk、df、du、ll)

记录&#xff1a;325 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用lsblk命令查看磁盘大小和磁盘挂载情况&#xff1b;使用df查看文件系统大小和挂载情况&#xff1b;使用du命令查看文件夹(目录)大小&#xff1b;使用ll和ls查看文件大小。 版本&#xff1a; 操作…

XXL-JOB任务有效期支持实现方案

概述 在做数据产品或平台系统时&#xff0c;经常会遇到类似如下截图中&#xff0c;有截至日期的定时调度任务的需求。即定时任务只在指定的开始日期-截至日期里指定的时间里执行。具体的业务需求场景&#xff0c;如营销活动的看板数据的订阅邮件&#xff0c;推送名单的活动&am…

实验(五):外部中断实验

一、实验目的与任务 实验目的&#xff1a; 1&#xff0e;掌握外部中断的工作原理&#xff1b; 2&#xff0e;学会中断程序设计。 任务&#xff1a; 1&#xff0e;运行Keil开发环境&#xff0c;完成外部中断响应软件编程&#xff1b; 2&#xff0e;外部中断接口分别接按键K1、K2…

Hibernate基本使用

注&#xff1a;本文使用maven创建项目。 目录&#xff1a;Hibernate简介&#xff1a;Hibernate使用&#xff1a;一、手动创建&#xff1a;1.建表&#xff1a;2.pom.xml中导入相关依赖&#xff1a;3.创建Hibernate核心配置文件hibernate.cfg.xml&#xff1a;4.创建实体类UserEnt…

Ubuntu系统、CentOS系统双网卡的配置

虚机双网卡配置前言一、CentOS系统1.配置网卡信息1.1编辑eth0网卡1.2查看eth0网卡信息1.3编辑eth1网卡1.4查看eth1网卡信息2.关闭网卡arp代答和rp_filter校验2.1编辑配置文件2.2查看配置文件3.重启网络服务4.配置路由4.1 配置路由4.2 查看路由二、Ubuntu系统1.配置网卡信息1.1.…

微信小程序运行机制和生命周期

一. 运行机制 首先了解下小程序的运行机制&#xff0c;小程序从启动到最终被销毁&#xff0c;会经历很多不同的状态&#xff0c;小程序在不同状态下会有不同的表现。大致运行机制如下图。 小程序生命周期图 接下来我们是图中概念讲解&#xff0c;项目中也会经常遇到。 1&…

etf动量轮动+大盘择时:年化30%的策略

原创文章第111篇&#xff0c;专注“个人成长与财富自由、世界运作的逻辑&#xff0c; AI量化投资”。 今天重点来探索一下elegantRL。 昨天的文章金融强化学习与finRL开发包里介绍了finRL的源码结构&#xff0c;背后的强化学习框架是elegantRL。 聚宽平台上有一个“动量轮动…

Java#18(面向对象三大特征之一:继承)

目录 一.继承 1.Java中提供了关键字extends,可以让一个类和另一个类建立起继承关系 2.继承的好处 3.什么时候使用继承? 二.继承的特点 java只支持单继承,不支持多继承,但支持多层继承 三.子类到底能继承父类中的哪些内容? 四.继承中成员变量和成员方法的访问特点 1. 继…

Apache Jmeter压力测试与性能监控,监测cpu、内存、磁盘、网络

1.官网下载Jmeter 解压&#xff0c;bin目录下 Windows 运行jmeter.bat 、Linux运行jmeter.sh 2.jmeter-plugins-manager 插件 测试机下载放置Jmeter的apache-jmeter-5.5\lib\ext 目录下,重新jmeter。 3.ServerAgent-2.2.3.zip下载 下载好放服务器端&#xff0c;给可执行文…