Linux中断

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、中断的相关概念
    • 1.中断号
    • 2.中断的申请和释放
      • 申请API函数如下:
      • 释放API函数如下:
      • 中断处理函数如下:
      • 使能和禁止中断
  • 二、上半部分和下本部分
    • 1.tasklet
    • 2.工作队列
  • 三、设备树中对于中断的描述
  • 三、获取中断号
  • 四、实例如下


前言

使用Linux内核提供的API函数,完成中断的驱动开发。

一、中断的相关概念

1.中断号

中断号的作用是用来区分不同的中断类型,数据结构是int类型。

编写驱动的时候需要用到中断号,我们用到中断号,中断信息已经写到了设备树里面,因
此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号,函数原型如下:
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
函数参数和返回值含义如下:
dev:设备节点。
index:索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
返回值:中断号。
如果使用 GPIO 的话,可以使用 gpio_to_irq 函数来获取 gpio 对应的中断号,函数原型如下:
int gpio_to_irq(unsigned int gpio)
函数参数和返回值含义如下:
gpio:要获取的 GPIO 编号。
返回值:GPIO 对应的中断号。

2.中断的申请和释放

Linux中使用中断要向内核提交申请,使用完成后要释放掉中断。

申请API函数如下:

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
函数参数和返回值含义如下:
irq:要申请中断的中断号。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志
name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev:如果将 flags 设置为 IRQF_SHARED 的话,dev 用来区分不同的中断,一般情况下将
dev 设置为设备结构体,dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
返回值:0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经
被申请了

下图提供常用的中断标志
在这里插入图片描述
注意:request_irq函数可能会导致休眠,因此不能在中断上下文或者其他禁止休眠的代码段中使用 request_irq 函数。request_irq 函数会自动激活(使能)中断,所以不需要我们手动去使能中断。

释放API函数如下:

void free_irq(unsigned int irq, void *dev)
函数参数和返回值含义如下:
irq:要释放的中断。
dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。
共享中断只有在释放最后中断处理函数的时候才会被禁止掉。
返回值:无。

注意:如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断

中断处理函数如下:

irqreturn_t (*irq_handler_t) (int, void *)
第一个参数是要中断处理函数要相应的中断号。第二个参数是一个指向 void 的指针,也就
是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备,
dev 也可以指向设备数据结构。
中断处理函数的返回值为 irqreturn_t 类型。其定义如下:
10 enum irqreturn {
11 IRQ_NONE = (0 << 0),
12 IRQ_HANDLED = (1 << 0),
13 IRQ_WAKE_THREAD = (1 << 1),
14 };
15
16 typedef enum irqreturn irqreturn_t;
可以看出 irqreturn_t 是个枚举类型,一共有三种返回值
一般中断服务函数返回值使用如下形式:
return IRQ_RETVAL(IRQ_HANDLED)

使能和禁止中断

void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)

disable_irq函数要等到当前正在执行的中断处理函数执行完才返回,因此使用者需要保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出。那如何处理上述情况的使用如下函数:

void disable_irq_nosync(unsigned int irq)
disable_irq_nosync 函数调用以后立即返回,不会等待当前中断处理程序执行完毕。

上述三个函数是使能或者禁止某一中断,有时候需要使能和关闭整个系统的中断,使用如下函数:

local_irq_enable()
local_irq_disable()

在使用全局中断的时候有个问题,比如当程序A执行关闭10S中断,当2秒后程序B也执行了关闭全局中断,但过了3秒后,B程序又启用了全局中断,但这时候程序A关闭10S还没有结束,这种情况严重会导致系统崩溃。那该怎么解决呢,Linux中提供如下函数解决整个问题

local_irq_save(flags)
local_irq_restore(flags)

这两个函数是成对出现的,local_irq_save 函数用于禁止中断,并且将中断状态保存在 flags 中。local_irq_restore 用于恢复中断,将中断到 flags 状态。

二、上半部分和下本部分

