19 异步通知

一、异步通知

1. 异步通知简介

  阻塞和非阻塞两种方式都是需要应用程序去主动查询设备的使用情况。

  异步通知类似于驱动可以主动报告自己可以访问,应用程序获取信号后会从驱动设备中读取或写入数据。

  异步通知最核心的就是信号

#define SIGHUP 1 /* 终端挂起或控制进程终止 */
#define SIGINT 2 /* 终端中断(Ctrl+C 组合键) */
#define SIGQUIT 3 /* 终端退出(Ctrl+\组合键) */
#define SIGILL 4 /* 非法指令 */
#define SIGTRAP 5 /* debug 使用,有断点指令产生 */
#define SIGABRT 6 /* 由 abort(3)发出的退出指令 */
#define SIGIOT 6 /* IOT 指令 */
#define SIGBUS 7 /* 总线错误 */
#define SIGFPE 8 /* 浮点运算错误 */
#define SIGKILL 9 /* 杀死、终止进程 */
#define SIGUSR1 10 /* 用户自定义信号 1 */
#define SIGSEGV 11 /* 段违例(无效的内存段) */
#define SIGUSR2 12 /* 用户自定义信号 2 */
#define SIGPIPE 13 /* 向非读管道写入数据 */
#define SIGALRM 14 /* 闹钟 */
#define SIGTERM 15 /* 软件终止 */
#define SIGSTKFLT 16 /* 栈异常 */
#define SIGCHLD 17 /* 子进程结束 */
#define SIGCONT 18 /* 进程继续 */
#define SIGSTOP 19 /* 停止进程的执行,只是暂停 */
#define SIGTSTP 20 /* 停止进程的运行(Ctrl+Z 组合键) */
#define SIGTTIN 21 /* 后台进程需要从终端读取数据 */
#define SIGTTOU 22 /* 后台进程需要向终端写数据 */
#define SIGURG 23 /* 有"紧急"数据 */
#define SIGXCPU 24 /* 超过 CPU 资源限制 */
#define SIGXFSZ 25 /* 文件大小超额 */
#define SIGVTALRM 26 /* 虚拟时钟信号 */
#define SIGPROF 27 /* 时钟信号描述 */
#define SIGWINCH 28 /* 窗口大小改变 */
#define SIGIO 29 /* 可以进行输入/输出操作 */
#define SIGPOLL SIGIO
#define SIGPWR 30 /* 断点重启 */
#define SIGSYS 31 /* 非法的系统调用 */
#define SIGUNUSED 31 /* 未使用信号 */

  其中 9 和 19 是不能被忽略的,这些信号相当于中断号,不同的中断号代表不同的中断,不同的中断又可以实现不同的功能。

  使用中断的时候需要设置中断处理函数,那么应用程序中使用了信号,也要有信号处理函数,在应用程序中使用 signal 函数来设置信号的处理函数:

/** @description : 设置指定信号的处理函数* @param - signum : 要设置处理函数的信号* @param - handler : 信号的处理函数* @return : 设置成功的话返回信号的前一个处理函数,设置失败的话返回 SIG_ERR*/
sighandler_t signal(int signum, sighandler_t handler);

  这里的信号处理原型为:

typedef void (*sighandler_t)(int)

  之前使用的 kill -9 PID就是指定进程发送  SIGKILL 这个信号。当我们按下 CTRL + C 的时候,会向当前正在占用终端的应用程序发送 SIGINT(2)信号,最终中断终端。

  在 linux/atk-mpl 文件夹下,创建 signaltest.c,并输入以下代码:

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>/* 信号处理函数 */
void signalint_handler(int num)
{printf("\r\nSIGINT signal!\r\n");exit(0);
}int main(void)
{signal(SIGINT, signalint_handler);while(1);return 0;
}/* 总体流程:首先自己设置信号处理函数,之后应用程序开启,按下CTRL+C向signaltest发送SIGINT信号以后signalint_handler函数就会执行,此函数会先输出SIGINT signal!后,exit函数会关闭signaltest的应用程序。*/

  输入命令编译 signaltest.c:

gcc signaltest.c -o signaltest

   输入命令执行应用程序 signaltest:

./signaltest

   按下 CTRL+ C后,可以看到:

2. 驱动中的信号处理

① fasync_struct 结构体

  如果在驱动中使用信号,首先先定义一个fasync_struct结构体变量,一般将这个结构体放在 xxx_dev 结构体中,例如:

