记录一种在内核空间向用户空间通知中断的方法
记录一种在内核空间向用户空间通知中断的方法
- 0.前言
- 1.代码实现
- 1)内核设备驱动实现
- 2)消息通知实现
- 3)测试程序
- 2.解析
参考文章:Linux驱动实践:中断处理函数如何【发送信号】给应用层?
0.前言
最近在项目中遇到一个需求,需要将一个设备的中断状态通知到用户态的一个指定程序中,该设备的整体架构如下:
CPU通过SPI与设备通信,在设备中存在一个ISR控制单元,当设备发生中断时,会在ISR单元中给对应的寄存器置位,CPU可以通过轮询这些寄存器单元来判断这些中断是否发生。但这样做就失去了中断的意义,内核依旧是通过轮询方式来扫描中断。
在原先的实现方案中,SPI device通过内核通用接口挂载成了一个内核设备spidev,笔者在此设备基础上,重新在内核中创建了一个影子设备,用来实现用户态程序的读写接口,包括open()、release()、ioctl()接口等,但笔者在后续尝试为这段ISR寄存器地址申请内核中断时,发现官方驱动似乎存在一些问题,会出现设备无法挂载,或挂载后无法正常感知中断的结果。
所以笔者在同事的帮助下,重新做了一个解决方案,SPI device依旧通过内核通用接口挂载成内核设备,但不创建影子设备,用户态直接操作原设备;此外,为这段ISR地址单独实现一个platform driver,将地址重映射到内存地址中,这样当中断发生时,通过内核的netlink函数通知指定的端口。
1.代码实现
1)内核设备驱动实现
spidev.h:
#ifndef __SPIDEV_H__
#define __SPIDEV_H__#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/acpi.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/uaccess.h>
#include <linux/phy.h>
#include <net/dsa.h>struct spidev_switch{dev_t devid;struct cdev cdev;struct class *class;struct device *device;struct device *parent;void __iomem * vir_base_addr;unsigned long spi_base_addr;unsigned long mem_size;struct spi_device *spidev;uint32_t speed_hz;struct mutex spi_api_lock;struct mii_bus* stmmac_mii;int irq;struct gpio_irq_dev *gpioDev;};typedef struct{uint32_t addr;uint32_t *data;uint32_t regCnt;uint32_t burstType;uint32_t crcFlag;int32_t ret;
}switch_reg_t;struct gpio_irq_dev {struct gpio_desc *gpiod;int irq;char irq_name[10];char irq_drv_name[20];
}gpio_irq_dev_t;#define SWI_DEVICE_NAME "spi-dev"
#define SWI_DEVICE_CNT 1
#define SWI_IOC_MAGIC 'k'#define DRV_NAME "2404-spi"
#define DRV_VERSION "0.1.1"
#define GPIO_INTR_NUM 3#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \| SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \| SPI_RX_QUAD | SPI_RX_OCTAL)#define WITCH_IOC_SYS_BUS_REG_RD _IOWR(SWI_IOC_MAGIC, 8, switch_reg_t)
#define WITCH_IOC_SYS_BUS_REG_WR _IOWR(SWI_IOC_MAGIC, 9, switch_reg_t)
#define WITCH_IOC_SPI_INNER_REG_RD _IOWR(SWI_IOC_MAGIC, 12, switch_reg_t)
#define WITCH_IOC_SPI_INNER_REG_WR _IOWR(SWI_IOC_MAGIC, 13, switch_reg_t)
#define WITCH_IOC_SPI_BURST_REG_RD _IOWR(SWI_IOC_MAGIC, 14, switch_reg_t)
#define WITCH_IOC_SPI_BURST_REG_WR _IOWR(SWI_IOC_MAGIC, 15, switch_reg_t)
#define SWITCH_IOC_GPIO_ISR_EN _IOWR(SWI_IOC_MAGIC, 16, switch_reg_t)
#endif
spidev.c:
#include "spidev.h"
#include "my-netlink.h"
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/irq.h>static int spi_module_open (struct inode *node, struct file *filp)
{struct cdev* cdev_t;cdev_t = node->i_cdev;filp->private_data = container_of(cdev_t, struct spidev_switch, cdev);return 0;
}static ssize_t spi_module_read(struct file *filp, char *buff, size_t count, loff_t *offp)
{return 0;
}static ssize_t spi_module_write(struct file *filp, const char __user *buf, size_t count, loff_t *off)
{return 0;
}static int spi_module_release(struct inode *node, struct file *filp)
{return 0;
}static long int spi_module_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int32_t retval = 0;int32_t len;switch_reg_t switch_reg;uint32_t g_mem_reg_data[256] = {0};struct spidev_switch* kgsw = file->private_data;spidev_dbg_printf(spidev_LOG_DEBUG,"%s:cmd:%x,arg:%x\n",__func__,cmd,arg);switch (cmd) {case WITCH_IOC_SYS_BUS_REG_RD:len = _IOC_SIZE(cmd);retval = copy_from_user(&switch_reg, (const void __user *)arg, len);spidev_dbg_printf(spidev_LOG_DEBUG,"%s:retval:%x\n",__func__,retval);if (retval == 0){switch_reg.ret = spidev_apb_reg_read32(kgsw,switch_reg.addr, \switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);retval = copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));spidev_dbg_printf(spidev_LOG_DEBUG,"%s:retval:%x\n",__func__,retval);} break;case WITCH_IOC_SYS_BUS_REG_WR:len = _IOC_SIZE(cmd);retval = copy_from_user(&switch_reg, (const void __user *)arg, len);if (retval == 0){retval = copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));if (retval == 0){switch_reg.ret = spidev_apb_reg_write32(kgsw,switch_reg.addr, \switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);spidev_dbg_printf(spidev_LOG_DEBUG,"%s:retval:%x\n",__func__,retval);}} break;case WITCH_IOC_SPI_INNER_REG_RD:len = _IOC_SIZE(cmd);retval = copy_from_user(&switch_reg, (const void __user *)arg, len);if (retval == 0){switch_reg.ret = spi_inner_reg_read32(kgsw,switch_reg.addr,g_mem_reg_data);retval = copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));}break;case WITCH_IOC_SPI_INNER_REG_WR:len = _IOC_SIZE(cmd);retval = copy_from_user(&switch_reg, (const void __user *)arg, len);if (retval == 0){retval = copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));if (retval == 0){switch_reg.ret = spi_inner_reg_write32(kgsw,switch_reg.addr,g_mem_reg_data);}}break;case WITCH_IOC_SPI_BURST_REG_RD:len = _IOC_SIZE(cmd);retval = copy_from_user(&switch_reg, (const void __user *)arg, len);if (retval == 0){switch_reg.ret = spi_burst_reg_read32(kgsw,switch_reg.addr, \switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);retval = copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));}break;case WITCH_IOC_SPI_BURST_REG_WR:len = _IOC_SIZE(cmd);retval = copy_from_user(&switch_reg, (const void __user *)arg, len);if (retval == 0){retval = copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));if (retval == 0){switch_reg.ret = spi_burst_reg_write32(kgsw,switch_reg.addr, \switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);}}break;case SWITCH_IOC_GPIO_ISR_EN:len = _IOC_SIZE(cmd);retval = copy_from_user(&switch_reg, (const void __user *)arg, len);if (retval == 0){retval = copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));if (retval == 0){switch_reg.ret = gpio_irq_enable(kgsw, switch_reg.addr);}}break;default:spidev_dbg_printf(spidev_LOG_ERROR, "%s nuknow cmd type\n",__func__);break;}return retval;
}static struct file_operations switch_module_opts = {.owner = THIS_MODULE,.open = spi_module_open,.read = spi_module_read,.write = spi_module_write,.unlocked_ioctl = spi_module_ioctl,.release = spi_module_release,
};static int is_dev_creat = 0;
int spidev_dev_creat(struct spidev_switch* kgsw)
{int major;int ret;if(is_dev_creat)return 0;/*alloc devid*/alloc_chrdev_region(&kgsw->devid, 0, SWI_DEVICE_CNT, SWI_DEVICE_NAME);major = MAJOR(kgsw->devid);/*register char dev*/cdev_init(&kgsw->cdev, &switch_module_opts);ret = cdev_add(&kgsw->cdev, kgsw->devid, SWI_DEVICE_CNT);if (ret) {goto del_unrigister;}/*creat device node*/kgsw->class = class_create(THIS_MODULE, SWI_DEVICE_NAME);if (IS_ERR(kgsw->class)) {goto del_cdev;} kgsw->device = device_create(kgsw->class, NULL, kgsw->devid, NULL, SWI_DEVICE_NAME);if (IS_ERR(kgsw->device)) {goto destroy_class;}find_spi_dev(kgsw);of_parse_spidev_reg_addr(kgsw);is_dev_creat = 1;return 0;destroy_class:device_destroy(kgsw->class, kgsw->devid);
del_cdev:cdev_del(&kgsw->cdev);
del_unrigister:unregister_chrdev_region(kgsw->devid, SWI_DEVICE_CNT);is_dev_creat = 0;return -EIO;
}static irqreturn_t switch_2404_isr_func(int irq, void *dev_id)
{//printk(KERN_NOTICE "Enter switch 2404 irq(%d) function\n", irq);struct spidev_switch* kgsw = (struct spidev_switch*)dev_id;netlink_msg_t nlg;memset(&nlg, 0, sizeof(netlink_msg_t));void __iomem* vir_addr2 = 0x30800068 - kgsw->spi_base_addr + kgsw->vir_base_addr;writel(1,vir_addr2);nlg.cmd_type = REPORT_IRQ_TO_USER;nlg.irq_type = SWITCH_INTERRUPT_TYPE;nlg.irq_number = irq;printk(KERN_NOTICE "Kernel netlink send to user: irq_type(0x%x) irq_number(%x)\n", nlg.irq_type, nlg.irq_number);myspi_send_to_usr_msg(&nlg);return IRQ_HANDLED;
}static irqreturn_t myspi_gpio_isr_func(int irq, void *dev_id)
{netlink_msg_t nlg;memset(&nlg, 0, sizeof(netlink_msg_t));//disable_irq_nosync(irq);nlg.cmd_type = REPORT_IRQ_TO_USER;nlg.irq_type = GPIO_INTR_TYPE;nlg.irq_number = irq;printk(KERN_NOTICE "Kernel netlink send to user: irq_type(0x%x) irq_number(%x)\n", nlg.irq_type, nlg.irq_number);myspi_send_to_usr_msg(&nlg);return IRQ_HANDLED;
}static int myspi_gpio_irq_init(struct spidev_switch* kgsw)
{int rc;struct gpio_irq_dev *gpio_dev = NULL;struct device *dev = kgsw->parent;int index = 0;gpio_dev = devm_kzalloc(dev, sizeof(*gpio_dev) * GPIO_INTR_NUM, GFP_KERNEL);if (!gpio_dev){dev_err(dev, "no memory.\n");return -ENOMEM;}for (index = 0; index < GPIO_INTR_NUM; index++){sprintf(gpio_dev[index].irq_name, "%d", index);sprintf(gpio_dev[index].irq_drv_name, "%d-gpio-irq", index);gpio_dev[index].gpiod = devm_gpiod_get(dev, gpio_dev[index].irq_name, GPIOD_IN);if (IS_ERR(gpio_dev[index].gpiod)) {rc = PTR_ERR(gpio_dev[index].gpiod);if (rc != -EPROBE_DEFER)dev_err(dev, "error getting gpio (%d)\n", rc);goto irq_err;}gpio_dev[index].irq = gpiod_to_irq(gpio_dev[index].gpiod);if (gpio_dev[index].irq < 0){goto irq_err;} rc = devm_request_irq(dev, gpio_dev[index].irq, myspi_gpio_isr_func, IRQF_TRIGGER_RISING, gpio_dev[index].irq_drv_name, &gpio_dev[index]); if (rc){dev_err(dev, "unable to claim IRQ %d\n", gpio_dev[index].irq);goto irq_err;}else{printk(KERN_NOTICE " gpio-irq%d %s request success!\n", gpio_dev[index].irq, gpio_dev[index].irq_drv_name);}}kgsw->gpioDev = gpio_dev;return 0;irq_err:devm_kfree(dev, gpio_dev);return rc;
}static int spidev_probe(struct platform_device *pdev)
{struct spidev_switch* kgsw;int ret = 0;kgsw = devm_kzalloc(&pdev->dev, sizeof(struct spidev_switch), GFP_KERNEL);kgsw->parent = &pdev->dev;kgsw->irq = platform_get_irq(pdev, 0);if (kgsw->irq < 0){dev_err(&pdev->dev, "irq number invalid\n");goto err_out;}ret = spidev_dev_creat(kgsw);if (ret < 0) {dev_err(&pdev->dev, "device create fail!\n");goto err_out;} ret = request_irq(kgsw->irq, switch_2404_isr_func, IRQF_SHARED, dev_name(&pdev->dev), &kgsw->devid);if (ret < 0 && ret != -ENOTCONN) {dev_err(&pdev->dev, "can not get IRQ\n");goto err_out;}platform_set_drvdata(pdev, kgsw);myspi_gpio_irq_init(kgsw);myspi_netlink_init();spidev_dbg_printf(spidev_LOG_INFO, "%s probe done\n", DRV_NAME);return 0;err_out:return -ENXIO;
}static int spidev_remove(struct platform_device *pdev)
{struct spidev_switch* kgsw = dev_get_drvdata(&pdev->dev);printk(KERN_NOTICE " Delete device files\n");device_destroy(kgsw->class, kgsw->devid);printk(KERN_NOTICE " Delete class files\n");class_destroy(kgsw->class);printk(KERN_NOTICE " Deregister the character device driver\n");cdev_del(&kgsw->cdev);//Indicates device numbers for deregistering applicationsunregister_chrdev_region(kgsw->devid, SWI_DEVICE_CNT);myspi_netlink_exit();free_irq(kgsw->irq, &kgsw->devid);is_dev_creat = 0;return 0;
}static const struct of_device_id spidev_switch_of_match[] = {{ .compatible = "my-spidev" },{ },
};MODULE_DEVICE_TABLE(of, spidev_switch_of_match);static struct platform_driver spidev_driver = {.probe = spidev_probe,.remove = spidev_remove,.driver = {.name = "spidev switch driver",.of_match_table = spidev_switch_of_match,},
};static int __init spidev_switch_init(void)
{printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION);return platform_driver_register(&spidev_switch_driver);
}static void __exit spidev_switch_exit(void)
{printk(KERN_NOTICE DRV_NAME ": version %s unloaded\n", DRV_VERSION);platform_driver_unregister(&spidev_switch_driver);
}module_init(spidev_switch_init);
module_exit(spidev_switch_exit);MODULE_DESCRIPTION("Driver for myspi switch");
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL v2");
2)消息通知实现
my_netlink.h:
#ifndef __MYSPI_SWITCH_NETLINK_H__
#define __MYSPI_SWITCH_NETLINK_H__#define NETLINK_IRQ_REPORT_USER (MAX_LINKS-1)
#define NETLINK_DEFAULT_PORT_ID 0
#define NETLINK_DEFAULT_SEQ 0
#define NETLINK_DEFAULT_FLAGS 0
#define NETLINK_USER_PORT 100#define INTERRUPT_TYPE 0x24040000
#define PHY_GPIO_INTR_FMC0_TYPE 0x24040001
#define PHY_GPIO_INTR_FMC1_TYPE 0x24040002
#define PHY_GPIO_INTR_FMC2_TYPE 0x24040003enum {ASYNC_READ_CMD = 1,ASYNC_WRITE_CMD = 2,SHARE_MEM_REG_UPDATE = 3,SHARE_MEM_REG_UPDATE_STOP = 4,REPORT_IRQ_TO_USER = 5,
};typedef struct netlink_msg
{int cmd_type;int irq_type;int irq_number;
}netlink_msg_t;int myspi_netlink_init(void);
void myspi_netlink_exit(void);
int myspi_send_to_usr_msg(netlink_msg_t* reg);
#endif
my_netlink.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include "myspi_switch_netlink.h"static struct sock *nlsk = NULL;int myspi_send_to_usr_msg(netlink_msg_t* msg)
{struct sk_buff *nl_skb;struct nlmsghdr *nlh;int ret;//creat sk_buffnl_skb = nlmsg_new(sizeof(netlink_msg_t), GFP_ATOMIC);//what time to free;if(!nl_skb){printk("netlink alloc failure\n");return -1;}// set netlink head msg// 填充nlmsg报文头,并最终将报文填充到sk_buff发送出去nlh = nlmsg_put(nl_skb, NETLINK_DEFAULT_PORT_ID, NETLINK_DEFAULT_SEQ, NETLINK_IRQ_REPORT_USER, sizeof(netlink_msg_t), NETLINK_DEFAULT_FLAGS); if(nlh == NULL){printk("nlmsg_put failaure \n");nlmsg_free(nl_skb);return -1;}//send msg; nl_skb will free automaticmemcpy(nlmsg_data(nlh), msg, sizeof(netlink_msg_t));ret = netlink_unicast(nlsk, nl_skb, NETLINK_USER_PORT, MSG_DONTWAIT); // 指定端口号,端口号是唯一识别目的地址的标识,广播非阻塞式发送return ret;
}static void netlink_rcv_msg(struct sk_buff *skb)
{struct nlmsghdr *nlh = NULL;netlink_msg_t *reg_data = NULL;if(skb->len >= nlmsg_total_size(0)){nlh = nlmsg_hdr(skb); // 取出报文头reg_data = NLMSG_DATA(nlh); // 取出报文内容if(reg_data){//print_regdata(__func__,__LINE__,reg_data);}}
}static struct netlink_kernel_cfg cfg = { .input = netlink_rcv_msg, /* set recv callback */
};int myspi_netlink_init(void)
{/* create netlink socket */printk(" Kernel netlink create");nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_IRQ_REPORT_USER, &cfg);if(nlsk == NULL){ printk("Kernel netlink create error!\n");return -1; } return 0;
}void myspi_netlink_exit(void)
{printk(" Kernel netlink exit!\n");if (nlsk){netlink_kernel_release(nlsk); /* release ..*/nlsk = NULL;}
}
3)测试程序
test.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>#define NETLINK_REG_ASYNC_RW (MAX_LINKS-1) //31
#define NETLINK_USER_PORT 100enum {ASYNC_READ_CMD = 1,ASYNC_WRITE_CMD = 2,SHARE_MEM_REG_UPDATE = 3,SHARE_MEM_REG_UPDATE_STOP = 4,REPORT_IRQ_TO_USER = 5,
};typedef struct netlink_msg
{struct nlmsghdr hdr;int cmd;int irq_type;int irq_number;
}netlink_msg_t;typedef struct netlink_handle
{int skfd;struct sockaddr_nl saddr;struct sockaddr_nl daddr;
} netlink_handle_t;static netlink_handle_t async_netlink_handle;netlink_handle_t* get_async_netlink_handle(void)
{return &async_netlink_handle;
}int switch_netlink_init(void)
{int skfd;struct sockaddr_nl* saddr; //saddr 表示源端口地址,daddr表示目的端口地址struct sockaddr_nl* daddr;netlink_handle_t* handle;handle = get_async_netlink_handle();saddr = &(handle->saddr);daddr = &(handle->daddr);/* 创建NETLINK socket */skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_REG_ASYNC_RW);if(skfd == -1){perror("create socket error\n");return -1;}memset(saddr, 0, sizeof(struct sockaddr_nl));saddr->nl_family = AF_NETLINK; //AF_NETLINKsaddr->nl_pid = NETLINK_USER_PORT; //端口号(port ID) saddr->nl_groups = 0;if(bind(skfd, (struct sockaddr *)saddr, sizeof(struct sockaddr_nl)) != 0){perror("bind() error\n");close(skfd);return -1;}memset(daddr, 0, sizeof(struct sockaddr_nl));daddr->nl_family = AF_NETLINK;daddr->nl_pid = 0; // to kernel daddr->nl_groups = 0;handle->skfd = skfd;return 0;
}int switch_recv_reg_msg(void)
{int ret;netlink_msg_t recv_msg;socklen_t len;netlink_handle_t* handle;handle = get_async_netlink_handle();memset(&recv_msg, 0, sizeof(netlink_msg_t));len = sizeof(struct sockaddr_nl);ret = recvfrom(handle->skfd, &recv_msg, sizeof(netlink_msg_t), 0, (struct sockaddr *)&(handle->daddr), &len);if(!ret){printf("%s,%d, err\n",__func__,__LINE__);return -1;}switch(recv_msg.cmd){case REPORT_IRQ_TO_USER:printf("User netlink recv from kernel: irq_type(%x), irq_number(%d)\n",recv_msg.irq_type, recv_msg.irq_number);break;default:printf("Unknown command\n");break;}return 0;
}int main(int argc, char *argv[]){if (fork() == 0){printf("Create a child process, pid = %d\n", getpid());int ret = 0;ret = switch_netlink_init();if (ret){return ret;}while(1){switch_recv_reg_msg();}printf("main end\n");}else{printf("Father process, pid = %d\n", getpid());return;}
}
2.解析
在设备驱动文件中,实现了一个platform子系统的设备驱动,通过指定的compatible来匹配,并实现了设备的probe、remove、ioctl操作等接口,并且在创建内核设备节点时,为设备申请了spi设备中断和gpio中断,并初始化了一个netlink通信端口。当设备发生中断时,会触发回调函数中的netlink接口,向指定的用户端口发送中断号以及中断类型,这样就可以在用户程序中进行后续操作。
测试小程序中主要是测试netlink的接收,在while(1)循环中一直监听等待内核驱动的信息。
注:此文中仅为大致的实现框架,仅供参考,具体设备的详细实现流程需根据实际情况进行修改。
相关文章:
记录一种在内核空间向用户空间通知中断的方法
记录一种在内核空间向用户空间通知中断的方法 0.前言1.代码实现1)内核设备驱动实现2)消息通知实现3)测试程序 2.解析 参考文章:Linux驱动实践:中断处理函数如何【发送信号】给应用层? 0.前言 最近在项目中遇到一个需求,需要将一个…...
Apache Maven 标准文件目录布局
Apache Maven 采用了一套标准的目录布局来组织项目文件。这种布局提供了一种结构化和一致的方式来管理项目资源,使得开发者更容易导航和维护项目。理解和使用标准目录布局对于有效的Maven项目管理至关重要。本文将探讨Maven标准目录布局的关键组成部分,并…...
【vim】使用 gn 组合命令实现搜索选中功能
gn是Vim 7.4新增的一个操作(motion),作用是跳到并选中下一个搜索匹配项。 具体说,Vim里执行搜索后,执行n操作只会跳转到下一个匹配项,而不选中它。但是我们往往需要对匹配项执行一些修改操作,例…...
解决登录Google账号遇到手机上Google账号无法验证的问题
文章目录 场景小插曲解决方案总结 场景 Google账号在新的设备上登录的时候,会要求在手机的Google上进行确认验证,而如果没有安装Google play就可能出现像我一样没有任何弹框,无法实现验证 小插曲 去年,我在笔记本上登录了Googl…...
基于YOLOv10深度学习的公共安全持刀行为检测系统研究与实现(PyQt5界面+数据集+训练代码)
随着社会的不断进步和城市化进程的加快,人口密度的增加和社会结构的复杂化使得公共安全问题日益凸显。近年来,各类公共安全事件频发,其中持刀行为作为一种典型的暴力行为,已成为威胁公共安全的严重因素之一。这种行为在公共场所发…...
HP6心率血压传感器
目录 一、介绍 1、工作原理概述 2、具体实现步骤 二、HP6的通信及配置 1、通信接口 2、器件地址/命令 3、校验 三、程序设计 ①IIC通信相关基础函数 ②HP6相关基础函数 一、介绍 HP6心率血压传感器的原理主要基于光电容积脉搏波描记法(PPG)&…...
深入理解 JVM 中的 G1 垃圾收集器原理、算法、过程和参数配置
引言 Java 虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称 GC)是自动内存管理的核心部分。G1(Garbage-First)垃圾收集器是 Oracle 在 JDK 7u4 版本中引入的一种新型垃圾收集器,…...
细说敏捷:敏捷四会之standup meeting
上一篇文章中,我们讨论了 敏捷四会 中 冲刺计划会 的实施要点,本篇我们继续分享敏捷四会中实施最频繁,团队最容易实施但往往也最容易走形的第二个会议:每日站会 关于每日站会的误区 站会是一个比较有标志性的仪式活动࿰…...
MySQL系列之身份鉴别(安全)
导览 前言Q:如何保障MySQL数据库身份鉴别的有效性一、有效性检查1. 用户唯一2. 启用密码验证3. 是否存在空口令用户4. 是否启用口令复杂度校验5. 是否设置口令的有效期6. 是否限制登录失败尝试次数7. 是否设置(超过尝试次数)锁定的最小时长8.…...
vue3封装Element Plus table表格组件
支持绝大部分Element Plus原有设置属性,支持分页,支持动态适配高度 效果展示 组件代码: <template><div class"table-wrap" ref"tableWrap"><el-tableclass"w100 h100":data"tableInfo.…...
SFP+光模块介绍
SFP光模块介绍 1 SFP光模块简介(Small Form -Factor Pluggable)2 光模块管脚定义 1 SFP光模块简介(Small Form -Factor Pluggable) 光模块(Optical Module)由光电子器件、功能电路和光接口等组成,光电子器件包括激光发射器(Laser Transmitte…...
神经网络的初始化
目录 为什么需要初始化? 初始化的常用方法: 是否必须初始化? 初始化神经网络中的权重和偏置是深度学习模型训练中非常重要的一步,虽然在某些情况下不进行初始化也能训练出模型,但正确的初始化方法能够显著提高训练效…...
数据集搜集器0.01
我们使用Python的Tkinter库来创建GUI界面,并使用requests库来从360百科获取数据。下面是一个完整的示例代码,它包括了一个简单的GUI界面,用户可以输入问题,点击按钮后会从360百科获取相关信息,并显示在同一个Text组件中…...
【Lucene】搜索引擎和文档相关性评分 BM25 算法的工作原理
BM25 算法的工作原理: 什么是 BM25 算法? BM25 是一种流行的文本检索算法,广泛用于搜索引擎和文档相关性评分。它基于概率检索模型,旨在评估查询和文档之间的相关性。 核心公式 BM25 的公式如下: score ( D , Q ) …...
嵌入式Linux——文件类型
目录 普通文件 目录文件 目录文件的权限与管理 字符设备文件和块设备文件 符号链接文件 查看符号链接 删除符号链接 修改符号链接 管道文件 匿名管道(Anonymous Pipe) 匿名管道的特点: 使用示例: 命名管道(…...
ES6 模块化语法
目录 ES6 模块化语法 分别暴露 统一暴露 编辑 默认暴露 ES6 模块化引入方式 ES6 模块化语法 模块功能主要由两个命令构成:export 和 import。 ⚫ export 命令用于规定模块的对外接口(哪些数据需要暴露,就在数据前面加上关键字即可…...
Gradio学习笔记记录
安装指令:pip install gradio方法介绍 Interface》用于构建一些简单的页面,可以直接用这个指令搞定 形式》接收三个参数分别为处理函数、输入、输出三部分,呈现一般左/上为输入,右或下为输出 fn:将用户界面 ࿰…...
Node.js的下载与安装(支持各种新旧版本)
目录 1、node官网 2、node软件下载 3、软件安装(完整版) 1、node官网 Node.js — Download Node.jshttps://nodejs.org/en/download/package-manager 2、node软件下载 按照下图进行选择node版本(真心推荐16/18,而是尽量是LTS…...
数据库和缓存的数据一致性 -20241124
问题描述 一致性 缓存中有数据,缓存的数据值数据库中的值缓存中本没有数据,数据库中的值最新值(有请求查询数据库时,会将数据写入缓存,则变为上面的“一致”状态) “数据不一致”: 缓存的数据值…...
【计算机网络】多路转接之select
系统提供select()来实现多路转接 IO 等 拷贝 -> select()只负责等待,可以一次等待多个fd select()本身没有数据拷贝的能力,拷贝要read()/write()来完成 一、select的使用 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exc…...
Linux命令思维导图
看到一个很不错的Linux命令思维导图,用机器翻译了一下,建议收藏备用。 附上英文版:...
自然语言处理(词嵌入和词向量的使用)
一、实验目的 1.了解词嵌入和词向量的基本概念及其在自然语言处理中的应用。 2.掌握使用One-Hot编码和Word2Vec模型构建词向量的方法。 3.掌握Doc2Vec模型构建文档向量的方法以及如何计算文档向量之间的相似度。 二、实验内容 (1)使用One-Hot编码构…...
量子感知机
神经网络类似于人类大脑,是模拟生物神经网络进行信息处理的一种数学模型。它能解决分类、回归等问题,是机器学习的重要组成部分。量子神经网络是将量子理论与神经网络相结合而产生的一种新型计算模式。1995年美国路易斯安那州立大学KAK教授首次提出了量子…...
[HarmonyOS] 解决HMRouter路由地址无法抽取的问题
解决HMRouter路由地址无法抽取的问题 背景 最近开始学习HarmonyOS开发,搭建项目的时候采用了 HMRouter 路由框架,在项目里使用到路由跳转,官方链接在这: https://gitee.com/hadss/hmrouter/blob/master/HMRouterLibrary/README…...
七天掌握SQL--->第四天:事务处理与并发控制
# 7天掌握SQL - 第四天:事务处理与并发控制 ## 目标 - 学习事务处理的基本概念,如ACID特性。 - 掌握并发控制的方法,如锁机制、事务隔离级别等。 - 通过实际案例练习事务处理和并发控制。 ## 1. 事务处理的基本概念 事务处理是数据库管理系…...
Docker学习笔记整理
这周不知道写点啥内容做个分享,但还是秉持学会分享的精神,粗略放一些Docker相关的问题和解答吧,后面有机会再补补再深挖深挖o(>﹏<)o 1. 容器VS虚拟机 虚拟机是一种带环境安装的解决方案(资源完全隔离),有以下缺…...
PIMPL模式和D指针
一、PIMPL模式概念: Pimpl(pointer to implementation, 指向实现的指针)是一种用来对“类的接口与实现”进行解耦合的方法。就是将真正的实现细节的Implementor从类定义的头文件中分离出去,公有类通过一个私有指针指向隐藏的实现类…...
Linux入门系列--文件与目录
一、介绍 在Linux中,有着一句话,叫做:一切皆文件。也就是任何东西都是以文件的形式存储的。 目录结构 bin:全程binary,含义是二进制。该目录中存储的都是一些二进制文件。我们学过C/C,其实也都知道机器能…...
论文阅读——Intrusion detection systems using longshort‑term memory (LSTM)
一.基本信息 论文名称:Intrusion detection systems using longshort‑term memory (LSTM) 中文翻译:基于长短期记忆(LSTM)的入侵检测系统 DOI:10.1186/s40537-021-00448-4 作者:FatimaEzzahra Laghrissi1* , Samira Douzi2*, Kha…...
平面点排序(结构体专题)
(1) 题目描述 平面上有n个点,坐标均为整数。请按与坐标原点(0,0)距离的远近将所有点排序输出。可以自己写排序函数,也可以用qsort库函数排序。 输入描述 输入有两行,第一行是整数n(1<n<10)ÿ…...
QT 实现仿制 网络调试器(未实现连接唯一性) QT5.12.3环境 C++实现
网络调试助手: 提前准备:在编写代码前,要在.pro工程文件中,添加network模块。 服务端: 代码: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QtWidgets> #inclu…...
搜索引擎中广泛使用的文档排序算法——BM25(Best Matching 25)
在搜索场景中,BM25能计算每个文档与查询的匹配度,从中找出最相关的文档,并按相关性高低排序展示。 要理解BM25,需要掌握以下几个关键概念: 1. 词频(Term Frequency, TF):某关键词在文…...
文件上传需要考虑的安全性问题及解决方案
恶意文件上传:攻击者可能上传包含恶意代码的文件,如病毒、木马等,这些文件被服务器执行后,可能完全控制服务器或破坏服务器安全。为避免这种情况,应实施严格的文件验证策略,只允许特定的安全文件类型上传&a…...
论文笔记:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
1. 挑战/问题(Challenges/Issues): 这篇论文探讨了大型预训练语言模型在处理知识密集型自然语言处理(NLP)任务时面临的挑战。尽管这些模型在参数中存储了大量事实知识,并在微调后能够在下游NLP任务中取得很…...
Web 网络安全
一. 浏览器系统安全方面,使用多进程方案,而js主线程运行在渲染进程中,渲染进程时运行在沙箱中的,没有对本地OS文件的直接读写权限,所以需要通过IPC与浏览器主线程通信,才可以获取cookie等信息,这…...
Android Gradle 插件和 Android Studio 兼容性
Android Gradle 插件和 Android Studio 兼容性 Android Studio 版本所需的 AGP 版本Ladybug 2024.2.13.2-8.7Ladybug 2024.2.13.2-8.7Koala 2024.1.12024.2.1Jellyfish 2023.3.12024.2.1Iguana 2023.2.13.2-8.3Hedgehog 2023.1.13.2-8.2Giraffe 2022.3.13.2-8.1Flamingo 2022.…...
C++设计模式-策略模式-StrategyMethod
动机(Motivation) 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。 如何在运…...
【H2O2|全栈】JS案例章节(四)——使用原型的JS工厂模式
目录 前言 开篇语 准备工作 需求 工厂模式 概念 优点 添加元素的新方法 分析案例 HTML代码 JS实现 结束语 前言 开篇语 本系列为短章节,单独讲述部分特殊重点案例,本期讲述使用原型来实现JS的工厂模式。 与HTML和CSS相比,JS加…...
.NET9 - 新功能体验(三)
书接上回,我们继续来聊聊.NET9和C#13带来的新变化。 01、Linq新方法 CountBy 和 AggregateBy 引入了新的方法 CountBy 和 AggregateBy后,可以在不经过GroupBy 分配中间分组的情况下快速完成复杂的聚合操作,同时方法命名也非常直观࿰…...
论文概览 |《Journal of Urban Technology》2024 Vol.31 Issue.2
本次给大家整理的是《Journal of Urban Technology》杂志2024年第31卷第2期的论文的题目和摘要,一共包括6篇SCI论文! 论文1 Aerial Video Surveillance in a Megacity: A Case Study in Santiago, Chile 大城市中的空中视频监控:智利圣地亚哥…...
Makefile 之 自动化变量
作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。 "$" 表示目标的集合,就像一个数组,"$"依次取出目标,并执于命令。 "$<"和"$&qu…...
【C语言】const修饰符在指针变量中的作用详解
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 💯前言💯指针与解引用:基础概念1. 指针变量2. 解引用操作 💯const 修饰指针变量的三种主要情况1. 情况一:int * const p (指针本…...
阿里云ECS服务器监控报警配置
背景:服务器偶尔会异常,比如CPU过载,磁盘满,影响使用,人工盯服务器不现实 采用方法:阿里云监控告警功能 步骤: 第一步:登录阿里云服务器 https://account.aliyun.com/login/logi…...
高性能linux服务器运维实战小结 性能调优工具
性能指标 进程指标 进程关系 父进程创子进程时,调fork系统调用。调用时,父给子获取一个进程描述符,并设置新的pid,同事复制父进程的进程描述符给子进程,此时不会复制父进程地址空间,而是父子用相同地址空…...
初识Linux—— 基本指令(下)
前言: 本篇继续来学习Linux的基础指令,继续加油!!! 本篇文章对于图片即内容详解,已同步到本人gitee:Linux学习: Linux学习与知识讲解 Linux指令 1、查看文件内容的指令 cat cat 查看文件…...
【Linux】线程的互斥和同步
【Linux】线程的互斥和同步 线程间的互斥 临界资源:多线程执行共享的资源就叫做临界资源临界区:每个线程内部,访问临界资源的代码,就叫做临界区互斥:任何时刻,互斥保证有且只有一个执行流进入临界区&#…...
详解Oracle表的类型(二)
1.引言: Oracle数据库提供了多种表类型,以满足不同的数据存储和管理需求。本博文将对Oracle分区表及使用场景进行详细介绍。 2. 分区表 分区表是Oracle数据库中一种重要的表类型,它通过将表数据分割成多个逻辑部分来提高查询性能、管理灵活…...
VSCode 下载 安装
VSCode【下载】【安装】【汉化】【配置C环境(超快)】(Windows环境)-CSDN博客 Download Visual Studio Code - Mac, Linux, Windowshttps://code.visualstudio.com/Downloadhttps://code.visualstudio.com/Download 注意࿰…...
java中的最小堆
概述 最小堆minHeap指的级别n的每个节点存储的值小于或等于级别n1的子节点的值。因此,根就存储了其中最小的值。 注意节点的值与其他兄弟节点的值之间没有必然关系。 java中最小堆的表示 利用数组 常用的是利用数组minHeap[]表示,将最小堆的节点或值…...
ES实用面试题
一、es是什么,为什么要用它? ES通常是Elasticsearch的简称,它是一个基于Lucene构建的开源搜索引擎。Elasticsearch以其分布式、高扩展性和实时数据分析能力而闻名,广泛用于全文搜索、日志分析、实时监控等多种场景。 基本特点&am…...