当发生中断的时候,会进入中断处理函数,在函数中,我们希望时间越小越好,但往往事与愿违,在时间驱动编写的时候,在中断处理函数中,需要花费的时间不小,比如电容触摸屏通过中断通知 SOC 有触摸事件发生,SOC 响应中断,然后通过 IIC 接口读取触摸坐标值并将其上报给系统。但是我们都知道 IIC 的速度最高也只有400Kbit/S,所以在中断中通过 IIC 读取数据就会浪费时间。我们可以将通过 IIC 读取触摸数据的操作暂后执行,中断处理函数仅仅响应中断,然后清除中断标志位即可,这种方式就是Linux处理中断的过程:
上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可
以放在上半部完成。
下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部
去执行,这样中断处理函数就会快进快出。
比如在上半部将数据拷贝到内存中,关于数据的具体处理就可以放到下半部去执行。至于哪些代码属于上半部,哪些代码属于下半部并没有明确的规定,当然了也是有些经验可以参考的:
①、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
②、如果要处理的任务对时间敏感,可以放到上半部。
③、如果要处理的任务与硬件有关,可以放到上半部
④、除了上述三点以外的其他任务,优先考虑放到下半部。
那如何将上半部分和下半部分连接起来呢,Linux提供许多方法,可以百度查看一下,下面提供两种常用的方式

1.tasklet

Linux 内核使用 tasklet_struct 结构体来表示 tasklet:

484 struct tasklet_struct
485 {
486 struct tasklet_struct *next; /* 下一个 tasklet */
487 unsigned long state; /* tasklet 状态 */
488 atomic_t count; /* 计数器,记录对 tasklet 的引用数 */
489 void (*func)(unsigned long); /* tasklet 执行的函数 */
490 unsigned long data; /* 函数 func 的参数 */
491 };

第 489 行的 func 函数就是 tasklet 要执行的处理函数,用户定义函数内容,相当于中断处理
函数。如果要使用 tasklet,必须先定义一个 tasklet,然后使用 tasklet_init 函数初始化 tasklet,taskled_init 函数原型如下:

void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), 
unsigned long data);
函数参数和返回值含义如下:
t:要初始化的 tasklet
func:tasklet 的处理函数。
data:要传递给 func 函数的参数
返回值:没有返回值也可以使用宏 DECLARE_TASKLET 来一次性完成 tasklet 的定义和初始化,
DECLARE_TASKLET 定义在 include/linux/interrupt.h 文件中,定义如下:
DECLARE_TASKLET(name, func, data)
其中 name 为要定义的 tasklet 名字,这个名字就是一个 tasklet_struct 类型的时候变量,func
就是 tasklet 的处理函数,data 是传递给 func 函数的参数。

在上半部,也就是中断处理函数中调用 tasklet_schedule 函数就能使 tasklet 在合适的时间运行,tasklet_schedule 函数原型如下:

void tasklet_schedule(struct tasklet_struct *t)
函数参数和返回值含义如下:
t:要调度的 tasklet,也就是 DECLARE_TASKLET 宏里面的 name。
返回值:没有返回值

使用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);......
}

2.工作队列

工作队列是另外一种下半部执行方式,工作队列在进程上下文执行,工作队列将要推后的
工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重新调度。因此如果你要推后的工作可以睡眠那么就可以选择工作队列,否则的话就只能选择软中断或 tasklet。
简单创建工作很简单,直接定义一个 work_struct 结构体变量即可,然后使用 INIT_WORK 宏来初始化工作,INIT_WORK 宏定义如下:

#define INIT_WORK(_work, _func)
_work 表示要初始化的工作,_func 是工作对应的处理函数。
也可以使用 DECLARE_WORK 宏一次性完成工作的创建和初始化,宏定义如下:
#define DECLARE_WORK(n, f)
n 表示定义的工作(work_struct),f 表示工作对应的处理函数。
和 tasklet 一样,工作也是需要调度才能运行的,工作的调度函数为 schedule_work,函数原
型如下所示:
bool schedule_work(struct work_struct *work)
函数参数和返回值含义如下:
work:要调度的工作。
返回值:0 成功,其他值 失败。