/* key设备结构体 */
struct key_dev{dev_t devid;			/* 设备号 	 */struct cdev cdev;		/* cdev 	*/struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */struct device_node	*nd; /* 设备节点 */int key_gpio;			/* key所使用的GPIO编号		*/struct timer_list timer;			/* 按键值 		*/int irq_num;			/* 中断号 		*/atomic_t status;   		/* 按键状态 */wait_queue_head_t r_wait;	/* 读等待队列头 */struct fasync_struct *async_queue;    /* fasync_struct结构体 */
};

② fasync 函数

  函数负责将用户空间应用程序的进程 ID 添加到驱动程序相应文件的异步通知队列中。

  如果要使用异步通知,需要在驱动程序里实现 file_operation 操作集中的 fasync函数:

int (*fasync) (int fd, struct file *filp, int on)

  驱动中的 fasync 函数示例如下:

struct xxx_dev {......struct fasync_struct *async_queue; /* 异步相关结构体 */
};static int xxx_fasync(int fd, struct file *filp, int on)    // fasync 函数
{struct xxx_dev *dev = (xxx_dev)filp->private_data;    // 私有数据if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)    // 这里的fasync_helper其实是被fasync调用的return -EIO;return 0;
}static struct file_operations xxx_ops = {.......fasync = xxx_fasync,    // 只要是 file_operations里的函数,都要这样的形势走一遍......
};

  如果需要关闭驱动文件,那么需要在 release 函数里面释放 fasync_struct,示例如下:

static int xxx_release(struct inode *inode, struct file *filp)
{return xxx_fasync(-1, filp, 0); /* 删除异步通知 */
}
static struct file_operations xxx_ops = {.......release = xxx_release,        // 只要用到了file_operations就要这样
};

③ kill_fasync 函数

/** @description : 当设备可以访问的时候,驱动程序需要向应用程序发出信号,此函数负责发送指定的信号* @param - fp : 要操作的 fasync_struct* @param - sig : 要发送的信号* @param - band : 可读时设置为 POLL_IN,可写时设置为 POLL_OUT* @return : 无*/
void kill_fasync(struct fasync_struct **fp, int sig, int band);

3. 应用程序对异步通知的处理

  总共包含三步:

① 注册信号处理函数

  这里的处理函数需要自己设置,并且最后传给 signal 函数。

② 将本应用程序的进程号告诉内核

  使用 fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核。 

③ 开启异步通知

flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */

  重点就是通过 fcntl 函数设置进程状态为 FASYNC,经过这一步,驱动程序中的 fasync 函数就会执行。 

  当用户空间应用程序调用 fcntl 系统调用并设置 F_SETFL 命令以及 FASYNC 标志时,内核会调用驱动程序中的 fasync 函数。

二、程序编写

  在 /linux/atk-mpl/Drivers 文件夹下创建 16_asynchronization 文件夹,并创建 asynchronization.c 文件,并输入以下代码:

#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/of_irq.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fcntl.h>#define KEY_CNT			1		/* 设备号个数 	*/
#define KEY_NAME		"key"	/* 名字 		*//* 定义按键状态 */
enum key_status {KEY_PRESS = 0,      // 按键按下KEY_RELEASE,        // 按键松开KEY_KEEP,           // 按键状态保持
};/* key设备结构体 */
struct key_dev{dev_t devid;			/* 设备号 	 */struct cdev cdev;		/* cdev 	*/struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */struct device_node	*nd; /* 设备节点 */int key_gpio;			/* key所使用的GPIO编号		*/struct timer_list timer;			/* 按键值 		*/int irq_num;			/* 中断号 		*/	atomic_t status;   		/* 按键状态 */wait_queue_head_t r_wait;	/* 读等待队列头 */struct fasync_struct *async_queue;	/* fasync_struct结构体 */
};static struct key_dev key;          /* 按键设备 */static irqreturn_t key_interrupt(int irq, void *dev_id)		// 中断处理函数
{/* 按键防抖处理,开启定时器延时15ms */mod_timer(&key.timer, jiffies + msecs_to_jiffies(15));return IRQ_HANDLED;
}/** @description	: 初始化按键IO,open函数打开驱动的时候* 				  初始化按键所使用的GPIO引脚。* @param 		: 无* @return 		: 无*/
static int key_parse_dt(void)
{int ret;const char *str;/* 设置LED所使用的GPIO *//* 1、获取设备节点:key */key.nd = of_find_node_by_path("/key");if(key.nd == NULL) {printk("key node not find!\r\n");return -EINVAL;}/* 2.读取status属性 */ret = of_property_read_string(key.nd, "status", &str);if(ret < 0) return -EINVAL;if (strcmp(str, "okay"))return -EINVAL;/* 3、获取compatible属性值并进行匹配 */ret = of_property_read_string(key.nd, "compatible", &str);if(ret < 0) {printk("key: Failed to get compatible property\n");return -EINVAL;}if (strcmp(str, "alientek,key")) {printk("key: Compatible match failed\n");return -EINVAL;}/* 4、 获取设备树中的gpio属性,得到KEY0所使用的KYE编号 */key.key_gpio = of_get_named_gpio(key.nd, "key-gpio", 0);if(key.key_gpio < 0) {printk("can't get key-gpio");return -EINVAL;}/* 5 、获取GPIO对应的中断号 */key.irq_num = irq_of_parse_and_map(key.nd, 0);if(!key.irq_num){return -EINVAL;}printk("key-gpio num = %d\r\n", key.key_gpio);return 0;
}static int key_gpio_init(void)		// gpio初始化
{int ret;unsigned long irq_flags;ret = gpio_request(key.key_gpio, "KEY0");		// 申请gpioif (ret) {printk(KERN_ERR "key: Failed to request key-gpio\n");return ret;}	/* 将GPIO设置为输入模式 */gpio_direction_input(key.key_gpio);/* 获取设备树中指定的中断触发类型 */irq_flags = irq_get_trigger_type(key.irq_num);if (IRQF_TRIGGER_NONE == irq_flags)irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;/* 申请中断 */ret = request_irq(key.irq_num, key_interrupt, irq_flags, "Key0_IRQ", NULL);if (ret) {gpio_free(key.key_gpio);return ret;}return 0;
}static void key_timer_function(struct timer_list *arg)		// 定时器处理函数
{static int last_val = 1;int current_val;/* 读取按键值并判断按键当前状态 */current_val = gpio_get_value(key.key_gpio);if (0 == current_val && last_val){atomic_set(&key.status, KEY_PRESS);	// 按下wake_up_interruptible(&key.r_wait);	// 唤醒r_wait队列头中的所有队列if(key.async_queue)kill_fasync(&key.async_queue, SIGIO, POLL_IN);	// }else if (1 == current_val && !last_val) {atomic_set(&key.status, KEY_RELEASE);   // 松开wake_up_interruptible(&key.r_wait);	// 唤醒r_wait队列头中的所有队列if(key.async_queue)kill_fasync(&key.async_queue, SIGIO, POLL_IN);}elseatomic_set(&key.status, KEY_KEEP);              // 状态保持last_val = current_val;
}/** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int key_open(struct inode *inode, struct file *filp)
{return 0;
}/** @description     : 从设备读取数据 * @param – filp    : 要打开的设备文件(文件描述符)* @param – buf     : 返回给用户空间的数据缓冲区* @param – cnt     : 要读取的数据长度* @param – offt    : 相对于文件首地址的偏移* @return          : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t key_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{int ret;if (filp->f_flags & O_NONBLOCK) {	// 非阻塞方式访问if(KEY_KEEP == atomic_read(&key.status))return -EAGAIN;} else {							// 阻塞方式访问/* 加入等待队列,当有按键按下或松开动作发生时,才会被唤醒 */ret = wait_event_interruptible(key.r_wait, KEY_KEEP != atomic_read(&key.status));if(ret)return ret;}/* 将按键状态信息发送给应用程序 */ret = copy_to_user(buf, &key.status, sizeof(int));/* 状态重置 */atomic_set(&key.status, KEY_KEEP);return ret;
}/** @description		: fasync函数,用于处理异步通知* @param – fd		: 文件描述符* @param – filp	: 要打开的设备文件(文件描述符)* @param – on		: 模式* @return			: 负数表示函数执行失败*/
static int key_fasync(int fd, struct file *filp, int on)
{return fasync_helper(fd, filp, on, &key.async_queue);
}/** @description		: 向设备写数据 * @param - filp 	: 设备文件,表示打开的文件描述符* @param - buf 	: 要写给设备写入的数据* @param - cnt 	: 要写入的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 写入的字节数,如果为负值,表示写入失败*/
static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** @description		: 关闭/释放设备* @param - filp 	: 要关闭的设备文件(文件描述符)* @return 			: 0 成功;其他 失败*/
static int key_release(struct inode *inode, struct file *filp)
{return key_fasync(-1, filp, 0);
}/** @description     : poll函数,用于处理非阻塞访问* @param - filp    : 要打开的设备文件(文件描述符)* @param - wait    : 等待列表(poll_table)* @return          : 设备或者资源状态,*/
static unsigned int key_poll(struct file *filp, struct poll_table_struct *wait)
{unsigned int mask = 0;poll_wait(filp, &key.r_wait, wait);if(KEY_KEEP != atomic_read(&key.status))	// 按键按下或松开动作发生mask = POLLIN | POLLRDNORM;	// 返回PLLINreturn mask;
}/* 设备操作函数 */
static struct file_operations key_fops = {.owner = THIS_MODULE,.open = key_open,.read = key_read,.write = key_write,.release = 	key_release,.poll = key_poll,.fasync	= key_fasync,
};/** @description	: 驱动入口函数* @param 		: 无* @return 		: 无*/
static int __init mykey_init(void)
{int ret;/* 初始化等待队列头 */init_waitqueue_head(&key.r_wait);/* 初始化按键状态 */atomic_set(&key.status, KEY_KEEP);/* 设备树解析 */ret = key_parse_dt();if(ret)return ret;/* GPIO 中断初始化 */ret = key_gpio_init();if(ret)return ret;/* 注册字符设备驱动 *//* 1、创建设备号 */ret = alloc_chrdev_region(&key.devid, 0, KEY_CNT, KEY_NAME);	/* 申请设备号 */if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", KEY_NAME, ret);goto free_gpio;}/* 2、初始化cdev */key.cdev.owner = THIS_MODULE;cdev_init(&key.cdev, &key_fops);/* 3、添加一个cdev */ret = cdev_add(&key.cdev, key.devid, KEY_CNT);if(ret < 0)goto del_unregister;/* 4、创建类 */key.class = class_create(THIS_MODULE, KEY_NAME);if (IS_ERR(key.class)) {goto del_cdev;}/* 5、创建设备 */key.device = device_create(key.class, NULL, key.devid, NULL, KEY_NAME);if (IS_ERR(key.device)) {goto destroy_class;}/* 6、初始化timer,设置定时器处理函数,还未设置周期,所有不会激活定时器 */timer_setup(&key.timer, key_timer_function, 0);return 0;destroy_class:device_destroy(key.class, key.devid);
del_cdev:cdev_del(&key.cdev);
del_unregister:unregister_chrdev_region(key.devid, KEY_CNT);
free_gpio:free_irq(key.irq_num, NULL);gpio_free(key.key_gpio);return -EIO;
}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static void __exit mykey_exit(void)
{/* 注销字符设备驱动 */cdev_del(&key.cdev);/*  删除cdev */unregister_chrdev_region(key.devid, KEY_CNT); /* 注销设备号 */del_timer_sync(&key.timer);		/* 删除timer */device_destroy(key.class, key.devid);/*注销设备 */class_destroy(key.class); 		/* 注销类 */free_irq(key.irq_num, NULL);	/* 释放中断 */gpio_free(key.key_gpio);		/* 释放IO */
}module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

  编写 asynchronizationApp.c 文件:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>static int fd;/** SIGIO信号处理函数* @param – signum		: 信号值* @return				: 无*/
