(Linux驱动学习 - 6).Linux中断
一. Linux 中断 API 函数
1.中断号
每个中断都有一个中断号,通过中断号即可区分不同的中断,有的资料也把中断号叫做中
断线。在 Linux 内核中使用一个 int 变量表示中断号。
2.申请中断 - request_irq
函数原型:
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev)
功能:
①.request_irq 函数用于申请中断,此函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用;
②.request_irq 函数会激活(使能)中断;
③.申请中断成功返回(0),申请失败返回(其他负值),如果返回(-EBUSY)则表示中断已经被申请了。
参数:
irq:要申请中断的中断号;
handler:中断处理函数;
name:中断名字,设置后可在 /proc/interrupts 文件中看到对应的中断名字;
dev:如果将 flags 设置为 IRQF_SHARED 的话,dev 用来区分不同的中断,一般情况下将 dev 设置为设备结构体,dev 会传递给中断处理函数 irq_handler_t 的第二个参数;
flags:中断标志,常用的中断标志有以下:
3.释放中断 - free_irq
函数原型:
void free_irq(unsigned int irq,void *dev)
功能:
释放中断,若中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断,若中断是共享的(IRQF_SHARED),那么共享中断在被释放掉最后的中断处理函数的时候才会被禁止掉。
参数:
irq:要释放的中断;
dev:如果中断设置为共享(IRQF_SHARED),此参数用来区分具体的中断;
4.设置中断处理函数 - irqreturn_t (*irq_handler_t)(int,void*)
函数原型:
irqreturn_t (*irq_handler_t)(int,void*)
功能:
设置中断处理函数;
一般中断处理函数返回值为 return IRQ_RETVAL(IRQ_HANDLED)
参数:
int:传入中断号;
void:用于区分共享中断的不同设备,也可以指向设备数据结构;
5.中断使能与禁止函数
函数原型:
/* 使能中断 */
void enable_irq(unsigned int irq);/* 禁止中断(等待当前正在执行的中断函数执行完才返回) */
void disable_irq(unsigned int irq);/* 禁止中断(立刻返回,不会等待当前中断处理函数执行完毕) */
void disable_irq_nosync(unsigned int irq);
功能:
使能和禁止指定的中断;
disable_irq 函数要等到当前正在执行的中断处理函数执行完才返回;
参数:
irq:要禁止的中断号
函数原型:
/* 使能当前处理器中断系统 */
local_irq_enable();/* 禁止当前处理器中断系统 */
local_disbale();/* 禁止中断,并且将中断状态保存在 flags 中 */
local_irq_save(flags);/* 恢复中断,将中断恢复到 flags 状态 */
local_irq_restore(flags);
二.上半部与下半部
1.上半部与下半部的说明
中断处理函数完成的要尽可能快点执行完毕,如果中断处理函数内处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行;将耗时短的代码(例如响应中断,清除中断标志位)可以放在上半部完成。
这样做的目的是为了实现中断处理函数的快进快出,那些对时间敏感、执行速度快的操作可以放到中断处理函数中,也就是上半部。剩下的所有工作都可以放到下半部去执行,比如在上半部将数据拷贝到内存中,关于数据的具体处理就可以放到下半部去执行。
建议的参考:
①.如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
②.如果要处理的任务对时间敏感,可以放到上半部。
③.如果要处理的任务与硬件有关,可以放到上半部。
④.除了上述 3 点,其他任务优先考虑放到下半部。
2.软中断
可用 软中断 和 tasklet 来实现下半部。
在 Linux 内核中使用结构体 softirq_action 表示软中断
struct softirq_action
{void (*action)(struct softirq_action *);
};
一共有 10 个软中断,如下所示
static struct softirq_action softirq_vec[NR_SOFTIRQS];/* NR_SOFTIRQS 是枚举类型 */
enum
{HI_SOFTIRQ=0, /* 高优先级软中断 */TIMER_SOFTIRQ, /* 定时器软中断 */NET_TX_SOFTIRQ, /* 网络数据发送软中断 */NET_RX_SOFTIRQ, /* 网络数据接收软中断 */BLOCK_SOFTIRQ,BLOCK_IOPOLL_SOFTIRQ,TASKLET_SOFTIRQ, /* tasklet 软中断 */SCHED_SOFTIRQ, /* 调度软中断 */HRTIMER_SOFTIRQ, /* 高精度定时器软中断 */RCU_SOFTIRQ, /* RCU 软中断 */NR_SOFTIRQS
};
(1).注册软中断处理函数 - open_softirq
函数原型:
void open_softirq(int nr,void (*action)(struct softirq_action *);
功能:
注册对应的软中断处理函数;
软中断必须在编译的时候静态注册!Linux 内核使用 softirq_init 函数初始化软中断
参数:
nr:要开启的软中断,在 HI_SOFTIRQ ~ RCU_SOFTIRQ 里面选一个;
action:软中断对应的处理函数;
3.tasklet
(1).tasklet 结构体
Linux 内核使用 tasklet_struct 结构体来表示 tasklet:
struct tasklet_struct
{struct tasklet_struct *next; /* 下一个 tasklet */unsigned long state; /* tasklet 状态 */atomic_t count; /* 计数器,记录对 tasklet 的引用数 */void (*func)(unsigned long); /* tasklet 执行的函数,相当于中断处理函数 */unsigned long data; /* 函数 func 的参数 */
};
(2).初始化 tasklet - tasklet_init
函数原型:
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data);
功能:
初始化tasklet
参数:
t:要初始化的tasklet;
func:tasklet 的处理函数;
data:要传递给 func 函数的参数
也可使用宏 DECLARE_TASKLET 来一次性完成 tasklet 的定义和初始化
/*
* @param - name :要定义的tasklet名字,即tasklet_struct 变量
* @param func :tasklet的处理函数
* @param - data :传递给 func 函数的参数
*/
DECLARE_TASKLET(name,func,data);
(3).使 tasklet 在合适的时间运行 - tasklet_schedule
函数原型:
void tasklet_schedule(struct tasklet_struct *t);
功能:
在上半部,也就是中断处理函数中调用 tasklet_schedule 函数就能使 tasklet 在合适的时间运行
参数:
t:要调度的 tasklet , 也就是 DECLARE_TAKLET 宏里面的 name
(4).tasklet 的使用示例
/* 定义 taselet */
struct tasklet_struct testtasklet;/* tasklet 处理函数 */
void testtasklet_func(unsigned long data)
{/* tasklet 具体处理内容 */
}/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{....../* 调度 tasklet */tasklet_schedule(&testtasklet);......
}/* 驱动入口函数 */
static int __init xxxx_init(void)
{....../* 初始化 tasklet */tasklet_init(&testtasklet, testtasklet_func, data);/* 注册中断处理函数 */request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);......
}
4.工作队列
工作队列是另外一种下半部执行方式,工作队列在进程上下文执行,工作队列将要推后的
工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重
新调度。因此如果你要推后的工作可以睡眠那么就可以选择工作队列,否则的话就只能选择软
中断或 tasklet。
(1).工作结构体
struct work_struct
{atomic_long_t data;struct list_head entry;work_func_t func; /* 工作队列处理函数 */
};
(2).工作队列结构体
struct workqueue_struct
{struct list_head pwqs;struct list_head list;struct mutex mutex;int work_color;int flush_color;atomic_t nr_pwqs_to_flush;struct wq_flusher *first_flusher;struct list_head flusher_queue;struct list_head flusher_overflow;struct list_head maydays;struct worker *rescuer;int nr_drainers;int saved_max_active;struct workqueue_attrs *unbound_attrs;struct pool_workqueue *dfl_pwq;char name[WQ_NAME_LEN];struct rcu_head rcu;unsigned int flags ____cacheline_aligned;struct pool_workqueue __percpu *cpu_pwqs;struct pool_workqueue __rcu *numa_pwq_tbl[];
};
(3).工作者线程结构体
struct worker
{union {struct list_head entry;struct hlist_node hentry;};struct work_struct *current_work;work_func_t current_func;struct pool_workqueue *current_pwq;bool desc_valid;struct list_head scheduled;struct task_struct *task;struct worker_pool *pool;struct list_head node;unsigned long last_active;unsigned int flags;int id;char desc[WORKER_DESC_LEN];struct workqueue_struct *rescue_wq;
};
(4).初始化工作 - INIT_WORK / DECLARE_WORK
宏原型:
/*
* @param - _work:要初始化的工作,传入 work_struct 结构体
* @param _func :工作对应的处理函数
*/
#define INIT_WORK(_work,_func)/*
* @param - n :定义的工作(work_struct)
* @param - f :工作对应的处理函数
*/
#define DECLARE_WORK(n,f)
(5).工作调度函数 - schedule_work
函数原型:
bool schedule_work(struct work_struct *work)
功能:
开启工作的调度
成功时返回(0),失败时返回(其他值)
参数:
work:要调度的工作
(6).工作队列的使用示例
/* 定义工作(work) */
struct work_struct testwork;/* work 处理函数 */
void testwork_func_t(struct work_struct *work);
{/* work 具体处理内容 */
}/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{....../* 调度 work */schedule_work(&testwork);......
}/* 驱动入口函数 */
static int __init xxxx_init(void)
{....../* 初始化 work */INIT_WORK(&testwork, testwork_func_t);/* 注册中断处理函数 */request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);......
}
三.获取中断号
编写驱动的时候需要用到中断号,中断信息已经写到了设备树里面,我们可以通过 irq_of_parse_and_map 函数从 interrupts 属性中提取到对应的设备号。
1. irq_of_parse_and_map
函数原型:
unsigned int irq_of_parse_and_map(struct device_node *dev,int index)
功能:
获取中断号;
返回值为(中断号)
参数:
dev :设备结点
index :索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息
2. gpio_to_irq
若使用 GPIO 的话,可以使用 gpio_to_irq 函数来获取 gpio 对应中断号
函数原型:
int gpio_to_irq(unsigned int gpio)
功能:
获取 gpio 对应的中断号;
返回( GPIO 对应的中断号)
参数:
gpio:要获取的 gpio 的编号
四.GPIO按键中断实验代码编写
1.设备树
(1).流程图
(2).设备树代码
2.驱动代码
(1).流程图
(2).代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define IMX6UIRQ_CNT 1 /* 设备号个数 */
#define IMX6UIRQ_NAME "imx6uirq" /* 设备名字 */
#define KEY0VALUE 0X01 /* KEY0按键值 */
#define INVAKEY 0XFF /* 无效的按键值 */
#define KEY_NUM 1 /* 按键数量 *//* 中断 IO 描述结构体 */
struct irq_keydesc
{int gpio; /* gpio */int irqnum; /* 中断号 */unsigned char value; /* 按键对应的键值 */char name[10]; /* 名字 */irqreturn_t (*handler)(int,void *); /* 指向中断服务函数的函数指针 */
};/* imx6uirq 设备结构体 */
struct imx6uirq_dev
{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */struct device_node *nd; /* 设备结点 */atomic_t keyvalue; /* 有效的按键值 */atomic_t releasekey; /* 标记是否完成一次完整的按键动作 */struct timer_list timer; /* 定义一个定时器 */struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键中断信息描述数组 */unsigned char curkeynum; /* 当前的按键号 */
};/* irq 设备 */
struct imx6uirq_dev imx6uirq;/*** @description: KEY0 按键中断服务函数,开启定时器,延时 10 ms,定时器用于按键消抖* @param - irq : 中断号* @param - dev_id : 设备结构* @return : 中断执行结果*/
static irqreturn_t key0_handler(int irq,void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;/* 表示按键中断触发(双边沿触发) (因为按键平时为上拉状态) */dev->curkeynum = 0;dev->timer.data = (volatile long)dev_id;/* 2.让定时器回调函数 10 ms 后触发 */mod_timer(&dev->timer,jiffies + msecs_to_jiffies(10)); return IRQ_RETVAL(IRQ_HANDLED);
}/*** @description: 定时器服务函数,用于按键消抖,定时器到了以后再次读取按键值,* 如果按键还是处于按下的状态就表示按键动作有效* @param - arg : 设备结构体变量* @return : 无*/
void timer_function(unsigned long arg)
{unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;/* 获取上一刻的按键状态 */num = dev->curkeynum;keydesc = &dev->irqkeydesc[num];/* 读取当前 gpio 的状态 */value = gpio_get_value(keydesc->gpio);/* 若此刻 gpio 的状态为低电平 , 则消抖后确认按键确实是按下了 (因为这个定时器服务函数是在按键中断之后触发的) */if(0 == value){atomic_set(&dev->keyvalue,keydesc->value);}/* 若此刻 gpio 的状态为高电平 , 则消抖后确认按键确实是松开了 (因为这个定时器服务函数是在按键中断之后触发的) */else{atomic_set(&dev->keyvalue, 0x80 | keydesc->value); //最高为置 1 atomic_set(&dev->releasekey, 1); //标记松开按键}
}/*** @description: 按键 IO 初始化* @param : 无* @return : 成功返回(0),返回其他则为失败*/
static int keyio_init(void)
{unsigned char i = 0;int ret = 0;/* 1.获取设备结点 */imx6uirq.nd = of_find_node_by_path("/key");if(NULL == imx6uirq.nd){printk("key node not find!\r\n");return -EINVAL;}/* 2.获取 KEY 的 GPIO 编号 */for(i = 0;i < KEY_NUM;i++){imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd,"key-gpio",i);/* 若获取 gpio 编号失败 */if(imx6uirq.irqkeydesc[i].gpio < 0){printk("can not get key%d\r\n",i);}}for(i = 0;i < KEY_NUM;i++){memset(imx6uirq.irqkeydesc[i].name,0,sizeof(imx6uirq.irqkeydesc[i].name));sprintf(imx6uirq.irqkeydesc[i].name,"KEY%d",i);/* 3.申请 GPIO */gpio_request(imx6uirq.irqkeydesc[i].gpio,imx6uirq.irqkeydesc[i].name);/* 4.设置 IO 为输入 */gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);/* 5.获取 GPIO 的中断号 */imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd,i);#if 0/* 若使用 GPIO 的话,可以使用 gpio_to_irq 函数来获取 gpio 对应中断号 */imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endifprintk("key : %d ; gpio = %d , irqnum = %d\r\n",i,imx6uirq.irqkeydesc[i].gpio,imx6uirq.irqkeydesc[i].irqnum);}/* 6.申请中断 */imx6uirq.irqkeydesc[0].handler = key0_handler;imx6uirq.irqkeydesc[0].value = KEY0VALUE;for(i = 0;i < KEY_NUM;i++){ret = request_irq(imx6uirq.irqkeydesc[i].irqnum,imx6uirq.irqkeydesc[i].handler,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,imx6uirq.irqkeydesc[i].name,&imx6uirq);}/* 7.创建定时器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_function;return 0;
}/*** @description: 打开设备* @param - inode : 传递给驱动的 inode* @param - filp : 设备文件* @return : 0 为成功 , 其他为失败*/
static int imx6uirq_open(struct inode *inode,struct file *filp)
{/* 设置私有数据 */filp->private_data = &imx6uirq;return 0;
}/*** @description: 从设备读取数据* @param - filp : 文件描述符* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的字节数* @param - offt : 相对于文件首地址的偏移量* @return : 成功读取的字节数,如果为负值,则表示失败*/
static ssize_t imx6uirq_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;/* 读取消抖后的键值 */keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);/* 如果有按键按下 */if(releasekey){if(keyvalue & 0x80){keyvalue &= ~0x80; //将 keyvalue 最高为清零ret = copy_to_user(buf,&keyvalue,sizeof(keyvalue));}else{goto data_error;}/* 清除按下标志 */atomic_set(&dev->releasekey,0); }else{ goto data_error;}return 0;data_error:return -EINVAL;
}/* 设备操作函数 */
static struct file_operations imx6uirq_fops =
{.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,
};/*** @description: 驱动入口函数* @param : 无* @return : 无*/
static int __init imx6uirq_init(void)
{/* 1.创建设备号 */if(imx6uirq.major) //若定义了设备号{imx6uirq.devid = MKDEV(imx6uirq.major,0);register_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT,IMX6UIRQ_NAME);}else //若没有定义设备号{alloc_chrdev_region(&imx6uirq.devid,0,IMX6UIRQ_CNT,IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}/* 2.初始化cdev */cdev_init(&imx6uirq.cdev,&imx6uirq_fops);/* 3,添加一个cdev */cdev_add(&imx6uirq.cdev,imx6uirq.devid,IMX6UIRQ_CNT);/* 4.创建类 */imx6uirq.class = class_create(THIS_MODULE,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.class)){return PTR_ERR(imx6uirq.class);}/* 5.创建设备 */imx6uirq.device = device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.device)){return PTR_ERR(imx6uirq.device);}/* 6.初始化按键 */atomic_set(&imx6uirq.keyvalue,INVAKEY);atomic_set(&imx6uirq.releasekey,0);keyio_init();return 0;
}/*** @description: 驱动出口函数* @param : 无* @return : 无*/
static void __exit imx6uirq_exit(void)
{unsigned int i = 0;/* 1.删除定时器 */del_timer_sync(&imx6uirq.timer);/* 2.释放 中断 与 GPIO */for(i = 0;i < KEY_NUM;i++){free_irq(imx6uirq.irqkeydesc[i].irqnum,&imx6uirq);gpio_free(imx6uirq.irqkeydesc[i].gpio);}/* 3. 注销字符设备驱动 */cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);device_destroy(imx6uirq.class,imx6uirq.devid);class_destroy(imx6uirq.class);
}module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaneki");
Makefile:
KERNELDIR := /home/linux/IMX6ULL/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH :=$(shell pwd)
obj-m := imx6uirq.o
build: kernel_modules
kernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
可在 /proc/interrupts 文件下查看对应的中断有没有被注册
相关文章:

(Linux驱动学习 - 6).Linux中断
一. Linux 中断 API 函数 1.中断号 每个中断都有一个中断号,通过中断号即可区分不同的中断,有的资料也把中断号叫做中 断线。在 Linux 内核中使用一个 int 变量表示中断号。 2.申请中断 - request_irq 函数原型: int request_irq(unsigne…...

SpringBoot驱动的明星周边产品电商解决方案
1系统概述 1.1 研究背景 如今互联网高速发展,网络遍布全球,通过互联网发布的消息能快而方便的传播到世界每个角落,并且互联网上能传播的信息也很广,比如文字、图片、声音、视频等。从而,这种种好处使得互联网成了信息传…...
C++、Ruby和JavaScript
C C最初被称为带类的C, 兼容C的语法,此既是C得以流行的前提,也是C某些语法被捆绑的根源。C的来源于C语言的递增运算符,代表增加,意义为扩展。 C的历史 C类的设计思想来源于Simula. Simula为模拟的意思,被称为最早的面向…...
32单片机 低功耗模式
以下是一个基于STM32的低功耗模式示例代码,展示如何将STM32微控制器置于低功耗模式,并在特定条件下唤醒它。这个示例使用的是STM32 HAL库。 ### 示例代码:进入睡眠模式并使用外部中断唤醒 c #include "stm32f4xx_hal.h" // 函数声明…...
501、二叉搜索树中的众数
1、题目描述 . - 力扣(LeetCode) 要求:给一个包含重复值的BST,找出并返回BST中的众数(出现频次最高的元素)。 注:如果树中有不止一个众数可以按任意顺序返回,即如果有多个众数多个都要返回。 ps࿱…...
【洛谷】P2330 [SCOI2005] 繁忙的都市 的题解
【洛谷】P2330 [SCOI2005] 繁忙的都市 的题解 题目传送门 题解 水最小生成树,发现可以水一堆黄题qaq 这题显然就是求最大边权最小的生成树,而用 Kruskal 很容易证明这就是最小生成树,考虑一下这个算法每次取的都是不构成环的最小边即可&a…...
第18场小白入门赛(蓝桥杯)
第 18 场 小白入门赛 6 武功秘籍 考察进制理解。 对于第 i i i 位,设 b i t i x bit_ix bitix ,每一位的最大值是 b j b_j bj ,也就是说每一位是 b j 1 b_j1 bj1 进制 ,那么第 i i i 位的大小就是 x ∑ j i 1…...

Redission · 可重入锁(Reentrant Lock)
前言 Redisson是一个强大的分布式Java对象和服务库,专为简化在分布式环境中的Java开发而设计。通过Redisson,开发人员可以轻松地在分布式系统中共享数据、实现分布式锁、创建分布式对象,并处理各种分布式场景的挑战。 Redisson的设计灵感来…...

初阶C语言-指针
1.指针是什么? 理解指针的两个要点: 1.指针是内存中一个最小单元的编号,也就是地址 2.口头语中说的指针,通常是指指针变量,是用来存放内存地址的变量 总结:指针就是地址,口语中说的指针通常是指…...

论文笔记:微表情欺骗检测
整理了AAAI2018 Deception Detection in Videos 论文的阅读笔记 背景模型实验可视化 背景 欺骗在我们的日常生活中很常见。一些谎言是无害的,而另一些谎言可能会产生严重的后果。例如,在法庭上撒谎可能会影响司法公正,让有罪的被告逍遥法外。…...

智能家居有哪些产品?生活中常见的人工智能有哪些?
智能家居有哪些产品? 1、智能照明设备类:智能开关、智能插座、灯控模块、智能空开、智能灯、无线开关。 2、家庭安防类:智能门锁、智能摄像机、智能猫眼、智能门铃。 3、智能传感器类:烟雾传感器、可燃气体传感器、水浸传感器、声光报警器…...

洗车行软件系统有哪些 佳易王洗车店会员管理系统操作教程#洗车店会员软件试用版下载
一、前言 【试用版软件下载可点击本文章最下方官网卡片】 洗车行软件系统有哪些 佳易王洗车店会员管理系统操作教程#洗车店会员软件试用版下载 洗车管理软件应用是洗车业务的得力助手,实现会员管理及数据统计一体化,助力店铺高效、有序运营。 洗车项…...

【Java】springboot 项目中出现中文乱码
在刚创建的springboot项目中,出现乱码,跟走着解决一下 1、Ctrl Shift S 打开idea设置,根据图片来,将③④这三个地方都修改为UTF-8 2、返回配置查看,解决...

开放式耳机是什么意思?漏音吗?开放式的运动蓝牙耳机推荐
目前运动耳机市场主要分为入耳式、骨传导和开放式三类。入耳式耳机占比30%-40%,虽目前占比较大,但因在运动场景下有闷塞感、出汗不适、屏蔽外界环境音带来安全隐患等缺点,占比会逐渐下降。 骨传导耳机占比也为30%-40%,其不堵塞耳…...
如何优雅的处理NPE问题?
1.什么是NPE? NPE,即NullPointerException,是开发中最常见的问题之一,有必要知道如何正确地处理NPE。 对于 Java 开发者来说,null 是一个令人头疼的类型,一不小心就会发生 NPE (空指针…...

k8s 中存储之 NFS 卷
目录 1 NFS 卷的介绍 2 NFS 卷的实践操作 2.1 部署一台 NFS 共享主机 2.2 在所有k8s节点中安装nfs-utils 2.3 部署nfs卷 2.3.1 生成 pod 清单文件 2.3.2 修改 pod 清单文件增加 实现 NFS卷 挂载的 参数 2.3.3 声明签单文件并查看是否创建成功 2.3.4 在 NFS 服务器 创建默认发布…...

Redis中BitMap实现签到与统计连续签到功能
服务层代码 //签到Overridepublic Result sign() {//1.获取当前登录的用户Long userId UserHolder.getUser().getId();//获取日期LocalDateTime now LocalDateTime.now();//拼接keyString keySuffix now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String …...

【Spring】“请求“ 之传递 JSON 数据
文章目录 JSON 概念JSON 语法JSON 的语法JSON 的两种结构 JSON 字符串和 Java 对象互转JSON 优点传递 JSON 对象 JSON 概念 JSON:JavaScript Object Notation【JavaScript 对象表示法】 JSON 就是一种数据格式,有自己的格式和语法,使用文本…...

文心一言 VS 讯飞星火 VS chatgpt (359)-- 算法导论24.3 1题
一、在图 24-2上运行Dijkstra算法,第一次使用结点 s s s作为源结点,第二次使用结点 z z z作为源结点。以类似于图 24-6 的风格,给出每次while循环后的 d d d值和 π π π值,以及集合 S S S中的所有结点。如果要写代码,…...

Redis-预热雪崩击穿穿透
预热雪崩穿透击穿 缓存预热 缓存雪崩 有这两种原因 redis key 永不过期or过期时间错开redis 缓存集群实现高可用 主从哨兵Redis Cluster开启redis持久化aof,rdb,尽快恢复集群 多缓存结合预防雪崩:本地缓存 ehcache redis 缓存服务降级&…...

【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...