MX6ULL学习笔记 (七) 中断实验

前言:     

   本章我们就来学习一 下如何在 Linux 下使用中断。在linux内核里面使用中断,不同于我们以往在别的裸机开发一样,需要进行各种寄存器的配置,中断使能之类的,而在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);

参数分析:

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

 顺便介绍几个常用的中断标志,如表所示:

3.free_irq 函数:

函数原型:

void free_irq(unsigned int irq, void *dev);

函数参数和返回值含义如下:

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

4.中断处理函数:

使用 request_irq 函数申请中断的时候需要设置中断处理函数,中断处理函数格式如下所示:

irqreturn_t (*irq_handler_t) (int, void *);
  • 第一个参数是要中断处理函数要相应的中断号。
  • 第二个参数是一个指向 void 的指针,也就是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备, dev 也可以指向设备数据结构。
  • 中断处理函数的返回值为 irqreturn_t 类型


二、了解中断初始化驱动的步骤。

1 .修改设备树

这里是基于之前按键的程序修改的,也就是在之前设备树的基础上修改就好。这里是在根节点下添加的。

key {#address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-key"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_key>; key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */ status = "okay"; }; 

 在上面基础上添加如下:

(1)设置 interrupt-parent 属性值,例如按键的中断,设置属性值为”gpio1,因为 KEY0 所使用的 GPIO GPIO1_IO18,也就是设置 KEY0 GPIO 中断控制器为 gpio1

(2)设置 interrupts 属性,也就是设置中断源。

2.编写字符型驱动框架。

  1. (1)直接选取之前的字符设备驱动框架,在那份代码的基础上来进行修改。

3.定义按键信息结构体

  1. (1)首先定义一个结构体,来保存有关key中断的一些信息。
/* 中断IO描述结构体 */
struct irq_keydesc {int gpio;								/* gpio *int irqnum;								/* 中断号     */unsigned char value;					/* 按键对应的键值 */char name[10];							/* 名字 */irqreturn_t (*handler)(int, void *);	/* 中断服务函数 */
};

  1. 4.编写按键中断初始化程序。

  2. 步骤如下:
  • ①使用of_find_node_by_path函数通过环境名获取节点。
  • ②使用of_get_named_gpio函数通过节点得到gpio的name编号。
  • ③使用gpio_request函数通过gpio的name编号去注册gpio
  • ④使用gpio_direction_input函数去设置gpio的方向,是输入还是输出啊。
  • ⑤使用irq_of_parse_and_map函数从 interupts 属性中提取到对应的中断号
  • ⑥使用request_irq函数去申请注册中断。同时指定我们的中断处理函数。
  • int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);
    
  • ⑦编写中断处理函数,函数原型:
  • static irqreturn_t key0_handler(int irq, void *dev_id);

5.编写按键释放程序

①使用free_irq函数去释放中断。


三、驱动代码的编写。

1.修改设备树