static void sigio_signal_func(int signum)
{unsigned int key_val = 0;read(fd, &key_val, sizeof(unsigned int));if (0 == key_val)printf("Key Press\n");else if (1 == key_val)printf("Key Release\n");printf("SIGIO signal!\r\n");
}/** @description		: main主程序* @param – argc		: argv数组元素个数* @param – argv		: 具体参数* @return			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int flags = 0;/* 判断传参个数是否正确 */if(2 != argc) {printf("Usage:\n""\t./asyncKeyApp /dev/key\n");return -1;}/* 打开设备 */fd = open(argv[1], O_RDONLY | O_NONBLOCK);  // 非阻塞O_NONBLOCKif(0 > fd) {printf("ERROR: %s file open failed!\n", argv[1]);return -1;}/* 设置信号SIGIO的处理函数 */signal(SIGIO, sigio_signal_func);       // signal函数,用于设置信号的处理函数fcntl(fd, F_SETOWN, getpid());			// 将当前进程的进程号告诉给内核flags = fcntl(fd, F_GETFD);				// 获取当前的进程状态fcntl(fd, F_SETFL, flags | FASYNC);		// 设置进程启用异步通知功能/* 循环轮询读取按键数据 */while(1) {sleep(2);}/* 关闭设备 */close(fd);return 0;
}

三、运行测试

  首先先编写 Makefile 文件:

KERNELDIR := /home/alientek/linux/atk-mpl/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd)obj-m := asynchronization.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

  之后编译 asynchronization.c 和 asynchronizationApp 文件:

make
arm-none-linux-gnueabihf-gcc asynchronizationApp.c -o asynchronization

  将编译好的 asynchronizationApp 和 asynchronization.ko 复制到:

sudo cp asynchronization asynchronization.ko /home/alientek/linux/nfs/rootfs/lib/modules/5.4.31/ -f

  开启开发板,输入以下命令:

cd lib/modules/5.4.31/
depmod
modprobe asynchronization.ko

   测试 App 应用程序:

./asynchronization /dev/key

  按下 Key0,则会出现:

  每当按下 KEY0 的时候,就会捕获到 SIGIO信号,并且按键获取成功。

  卸载驱动:rmmod asynchronization.ko

总结

  概念:

  其实异步通知就是让驱动设备空出来的时候发送一个信号给用户进程空间,让进程空间知道,哦原来可以使用这个设备了,一般搭配 非阻塞+异步通知使用,效果更佳。

  异步通知的使用方法需要掌握,他们分为驱动和用户进程空间:

  在内核空间中:① 定义 fasync_struct结构体;② 编写fasync函数;③ 最后当设备空出来的时候指定信号 kill_fasync

  在用户空间中:① 编写信号处理函数,并且最后把信号处理函数传给 signal函数;② 本程序进程号告诉内核 fcntl(fd, F_SETOWN, getpid());③ 开启异步通知:flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */ fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */

  代码:其实跟上一节差不太多,在按键按下/松开的时候设置kill_fasync发送信号给用户空间。有个小tip,只要使用了file_operations函数里的函数,都要在 static struct file_operations key_fops = {...}这个函数下写下设备操作函数。测试代码中按照上述用户空间编写即可。

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

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

相关文章

Clickhouse学习笔记(5)—— ClickHouse 副本

Data Replication | ClickHouse Docs 副本的目的主要是保障数据的高可用性&#xff0c;即使一台 ClickHouse 节点宕机&#xff0c;那么也可以从其他服务器获得相同的数据 注意&#xff1a; clickhouse副本机制的实现要基于zookeeperclickhouse的副本机制只适用于MergeTree f…

API SIX系列-服务搭建(一)

APIsix简介 APISIX是一个微服务API网关&#xff0c;具有高性能、可扩展性等优点。它基于nginx&#xff08;openresty&#xff09;、Lua、etcd实现功能&#xff0c;借鉴了Kong的思路。和传统的API网关相比&#xff0c;APISIX具有较高的性能和较低的资源消耗&#xff0c;并且具有…

Lua更多语法与使用

文章目录 目的错误处理元表和元方法垃圾回收协程模块面向对象总结 目的 在前一篇文章&#xff1a; 《Lua入门使用与基础语法》 中介绍了一些基础的内容。这里将继续介绍Lua一些更多的内容。 同样的本文参考自官方手册&#xff1a; https://www.lua.org/manual/ 错误处理 下…

嵌入式养成计划-48----QT--信息管理系统:百川仓储管理

一百二十二、信息管理系统&#xff1a;百川仓储管理 122.1 UI界面 122.2 思路 客户端&#xff1a; 用户权限有两种类型&#xff0c;一种是用户权限&#xff0c;一种是管理员权限&#xff0c;登录时服务器端会根据数据库查询到的此用户名的权限返回不同的结果&#xff0c;客户…

学习c#的第五天

目录 C# 运算符 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 C# 中的运算符优先级 C# 运算符 算术运算符 下表显示了 C# 支持的所有算术运算符。假设变量 A 的值为 10&#xff0c;变量 B 的值为 20&#xff0c;则&#xff1a; 运算符描述实例…

JVM在线分析-解决问题的工具一(jinfo,jmap,jstack)

1. jinfo (base) PS C:\Users\zishi\Desktop> jinfo Usage:jinfo <option> <pid>(to connect to a running process)where <option> is one of:-flag <name> to print the value of the named VM flag #输出对应名称的参数-flag [|-]<n…

ElasticSearch7.x - HTTP 操作 - 文档操作

创建文档(添加数据) 索引已经创建好了,接下来我们来创建文档,并添加数据。这里的文档可以类比为关系型数 据库中的表数据,添加的数据格式为 JSON 格式 向 ES 服务器发 POST 请求 :http://192.168.254.101:9200/shopping/_doc 请求体内容为: {"title":"小…

【Python 千题 —— 基础篇】账号登录