格式如下:

/* 定义工作(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);......
}

三、设备树中对于中断的描述

如果使用设备树的话就需要在设备树中设置好中断属性信息,Linux 内核通过读取设备树中的中断属性信息来配置中断。
在imx6ull中描述中断控制器的节点在imx6ull.dtsi文件中

intc: interrupt-controller@00a01000 {compatible = "arm,cortex-a7-gic";#interrupt-cells = <3>;interrupt-controller;reg = <0x00a01000 0x1000>,<0x00a02000 0x100>;};

compatible 属性值为“arm,cortex-a7-gic”在 Linux 内核源码中搜索“arm,cortex-a7-gic”即可找到中断控制器驱动文件。
#interrupt-cells属性,描述了cells大小,可以在绑定文档中查看其含义:
在这里插入图片描述
第一个 cells:中断类型,0 表示 SPI 中断,1 表示 PPI 中断。
第二个 cells:中断号,对于 SPI 中断来说中断号的范围为 0~987,对于 PPI 中断来说中断
号的范围为 0~15。
第三个 cells:标志,bit[3:0]表示中断触发类型,为 1 的时候表示上升沿触发,为 2 的时候
表示下降沿触发,为 4 的时候表示高电平触发,为 8 的时候表示低电平触发。bit[15:8]为 PPI 中断的 CPU 掩码。
interrupt-controller 节点为空,表示当前节点是中断控制器

对于 gpio 来说,gpio 节点也可以作为中断控制器,比如 imx6ull.dtsi 文件中的 gpio5 节点内
容如下所示:

	gpio5: gpio@020ac000 {compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";reg = <0x020ac000 0x4000>;interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;gpio-controller;#gpio-cells = <2>;interrupt-controller;#interrupt-cells = <2>;};

interrupts描述中断源信息,属性值大小按照intc中设置填写,对于 gpio5 来说一共有两条信息,中断类型都是 SPI,触发电平都是 IRQ_TYPE_LEVEL_HIGH。不同之处在于中断源,一个是 74,一个是 75,打开可以打开《IMX6ULL 参考手册》的“Chapter 3 Interrupts and DMA Events”章节,找到表 3-1:
在这里插入图片描述
从上图中可以看出,GPIO5 一共用了 2 个中断号,一个是 74,一个是 75。其中 74 对应 GPIO5_IO00-GPIO5_IO15 这低 16 个 IO,75 对应 GPIO5_IO16~GPIOI5_IO31 这高 16 位 IO。
interrupt-controller 表明了 gpio5 节点也是个中断控制器,用于控制 gpio5 所有 IO的中断。
将#interrupt-cells 修改为 2,为什么是2呢,查看绑定信息
在这里插入图片描述
此绑定信息在目录\Documentation\devicetree\bindings\gpio
在这里插入图片描述
上边是NXP官方写的设备树,如果用户要使用该如何使用呢,下面举个例子:

1 fxls8471@1e {
2 compatible = "fsl,fxls8471";
3 reg = <0x1e>;
4 position = <0>;
5 interrupt-parent = <&gpio5>;
6 interrupts = <0 8>;
7 };
fxls8471 是 NXP 官方的 6ULL 开发板上的一个磁力计芯片,fxls8471 有一个中断引脚链接
到了 I.MX6ULL 的 SNVS_TAMPER0 因脚上,这个引脚可以复用为 GPIO5_IO00。
第 5 行,interrupt-parent 属性设置中断控制器,这里使用 gpio5 作为中断控制器。
第 6 行,interrupts 设置中断信息,0 表示 GPIO5_IO00,8 表示低电平触发。
简单总结一下与中断有关的设备树属性信息:
①、#interrupt-cells,指定中断源的信息 cells 个数。
②、interrupt-controller,表示当前节点为中断控制器。
③、interrupts,指定中断号,触发方式等。
④、interrupt-parent,指定父中断,也就是中断控制器

三、获取中断号

编写驱动的时候需要用到中断号,我们用到中断号,中断信息已经写到了设备树里面,因
此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号,函数原型如下:

unsigned int irq_of_parse_and_map(struct device_node *dev,int index)
函数参数和返回值含义如下:
dev:设备节点。
index:索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
返回值:中断号。
如果使用 GPIO 的话,可以使用 gpio_to_irq 函数来获取 gpio 对应的中断号,函数原型如
下:
int gpio_to_irq(unsigned int gpio)
函数参数和返回值含义如下:
gpio:要获取的 GPIO 编号。
返回值:GPIO 对应的中断号。

四、实例如下

#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/export.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>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/sched.h>
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名		: imx6uirq.c
作者	  	: 车文超
版本	   	: V1.0
描述	   	: Linux中断驱动实验
其他	   	: 无
论坛 	   	: 
日志	   	: 
***************************************************************/
#define IMX6ULL_CNT		1			    /* 设备号个数 	*/
#define IMX6ULL_NAME		"imx6ull"	/* 名字 		*/#define KEY0VALUE 0X01 /* KEY0 按键值 */
#define INVAKEY 0XFF /* 无效的按键值 */
#define KEY_NUM 1 /* 按键数量 */
/*按键设备*/
struct key_dev{int gpioid;		            /*gpio编号*/unsigned char value;		/*按键保存值*/char name[10];				/*按键名字*/int irqnum;				/*中断号*/irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
};/* imx6uirq设备结构体 */
struct imx6ull_dev{dev_t devid;			/* 设备号 	 */struct cdev cdev;		/* cdev 	*/struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */int major;				/* 主设备号	  */int minor;				/* 次设备号   */struct device_node	*nd; /* 设备节点 */struct key_dev key0dev;	/*按键设备*/struct timer_list timerdev;/*定时器设备*/atomic_t keyvalue;/*按键值*/atomic_t release;/*按键释放值*/};struct imx6ull_dev imx6ulldev;	/* imx6ulldev */
/** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int imx6ull_open(struct inode *inode, struct file *filp)
{filp->private_data = &imx6ulldev;	/* 设置私有数据 */return 0;
}/** @description     : 从设备读取数据 * @param - filp    : 要打开的设备文件(文件描述符)* @param - buf     : 返回给用户空间的数据缓冲区* @param - cnt     : 要读取的数据长度* @param - offt    : 相对于文件首地址的偏移* @return          : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t imx6ull_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{struct imx6ull_dev *dev = (struct imx6ull_dev *)filp->private_data;int ret = 0;int value = 0;int release = 0;value = atomic_read(&dev->keyvalue);release = atomic_read(&dev->release);if(release ==1){if(value & 0x80){value &= ~0x80;ret = copy_to_user(buf,&value,sizeof(value));}else{goto data_error;}atomic_set(&dev->release,0);}else{return -EINVAL;}return ret;
data_error:return -EINVAL;
}/* 设备操作函数 */
static const struct file_operations imx6ull_fops = {.owner = THIS_MODULE,.open = imx6ull_open,.read = imx6ull_read,
};
/** @description	: 中断处理函数* @param 		: 无* @return 		: 无*/
static irqreturn_t key0_irq(int irq, void *dev_id){struct imx6ull_dev *dev = (struct imx6ull_dev *)dev_id;
#if 0value = gpio_get_value(dev->key0dev.gpioid);printk("value=%d\n\r", value);if(value == 0){printk("key0按下");}if(value ==1){printk("key0抬起");}
#endifdev->timerdev.data = (unsigned long)dev_id;mod_timer(&dev->timerdev, jiffies + msecs_to_jiffies(10));return 0;
}
/** @description	: 定时器中断函数* @param 		: 无* @return 		: 无*/
static void timer_func(unsigned long data)
{int value = 0;struct imx6ull_dev *dev = (struct imx6ull_dev *)data;value = gpio_get_value(dev->key0dev.gpioid);printk("value=%d\n\r", value);if(value == 0){printk("key0按下");atomic_set(&dev->keyvalue, dev->key0dev.value);}if(value ==1){printk("key0抬起");atomic_set(&dev->keyvalue, (dev->key0dev.value)|0x80);atomic_set(&dev->release,1);}}/** @description	: 按键初始化函数* @param 		: 无* @return 		: 无*/
static int key_myinit(struct imx6ull_dev *dev_id){int ret = 0	;struct imx6ull_dev *dev = (struct imx6ull_dev *)dev_id;/*1.获取节点*/dev->nd = of_find_node_by_path("/key");if(dev->nd == NULL){printk("of_find_node_by_path_erro\n\r");return -1;}/*2.获取GPIO编号*/dev->key0dev.gpioid = of_get_named_gpio(dev->nd, "key-gpios", 0);if(dev->key0dev.gpioid < 0){printk("of_get_named_gpio_erro\n\r");return -1;}/*3.申请IO*/memset(dev->key0dev.name, 0, sizeof(dev->key0dev.name));//sprintf(dev->keydev[i].name,"KEY%d",i);sprintf(dev->key0dev.name,"key%d",0);ret = gpio_request(dev->key0dev.gpioid,dev->key0dev.name);if(ret < 0){printk("gpio_request_erro\n\r");return -1;}gpio_direction_input(dev->key0dev.gpioid);/*4中断号获取*///dev->keydev[i].irqnum = gpio_to_irq(dev->keydev[i].gpioid);dev->key0dev.irqnum = irq_of_parse_and_map(dev->nd,0);printk("dev->key0dev.irqnum=%d\n\r",dev->key0dev.irqnum);dev->key0dev.handler = key0_irq;dev->key0dev.value = KEY0VALUE;/*5.注册中断*/ret = request_irq(dev->key0dev.irqnum, dev->key0dev.handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,dev->key0dev.name, &imx6ulldev);printk("ret=%d\n\r",ret);if(ret){printk("request_irq_erro\n\r");//goto file_irq;return -1;}/*初始化定时器*/init_timer(&imx6ulldev.timerdev);imx6ulldev.timerdev.function = timer_func;//file_irq:// free_irq(dev->key0dev.irqnum, &imx6ulldev);
return ret;
}
/** @description	: 驱动入口函数* @param 		: 无* @return 		: 无*/
static int __init imx6ull_init(void)
{/* 1、构建设备号 */if (imx6ulldev.major) {imx6ulldev.devid = MKDEV(imx6ulldev.major, 0);register_chrdev_region(imx6ulldev.devid, IMX6ULL_CNT, IMX6ULL_NAME);} else {alloc_chrdev_region(&imx6ulldev.devid, 0, IMX6ULL_CNT, IMX6ULL_NAME);imx6ulldev.major = MAJOR(imx6ulldev.devid);imx6ulldev.minor = MINOR(imx6ulldev.devid);}/* 2、注册字符设备 */cdev_init(&imx6ulldev.cdev, &imx6ull_fops);cdev_add(&imx6ulldev.cdev, imx6ulldev.devid, IMX6ULL_CNT);/* 3、创建类 */imx6ulldev.class = class_create(THIS_MODULE, IMX6ULL_NAME);if (IS_ERR(imx6ulldev.class)) {return PTR_ERR(imx6ulldev.class);}/* 4、创建设备 */imx6ulldev.device = device_create(imx6ulldev.class, NULL, imx6ulldev.devid, NULL, IMX6ULL_NAME);if (IS_ERR(imx6ulldev.device)) {return PTR_ERR(imx6ulldev.device);}/*key初始化*/key_myinit(&imx6ulldev);atomic_set(&imx6ulldev.keyvalue, INVAKEY);atomic_set(&imx6ulldev.release, 0);return 0;
}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static void __exit imx6ull_exit(void)
{/*释放中断号,释放IO*/free_irq(imx6ulldev.key0dev.irqnum, &imx6ulldev);gpio_free(imx6ulldev.key0dev.gpioid);/*删除定时器*/del_timer_sync(&imx6ulldev.timerdev);/*删除设备 *//*删除字符设备 */cdev_del(&imx6ulldev.cdev);/*删除设备号 */unregister_chrdev_region(imx6ulldev.devid, IMX6ULL_CNT);/*删除设备 */device_destroy(imx6ulldev.class, imx6ulldev.devid);/*删除类 */class_destroy(imx6ulldev.class);
}module_init(imx6ull_init);
module_exit(imx6ull_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("chewenchao");

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac安装tomcat

代码 brew install tomcat 运行结果如下&#xff1a; 如果要启动输入&#xff1a; brew services start tomcat

Linux学习笔记(epoll,IO多路复用)

Linux learning note 1、epoll的使用场景2、epoll的使用方法和内部原理2.1、创建epoll2.2、使用epoll监听和处理事件 3、示例 1、epoll的使用场景 epoll的英文全称是extend poll&#xff0c;顾名思义是poll的升级版。常见的IO复用技术有select&#xff0c;poll&#xff0c;epo…

连锁收银系统支持带结算功能

连锁实体店的收银系统需要支持结算功能&#xff0c;以适应连锁运营效率和提升连锁管理的水平。商淘云连锁收银系统与您一起分享连锁收银系统需支持结算功能的三大必要点。大家点赞收藏&#xff0c;以免划走后找不到了。 一是&#xff0c;连锁模式的运营比较复杂&#xff0c;有加…

同时执行多个python脚本扫描,报如下错误,原因为文件越大读取到内存占用内存越多。

killed nohup python $file unable to fork process cannot allocate memory ls: error while loading shared libraries: libdl.so.2 failed to map segment from shared object cannot allocate memory python进程被系统或者某个用户通过 kill 命令强制终止了

Iphone自动化指令每隔固定天数打开闹钟关闭闹钟(二)

1.首先在搜索和操作里搜索“查找日期日程" 1.1.然后过滤条件开始日期选择”是今天“ 1.2.增加过滤条件&#xff0c;日历是这里选择”工作“ 1.3.增加过滤条件&#xff0c;选择标题&#xff0c;是这里选择”workDay“ 1.4选中限制&#xff0c;日历日程只要一个&#xff0c;…

Vue从入门到实战Day07

一、vuex概述 目标&#xff1a;明确vuex是什么&#xff0c;应用场景&#xff0c;优势 1. 是什么&#xff1a; vuex是一个vue的状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;vuex是一个插件&#xff0c;可以帮助我们管理vue通用的数据&#xff08;多组件共享的…

九章云极DataCanvas公司DingoDB完成中国信通院权威多模数据库测试

2024年5月16日&#xff0c;九章云极DataCanvas公司自主研发和设计的开源多模向量数据库DingoDB顺利完成中国信息通信研究院&#xff08;以下简称中国信通院&#xff09;多模数据库产品测试。本次测试的成功标志着DingoDB在技术能力、性能表现和产品稳定性方面得到了权威机构的高…

go语言的使用方法

一.go语言的介绍 1.简介 2.应用领域 3.使用go语言的公司 4.go语言开发工具介绍 5.go语言开发环境搭建 【1】搭建Go开发环境-安装和配置SDK 基本介绍: 1).SDK的全称(Software Development Kit软件开发工具包&#xff09;2).SDK是提供给开发人员使用的&#xff0c;其中包含了…

蓝海卓越计费管理系统 agent_setstate.php SQL注入漏洞复现

0x01 产品简介 蓝海卓越计费管理系统是一套以实现网络运营为基础,增强全局安全为中心,提高管理效率为目的的网络安全运营管理系统,提供“高安全、可运营、易管理”的运营管理体验,基于标准的RADIUS协议开发,它不仅支持PPPOE和WEB认证计费,还支持802.1X接入控制技术,与其…

详解 HTML5 服务器发送事件(Server-Sent Events)

HTML5 服务器发送事件&#xff08;server-sent event&#xff09;允许网页获得来自服务器的更新。 EventSource 是单向通信的&#xff08;是服务器向客户端的单向通信&#xff0c;客户端接收来自服务器的事件流&#xff09;、基于 HTTP 协议&#xff08;EventSource 是基于标准…

5.29_Java程序流程控制

CSDN 同C语言的流程同 1、补充&#xff1a; 1、switch使用时的注意事项 1、表达式只能是byte、short、int、char&#xff0c;JDK5开始支持枚举&#xff0c;JDK7开始支持String、不支持double、float、long switch里面是做分支匹配&#xff0c;也就是可以出现很多分支&am…

JavaScript-JavaWeb

目录 什么是JavaScript? js引入方式 js基础语法 书写语法 变量 数据据类型 运算符 类型转换 流程语句 js函数 js对象 1.Array 2.String 3.JSON js事件监听 什么是JavaScript? ● JavaScript(简称:JS)是一门跨平台、面向对象的脚本语言。是用来控制网页行为的,它能…

前缀和(下)

目录 热身&#xff1a; 寻找数组的中心下标 题解&#xff1a; 代码&#xff1a; 进阶&#xff1a; 除自身之外数组的乘积 题解&#xff1a; 代码&#xff1a; 和为K的子数组 题解&#xff1a; 代码&#xff1a; 和可被 K 整除的子数组 题解&#xff1a; 同余定理…

mysql - 索引原理

mysql索引原理 文中的查询, 以该表结构为例 CREATE TABLE user (id int NOT NULL COMMENT id,name varchar(255) COLLATE utf8mb4_bin NOT NULL COMMENT 姓名,age int NOT NULL COMMENT 年龄,sex tinyint(1) NOT NULL COMMENT 性别,phone varchar(255) CHARACTER SET utf8mb4…

esp32-idf 开发踩坑记录

现象 直接使用原始命令编译idf.py build 但是提示idf 版本错误 卸载旧版本 编译出错build 问题 然后删除编译文件后&#xff0c;重新编译&#xff0c;还是出错 解决方法1 最后发现是因为项目所在文件夹有中文目录&#xff0c;把项目迁移到英文目录后&#xff0c;重新编译&a…

⌈ 传知代码 ⌋ YOLOv9最新最全代码复现

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

【WEB前端2024】开源智体世界:乔布斯3D纪念馆-第29课-会员制展厅

【WEB前端2024】开源智体世界&#xff1a;乔布斯3D纪念馆-第29课-会员制展厅 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

【ESP32之旅】ESP32 PlatformIO 固件单独烧录

背景 有时候使用PIO编写的代码需要发给客户去验证&#xff0c;相比较于发送源码直接发送bin文件&#xff0c;更加的安全而且高效。不用担心源码的泄漏&#xff0c;也不用帮客户配置PIO环境。 操作方法 1.编译 首先进行代码编译&#xff0c;如编译成功会在 .pio\build\airm2…

光耦的工作原理

一、光电耦合器简介 光电耦合器主要是一种围绕光作为媒介的光电转换元器件&#xff0c;能够实现光到电、电到光之间的自由转换。我们又可以称之为光电隔离器&#xff0c;之所以这么称呼&#xff0c;主要是因为光电耦合器能够很好的对电路中的电信号起到隔离的作用。有效的保护…

基于java实现图片中任意封闭区域识别

需求&#xff1a; 在浏览器中给用户呈现一张图片&#xff0c;用户点击图片中的某些标志物&#xff0c;需要系统给出标志物的信息反馈&#xff0c;达到一个交互的作用。 比如下图中&#xff0c;点击某个封闭区域时候&#xff0c;需要告知用户点击的区域名称及图形形状特性等等。…