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.mzph.cn/news/138998.shtml

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

相关文章

[工业自动化-14]:西门子S7-15xxx编程 - 软件编程 - STEP7 TIA博途是全集成自动化软件TIA portal快速入门

目录 一、TIA博途是全集成自动化软件TIA portal快速入门 1.1 简介 1.2 软件常用界面 1.3 软件安装的电脑硬件要求 1.4 入口 1.5 主界面 二、PLC软件编程包含哪些内容 2.1 概述 2.2 电机运动控制 一、TIA博途是全集成自动化软件TIA portal快速入门 1.1 简介 Siemens …

openssl研发之base64编解码实例

一、base64编码介绍 Base64编码是一种将二进制数据转换成ASCII字符的编码方式。它主要用于在文本协议中传输二进制数据&#xff0c;例如电子邮件的附件、XML文档、JSON数据等。 Base64编码的特点如下&#xff1a; 字符集&#xff1a; Base64编码使用64个字符来表示二进制数据…

SparkSQL语法优化

SparkSQL在整个执行计划处理的过程中&#xff0c;使用了Catalyst 优化器。 1 基于RBO的优化 在Spark 3.0 版本中&#xff0c;Catalyst 总共有 81 条优化规则&#xff08;Rules&#xff09;&#xff0c;分成 27 组&#xff08;Batches&#xff09;&#xff0c;其中有些规则会被归…

uniapp——项目02

分类 创建cate分支 渲染分类页面的基本结构 效果页面,包含左右两个滑动区. 利用提供的api获取当前设备的信息。用来计算窗口高度。可食用高度就是屏幕高度减去上下导航栏的高度。 最终效果: 每一个激活项都特殊背景色&#xff0c;又在尾部加了个红条一样的东西。 export d…

python3GUI--QQ音乐By:PyQt5(附下载地址)

文章目录 一&#xff0e;前言二&#xff0e;展示0.播放页1.主界面1.精选2.有声电台3.排行4.歌手5.歌单 2.推荐3.视频1.视频2.分类3.视频分类 4.雷达5.我喜欢1.歌曲2.歌手 6.本地&下载7.最近播放8.歌单1.一般歌单2.自建歌单3.排行榜 9.其他1.搜索词推荐2.搜索结果 三&#x…

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

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

智慧城市建设解决方案分享【完整】

文章目录 第1章 前言第2章 智慧城市建设的背景2.1 智慧城市的发展现状2.2 智慧城市的发展趋势 第3章 智慧城市“十二五”规划要点3.1 国民经济和社会发展“十二五”规划要点3.2 “十二五”信息化发展规划要点 第4章 大数据&#xff1a;智慧城市的智慧引擎4.1 大数据技术—智慧城…

智慧城市照明为城市节能降耗提供支持继电器开关钡铼S270

智慧城市照明&#xff1a;为城市节能降耗提供支持——以钡铼技术S270继电器开关为例 随着城市化进程的加速&#xff0c;城市照明系统的需求也日益增长。与此同时&#xff0c;能源消耗和环境污染问题日益严重&#xff0c;使得城市照明的节能减排成为重要议题。智慧城市照明系统…

Linux技能篇-yum源搭建(本地源和公网源)

文章目录 前言一、yum源是什么&#xff1f;二、使用镜像搭建本地yum源1.搭建临时仓库第一步&#xff1a;挂载系统ios镜像到虚拟机第二步&#xff1a;在操作系统中挂载镜像第三步&#xff1a;修改yum源配置文件 2.搭建本地仓库第一步&#xff1a;搭建临时yum源来安装httpd并做文…

javaEE案例,前后端交互,计算机和用户登录

加法计算机,前端的代码如下 : 浏览器访问的效果如图 : 后端的代码如下 再在浏览器进行输入点击相加,就能获得结果 开发中程序报错,如何定位问题 1.先定位前端还是后端(通过日志分析) 1)前端 : F12 看控制台 2)后端 : 接口,控制台日志 举个例子: 如果出现了错误,我们就在后端…

如何查看网站的https的数字证书

如题 打开Chrome浏览器&#xff0c;之后输入想要抓取https证书的网址&#xff0c;此处以知乎为例点击浏览器地址栏左侧的锁的按钮&#xff0c;如下图 点击“连接是安全的”选项&#xff0c;如下图 点击“证书有效”选项卡&#xff0c;如下图 查看基本信息和详细信息 点击详细信…

C/C++数字判断 2021年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C数字判断 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C数字判断 2021年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 输入一个字符&#xff0c;如何输入的字符是数字&#x…

Spark的转换算子和操作算子

1 Transformation转换算子 1.1 Value类型 1&#xff09;创建包名&#xff1a;com.shangjack.value 1.1.1 map()映射 参数f是一个函数可以写作匿名子类&#xff0c;它可以接收一个参数。当某个RDD执行map方法时&#xff0c;会遍历该RDD中的每一个数据项&#xff0c;并依次应用f函…

Mac下eclipse配置JDK

一、配置JDK&#xff0c;需要电脑下载Java并且配置环境 Mac环境配置&#xff08;Java&#xff09;----使用bash_profile进行配置&#xff08;附下载地址&#xff09; (1)、左上角找到“Eclipse”-->“Preferences...” (2)、找到“Java”-->“Installde JREs”-->界…

S7-1200PLC和SMART PLC开放式以太网通信(UDP双向通信)

S7-1200PLC的以太网通信UDP通信相关介绍还可以参考下面文章链接: 博途PLC开放式以太网通信TRCV_C指令应用编程(运动传感器UDP通信)-CSDN博客文章浏览阅读2.8k次。博途PLC开放式以太网通信TSENG_C指令应用,请参看下面的文章链接:博途PLC 1200/1500PLC开放式以太网通信TSEND_…

AI:73-结合语法知识的神经机器翻译研究

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

HTTPS的工作流程

. HTTPS是什么&#xff1f; https是应用层中的一个协议&#xff0c;是在http协议的基础上引入的一个加密层。 为什么需要HTTPS 由于http协议内容都是按照文本的方式明文传输的&#xff0c;这就导致传输过程中会出现一些被篡改的情况。运营商劫持事件最开始百度&#xff0c;…

云栖大会丨桑文锋:打造云原生数字化客户经营引擎

近日&#xff0c;2023 云栖大会在杭州举办。今年云栖大会回归了 2015 的主题&#xff1a;「计算&#xff0c;为了无法计算的价值」。神策数据创始人 & CEO 桑文锋受邀出席「生态产品与伙伴赋能」技术主题&#xff0c;并以「打造云原生数字化客户经营引擎」为主题进行演讲。…

CSS特效006:绘制不断跳动的心形

css实战中&#xff0c;怎么绘制不断跳动的心形呢&#xff1f; 绘图的时候主要用到了transform: rotate(-45deg); transform-origin: 0 100%; transform: rotate(45deg); transform-origin: 100% 100%; 动画使用keyframes 时间上为infinite。 效果图 源代码 /* * Author: 大剑…

迅为龙芯3A5000主板,支持PCIE 3.0、USB 3.0和 SATA 3.0显示接口2 路、HDMI 和1路 VGA,可直连显示器

性能强 采用全国产龙芯3A5000处理器&#xff0c;基于龙芯自主指令系统 (LoongArch)的LA464微结构&#xff0c;并进一步提升频率&#xff0c;降低功耗&#xff0c;优化性能。 桥片 桥片采用龙芯 7A2000&#xff0c;支持PCIE 3.0、USB 3.0和 SATA 3.0显示接口2 路、HDMI 和1路 …