题目描述 题目描述 简易登录系统。你的账号密码分别是 “student”&#xff0c;“123456”&#xff1b;请使用 if-else 设计一个简易登录系统&#xff0c;输入账号密码。登陆成功输出 “Welcome !”&#xff0c;登录失败输出 “Login failed !” 输入描述 输入账号和密码。…

idea配置tomcat参数,防止nvarchar保存韩文、俄文、日文等乱码

描述下我的场景&#xff1a; 数据库服务器在远程机器上&#xff0c;数据库使用的Oracle&#xff0c;字符集是ZHS16GBK&#xff0c;但保存韩文、俄文、日文等字段A的数据类型是nvarchar(120)&#xff0c;而nvarchar使用的是Unicode 编码&#xff0c;有点乱。。 遇到的问题&…

【机器学习范式】监督学习,无监督学习,强化学习, 半监督学习,自监督学习,迁移学习,对比分析+详解与示例代码

目录 1. 监督学习 (Supervised Learning): 2. 无监督学习 (Unsupervised Learning): 3. 强化学习 (Reinforcement Learning): 4. 半监督学习 (Semi-Supervised Learning): 5. 自监督学习 (Self-Supervised Learning): 6. 迁移学习 (Transfer Learning): 7 机器学习范式应…

AI:67-基于深度学习的脱机手写汉字识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

『Linux升级路』基础开发工具——vim篇

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;Linux &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、vim的基本概念 &#x1f4d2;1.1命令模式 &#x1f4d2;1.2插入模式 &…

dcat admin 各种问题

样式问题 如何根据条件给表格数据栏添加背景色 use Illuminate\Support\Collection;protected function grid(){return Grid::make(new BookArticle(), function (Grid $grid) {... 其他代码// Collection的完整路径&#xff1a;Illuminate\Support\Collection;$grid->row…

Spark的执行计划

Spark 3.0 大版本发布&#xff0c;Spark SQL 的优化占比将近 50%。Spark SQL 取代 Spark Core&#xff0c;成为新一代的引擎内核&#xff0c;所有其他子框架如 Mllib、Streaming 和 Graph&#xff0c;都可以共享 Spark SQL 的性能优化&#xff0c;都能从 Spark 社区对于 Spark …

笔记:AI量化策略开发流程-基于BigQuant平台(一)

从本文开始&#xff0c;按照AI策略开发的完整流程&#xff08;共七步&#xff09;&#xff0c;上手在BigQuant平台上快速构建AI策略。本文首先介绍如何使用证券代码模块指定股票范围和数据起止日期。重要的事情说三遍&#xff1a;模块的输入端口有提示需要连线的上游数据类型&a…

魔搭社区LLM模型部署实践, 以ChatGLM3为例(一)

魔搭社区LLM模型部署实践&#xff0c; 以ChatGLM3为 例 本文以ChatGLM3-6B为例&#xff0c; 主要介绍在魔搭社区如何部署LLM&#xff0c; 主要包括如下内容&#xff1a; ● SwingDeploy - 云端部署&#xff0c; 实现零代码一键部署 ● 多端部署 - MAC个人笔记本&#xff0c;…

快速入门安装及使用git与svn的区别常用命令

一、导言 1、什么是svn&#xff1f; SVN是Subversion的简称&#xff0c;是一个集中式版本控制系统。与Git不同&#xff0c;SVN没有分布式的特性。在SVN中&#xff0c;项目的代码仓库位于服务器上&#xff0c;团队成员通过向服务器提交和获取代码来实现版本控制。SVN记录了每个…

Leetcode刷题详解—— 目标和

1. 题目链接&#xff1a;494. 目标和 2. 题目描述&#xff1a; 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可…

时间序列预测实战(十二)DLinear模型实现滚动长期预测并可视化预测结果

官方论文地址->官方论文地址 官方代码地址->官方代码地址 个人修改代码->个人修改的代码已经上传CSDN免费下载 一、本文介绍 本文给大家带来是DLinear模型&#xff0c;DLinear是一种用于时间序列预测&#xff08;TSF&#xff09;的简单架构&#xff0c;DLinear的核…

Ansible自动化运维工具及模块

目录 一、Ansible 1.ansible简介 2、ansible的特性 二、ansible的部署 1&#xff09;管理端安装ansible 2&#xff09;配置主机清单 3&#xff09;配置密钥对验证 三、ansible命令块模块 1&#xff09;command模块 2&#xff09;shell模块 3&#xff09;cron模块 4)…