key {#address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-key"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_key>; key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */ interrupt-parent = <&gpio1>; interrupts = <18 IRQ_TYPE_EDGE_BOTH>; /* FALLING RISING */ status = "okay"; }; 

2.编写字符型驱动框架。

        这里就直接使用之前的字符设备驱动的框架就可以了,把那些函数名,还有结构体名字之类的改一改就好,后面也会贴出完整的代码。

3.首先定义一个结构体,来保存有关key中断的一些信息。

struct irq_keydesc
{int gpio_num;                           /* gpio号 	 */  	int irq_num;							/*  中断号    */char gpio_name[10];						/* gpio名字   */unsigned char key_value;				/*按键对应键值*/irqreturn_t (*handle)(int ,void *);		/*终端服务函数  */
};

  1. 4.编写按键中断初始化程序。

  2. 步骤如下:
  • ①使用of_find_node_by_path函数通过环境名获取节点。
  • 	/*使用of_find_node_by_path函数通过环境名获取节点。*/dev->nd = of_find_node_by_path("/key");if(dev->nd == NULL){printk("key node not find!\r\n");return -EINVAL;}

  • ②使用of_get_named_gpio函数通过节点得到gpio的name编号。
  • 	/*使用of_get_named_gpio函数通过节点得到gpio的name编号。*/dev->irqkeydesc[0].gpio_num = of_get_named_gpio(dev->nd,"gpios",0);if(dev->irqkeydesc[0].gpio_num < 0){printk("can't get key0\r\n");}

  • ③使用gpio_request函数通过gpio的name编号去注册gpio
  • 	/*使用gpio_request函数通过gpio的name编号,以及设置中断名去注册gpio*/memset(dev->irqkeydesc[0].gpio_name,0,sizeof(dev->irqkeydesc[0].gpio_name));sprintf(dev->irqkeydesc[0].gpio_name, "KEY0");gpio_request(dev->irqkeydesc[0].gpio_num,dev->irqkeydesc[0].gpio_name);

  • ④使用gpio_direction_input函数去设置gpio的方向,是输入还是输出啊。
  • 	/*使用gpio_direction_input函数去设置gpio的方向,是输入还是输出啊。*/gpio_direction_input(dev->irqkeydesc[0].gpio_num);

  • ⑤使用irq_of_parse_and_map函数从 interupts 属性中提取到对应的中断号。
  • 	/*使用irq_of_parse_and_map函数从 interupts 属性中提取到对应的中断号*/dev->irqkeydesc[0].irq_num=irq_of_parse_and_map(dev->nd,0);printk("key0:gpio=%d, irqnum=%d\r\n", dev->irqkeydesc[0].gpio_num, dev->irqkeydesc[0].irq_num);

  • ⑥使用request_irq函数去申请注册中断。同时指定我们的中断处理函数。
  • 	/*使用request_irq函数去申请注册中断。同时指定我们的中断处理函数。*/dev->irqkeydesc[0].handle    = key0_handler;dev->irqkeydesc[0].key_value = KEY0_VALUE;ret = request_irq(dev->irqkeydesc[0].irq_num,dev->irqkeydesc[0].handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,dev->irqkeydesc[0].gpio_name, &imx6uirq);if(ret < 0){printk("irq %d request failed!\r\n", dev->irqkeydesc[0].irq_num);return -EFAULT;}

5.编写中断释放程序

①使用free_irq函数去释放中断。

	free_irq(imx6uirq.irqkeydesc[0].irq_num,&imx6uirq);gpio_free(imx6uirq.irqkeydesc[0].gpio_num);

 

6.完整驱动代码:

/**************头文件区域*********************************************************/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/major.h>
#include <linux/gfp.h>#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/device.h>#include <linux/of_gpio.h>
#include <linux/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>
/**********************************************************************************//************************宏定义-begin***********************************************/
#define DEVICE_CNT			1		  	/* 设备号个数 */
#define DEVICE_NAME			"imx6uirq"	/* 名字 */
#define KEY_CNT				1	        /* 按键数量*/
#define KEY0_VALUE          0X01		/*按键0对应键值*/
#define INVAKEY_VALUE       0XFF		/*无按键对应键值*/
/************************宏定义-end********************************************//************************结构体定义-begin***********************************************/struct irq_keydesc
{int gpio_num;                           /* gpio号 	 */  	int irq_num;							/*  中断号    */char gpio_name[10];						/* gpio名字   */unsigned char key_value;				/*按键对应键值*/irqreturn_t (*handle)(int ,void *);		/*终端服务函数  */
};/* imx6uirql设备信息结构体 */
struct char_dev{dev_t devid;							/* 设备号 	 */struct cdev cdev;						/* cdev 	*/struct class *class;					/* 类 		*/struct device *device;					/* 设备 	*/int major;								/* 主设备号	 */int minor;								/* 次设备号  */struct device_node *nd; 				/* 设备节点  */struct irq_keydesc irqkeydesc[KEY_CNT];	/*按键信息结构体数组*/struct timer_list timer;				/*timer结构体*/atomic_t keyvalue;      				/* 有效的按键键值 */atomic_t releasekey;    				/* 标记是否完成一次完成的按键,包括按下和释放 */
};
struct char_dev imx6uirq;
/************************结构体定义-end***********************************************//*----------------------------------------------------------------------------------------------------*/
/************************按键中断服务函数***********************************************/
static irqreturn_t key0_handler(int irq, void *dev_id)
{struct char_dev *dev = (struct char_dev *)dev_id;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer,jiffies + msecs_to_jiffies(10));return IRQ_RETVAL(IRQ_HANDLED);}/************************定时器中断服务函数***********************************************/
void timer_function(unsigned long arg)
{unsigned char value;struct char_dev *dev=(struct char_dev *)arg;struct irq_keydesc*keydesc;keydesc = &dev->irqkeydesc[0];value=gpio_get_value(keydesc->gpio_num);if(value == 0){atomic_set(&dev->keyvalue,keydesc->key_value);}else{atomic_set(&dev->keyvalue,0x80 | keydesc->key_value);atomic_set(&dev->releasekey, 1);	/* 标记松开按键,即完成一次完整的按键过程 */			}
}/*************按键gpio初始化函数**************************/
static int keyio_init(struct char_dev *dev)
{int ret;/*使用of_find_node_by_path函数通过环境名获取节点。*/dev->nd = of_find_node_by_path("/key");if(dev->nd == NULL){printk("key node not find!\r\n");return -EINVAL;}/*使用of_get_named_gpio函数通过节点得到gpio的name编号。*/dev->irqkeydesc[0].gpio_num = of_get_named_gpio(dev->nd,"gpios",0);if(dev->irqkeydesc[0].gpio_num < 0){printk("can't get key0\r\n");}/*使用gpio_request函数通过gpio的name编号,以及设置中断名去注册gpio*/memset(dev->irqkeydesc[0].gpio_name,0,sizeof(dev->irqkeydesc[0].gpio_name));sprintf(dev->irqkeydesc[0].gpio_name, "KEY0");gpio_request(dev->irqkeydesc[0].gpio_num,dev->irqkeydesc[0].gpio_name);/*使用gpio_direction_input函数去设置gpio的方向,是输入还是输出啊。*/gpio_direction_input(dev->irqkeydesc[0].gpio_num);/*使用irq_of_parse_and_map函数从 interupts 属性中提取到对应的中断号*/dev->irqkeydesc[0].irq_num=irq_of_parse_and_map(dev->nd,0);printk("key0:gpio=%d, irqnum=%d\r\n", dev->irqkeydesc[0].gpio_num, dev->irqkeydesc[0].irq_num);/*使用request_irq函数去申请注册中断。同时指定我们的中断处理函数。*/dev->irqkeydesc[0].handle    = key0_handler;dev->irqkeydesc[0].key_value = KEY0_VALUE;ret = request_irq(dev->irqkeydesc[0].irq_num,dev->irqkeydesc[0].handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,dev->irqkeydesc[0].gpio_name, &imx6uirq);if(ret < 0){printk("irq %d request failed!\r\n", dev->irqkeydesc[0].irq_num);return -EFAULT;}return 0;
}/*************定时器初始化函数********************************************/
void mytimer_init(struct char_dev *dev)
{init_timer(&dev->timer);dev->timer.function = timer_function;
}/*****实现对应的open/read/write等函数,填入file_operations结构体************//******************实现对应的read函数************************************/
static ssize_t imx6uirq_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct char_dev *dev = (struct char_dev *)file->private_data;keyvalue   = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if(releasekey){if(keyvalue & 0x80){keyvalue &= ~0x80;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;}/******************实现对应的write函数************************************/
static ssize_t imx6uirq_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{return 0;
}/******************实现对应的open函数************************************/
static int imx6uirq_drv_open (struct inode *node, struct file *file)
{file->private_data = &imx6uirq;	/* 设置私有数据 */return 0;
}/******************实现对应的close函数************************************/
static int imx6uirq_drv_close (struct inode *node, struct file *file)
{return 0;
}/********************定义自己的file_operations结构体****************************/
static struct file_operations imx6uirq_drv = {.owner	 = THIS_MODULE,.open    = imx6uirq_drv_open,.read    = imx6uirq_drv_read,.write   = imx6uirq_drv_write,.release = imx6uirq_drv_close,
};/**************************注册驱动程序******************************************/
/* 把file_operations结构体告诉内核:注册驱动程序                        */
/* 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init imx6uirq_init(void)
{/* 动态注册字符设备的流程一般如下:1.调用 alloc_chrdev_region() 函数申请设备编号。2.使用 cdev_init() 函数初始化设备描述结构体。3.使用 cdev_add() 函数将设备号与设备描述结构体关联起来,注册字符设备驱动。4.使用 class_create() 函数创建一个设备类.5.使用 device_create() 函数创建一个设备*/printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*1 创建设备号根据是否定义了设备号,通过条件判断选择不同的创建方式。如果定义了设备号,则使用MKDEV宏将主设备号和次设备号合成为设备号,并调用register_chrdev_region()函数注册字符设备号。如果没有定义设备号,则使用alloc_chrdev_region()函数动态分配设备号,并通过MAJOR和MINOR宏获取分配得到的主设备号和次设备号。*/if (imx6uirq.major) {		/*  定义了设备号 */imx6uirq.devid = MKDEV(imx6uirq.major, 0);/*register_chrdev_region() 是 Linux 内核中用于向系统申请指定范围内的字符设备编号的函数*/register_chrdev_region(imx6uirq.devid, DEVICE_CNT, DEVICE_NAME);} else {						/* 没有定义设备号 */alloc_chrdev_region(&imx6uirq.devid, 0, DEVICE_CNT, DEVICE_NAME);	/* 申请设备号 */imx6uirq.major = MAJOR(imx6uirq.devid);	/* 获取分配号的主设备号 */imx6uirq.minor = MINOR(imx6uirq.devid);	/* 获取分配号的次设备号 */}/* 2 初始化cdev设置cdev结构体的拥有者为当前模块(THIS_MODULE),然后使用 cdev_init() 函数初始化cdev结构体。参数包括待初始化的cdev结构体和用于操作该设备的file_operations结构体(imx6uirq_drv) */imx6uirq.cdev.owner = THIS_MODULE;cdev_init(&imx6uirq.cdev, &imx6uirq_drv);/* 3、添加一个cdev */cdev_add(&imx6uirq.cdev, imx6uirq.devid, DEVICE_CNT);/*4 创建设备类使用 class_create() 函数创建一个设备类,设备类用于在/sys/class目录下创建子目录,以组织同一类设备的相关信息。该函数的参数包括所属的模块(THIS_MODULE)和设备类的名称(imx6uirq_NAME)。如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */imx6uirq.class = class_create(THIS_MODULE, DEVICE_NAME);if (IS_ERR(imx6uirq.class)) {return PTR_ERR(imx6uirq.class);}/*5 创建设备使用 device_create() 函数创建一个设备,并在/dev目录下创建相应的设备节点。参数包括设备所属的类(imx6uirq.class)、父设备(NULL,如果没有父设备)、设备号(imx6uirq.devid)、设备私有数据(NULL,一般为设备驱动程序提供一个指针)和设备名称(imx6uirq_NAME)。如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, DEVICE_NAME);if (IS_ERR(imx6uirq.device)) {return PTR_ERR(imx6uirq.device);}atomic_set(&imx6uirq.keyvalue, 0XFF);atomic_set(&imx6uirq.releasekey, 0);keyio_init(&imx6uirq);mytimer_init(&imx6uirq);printk("imx6uirq init!\r\n");return 0;
}/************有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数****************/
static void __exit imx6uirq_exit(void)
{del_timer_sync(&imx6uirq.timer);free_irq(imx6uirq.irqkeydesc[0].irq_num,&imx6uirq);gpio_free(imx6uirq.irqkeydesc[0].gpio_num);/*在模块卸载时,使用 cdev_del() 函数注销字符设备驱动,并使用 unregister_chrdev_region() 函数释放设备号资源。*//* 注销字符设备驱动 */cdev_del(&imx6uirq.cdev);/*  删除cdev */unregister_chrdev_region(imx6uirq.devid, DEVICE_CNT); /* 注销设备号 */device_destroy(imx6uirq.class, imx6uirq.devid);// 销毁设备,删除相应的设备节点class_destroy(imx6uirq.class);// 销毁设备类,释放相关资源printk("imx6uirq_drv exit!\r\n");
}/*****************其他完善:提供设备信息**********************/
module_init(imx6uirq_init);
module_exit(imx6uirq_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("oudafa");
MODULE_LICENSE("GPL");

四、应用程序的编写.

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"
/** @description		: main主程序* @param - argc 	: argv数组元素个数* @param - argv 	: 具体参数* @return 			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd;int ret = 0;char *filename;unsigned char data;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}while (1) {ret = read(fd, &data, sizeof(data));if (ret < 0) {  /* 数据读取错误或者无效 */} else {		/* 数据读取正确 */if (data)	/* 读取到数据 */printf("key value = %#X\r\n", data);}}close(fd);return ret;
}

 

五、实验测试

测试流程和之前一样,先使用make命令生成.ko文件还有APP这 两 个 文 件,然后拷贝到nfs挂载的rootfs/lib/modules/4.1.15 目录中,重启开发板。

先进入/lib/modules/4.1.15 目录,输入如下命令加载驱动模块:

insmod imx6uirq.ko

驱动加载成功以后可以通过查看/proc/interrupts 文件来检查一下对应的中断有没有被注册 上,输入如下命令:

 cat /proc/interrupts

可以看到如下:

从图可以看出 imx6uirq.c 驱动文件里面的 KEY0 中断已经存在了,触发方式为跳边沿(Edge),中断号为 49。

接下来使用如下命令来测试中断:

./imx6uirqApp /dev/imx6uirq

按下开发板上的 KEY0 键,终端就会输出按键值,如图所示:

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

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

相关文章

AWS攻略——使用中转网关(Transit Gateway)连接同区域(Region)VPC

文章目录 环境准备创建VPC 配置中转网关给每个VPC创建Transit Gateway专属挂载子网创建中转网关创建中转网关挂载修改VPC的路由 验证创建业务Private子网创建可被外网访问的环境测试子网连通性Public子网到Private子网Private子网到Private子网 知识点参考资料 在《AWS攻略——…

java日历功能

java 日历功能 功能概述java代码打印结果 功能概述 输入年份和月份&#xff0c;打印该月份所有日期&#xff0c;头部信息为星期一至星期日 java代码 package com.java.core.demoTest; import java.util.Calendar; import java.util.Scanner;// 打印日历 public class Calend…

计算机网络之IP篇

目录 一、IP 的基本认识 二、DNS 三、ARP 四、DHCP 五、NAT 六、ICMP 七、IGMP 七、ping 的工作原理 ping-----查询报文的使用 traceroute —— 差错报文类型的使用 八、断网了还能 ping 通 127.0.0.1 吗&#xff1f; 8.1、什么是 127.0.0.1 &#xff1f; 8.2、为…

小程序1rpx边框不完美

问题展示 原因 rpx类似rem&#xff0c;渲染后实际转换成px之后可能存在小数&#xff0c;在不同的设备上多多少少会存在渲染的问题。而1rpx的问题就更加明显&#xff0c;因为不足1个物理像素的话&#xff0c;在IOS会进行四舍五入&#xff0c;而安卓好像统一向上取整&#xff0c…

人工智能|网络爬虫——用Python爬取电影数据并可视化分析

一、获取数据 1.技术工具 IDE编辑器&#xff1a;vscode 发送请求&#xff1a;requests 解析工具&#xff1a;xpath def Get_Detail(Details_Url):Detail_Url Base_Url Details_UrlOne_Detail requests.get(urlDetail_Url, headersHeaders)One_Detail_Html One_Detail.cont…

[MySQL--基础]多表查询

前言 ⭐Hello!这里是欧_aita的博客。 ⭐今日语录&#xff1a;生活中最大的挑战就是发现自己是谁。然后&#xff0c;坚定不移地成为那个人。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法 MySQL数据库 多表查询 前言多表关系概述&#x1f…

为什么 SQL 不适合图数据库

背景 “为什么你们的图形产品不支持 SQL 或类似 SQL 的查询语言&#xff1f;” 过去&#xff0c;我们的一些客户经常问这个问题&#xff0c;但随着时间的推移&#xff0c;这个问题变得越来越少。 尽管一度被忽视&#xff0c;但图数据库拥有无缝设计并适应其底层数据结构的查询…

四层LVS与七层Nginx负载均衡的区别

一、四层负载均衡与七层负载均衡&#xff1a; &#xff08;1&#xff09;四层负载均衡&#xff1a; 四层负载均衡工作在 OSI 七层模型的第四层&#xff08;传输层&#xff09;&#xff0c;指的是负载均衡设备通过报文中的目标IP地址、端口和负载均衡算法&#xff0c;选择到达的…

赛事回顾 | 首届“智航杯“全国无人机智能算法竞赛落幕

11月28日&#xff0c;首届“智航杯”全国无人机智能算法竞赛实物赛在海南省三亚市成功落下帷幕。此次竞赛自2023年4月启动以来&#xff0c;共有来自全国145所高等院校和50多所企事业单位的1253支团队、3655人报名参赛&#xff0c;最终有6支队伍脱颖而出&#xff0c;入围了实物赛…

Elasticsearch:评估 RAG - 指标之旅

作者&#xff1a;Quentin Herreros&#xff0c;Thomas Veasey&#xff0c;Thanos Papaoikonomou 2020年&#xff0c;Meta发表了一篇题为 “知识密集型NLP任务的检索增强生成” 的论文。 本文介绍了一种通过利用外部数据库将语言模型 (LLM) 知识扩展到初始训练数据之外的方法。 …

11.9每日一题(无穷比无穷型的极限:提出并消去无穷因子、抓大头、拆多项式)

注&#xff1a;1、 为x的绝对值&#xff0c;因为x趋向负无穷&#xff0c;所以为 -x 2、用有理运算法则拆分式子时&#xff0c;注意判断极限是否存在

Python 网络爬虫(二):HTTP 基础知识

《Python入门核心技术》专栏总目录・点这里 文章目录 1. HTTP 协议简述2. HTTP 请求过程3. HTTP 的结构3.1 请求行3.2 请求头3.3 请求体3.4 状态行3.5 响应头3.6 响应体 4. Cookie 状态管理5. HTTP 请求示例6. 总结 大家好&#xff0c;我是水滴~~ 在准备学习网络爬虫之前&…

springboot慢性胃炎管理系统-计算机毕设 附源码 35541

SpringBoot慢性胃炎管理系统 摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;医疗行业当然也不例外。慢性胃炎管理系统是以实际运用为开发背景&#xff0c;运用软件工程…

PLC通过485Modbus转Profinet网关与温控表通讯在发酵罐的应用

前提&#xff1a;在自动化控制系统中&#xff0c;PLC通常需要和各种设备进行数据通讯&#xff0c;其中就包括温控表。而这些设备之间的通讯常常需要通过485Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;来实现。 方案&#xff1a;在一些应用场合中&#xff0c;Profi…

Spring Boot 3 整合 Spring Cache 与 Redis 缓存实战

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

2023/11/30JAVAweb学习(postman,各种参数,统一响应数据,三层架构,分层解耦,bean组件扫描,Bean注入及解决方式)

数组json形式 想切换实现类,只需要只在你需要的类上添加 Component 如果在同一层,可以更改扫描范围,但是不推荐这种方法 注入时存在多个同类型bean解决方式

C++不同平台下的RTTI实现

给定一个含有虚函数的对象的地址&#xff0c;找到对应的类名&#xff0c;不同平台下方法也不同&#xff0c;这是由于RTTI实现并没有统一的标准。 Linux&#xff1a; #include <iostream> #include <typeinfo>class Person { public:virtual void func(){std::cout…

正则表达式(3):入门

正则表达式&#xff08;3&#xff09;&#xff1a;入门 小结 本博文转载自 从这篇文章开始&#xff0c;我们将介绍怎样在Linux中使用”正则表达式”&#xff0c;如果你想要学习怎样在Linux中使用正则表达式&#xff0c;这些文章就是你所需要的。 在认识”正则表达式”之前&am…

ODN光纤链路全程衰减如何计算

在FTTH等宽带光纤接入工程设计中&#xff0c;需根据应用系统的相应波长计算ODN光纤链路的全程衰减&#xff0c;一方面验证是否满足系统的光功率预算指标要求&#xff0c;另一方面作为工程验收的参考指标。 1 计算方法 ODN光纤链路的全程衰减是指从OLT至ONU的光纤链路中&#xf…

LinuxBasicsForHackers笔记 -- 控制文件和目录权限

对于每个文件和目录&#xff0c;我们可以指定文件所有者、特定用户组以及所有其他用户的权限状态。 不同类型的用户 在Linux中&#xff0c;root用户是拥有一切权力的。 root 用户基本上可以在系统上执行任何操作。 系统上的其他用户具有有限的能力和权限&#xff0c;并且几乎…