Linux_kernel内核定时器14

一、内核定时器

        1、内核定时器

使用方法:

        2、系统时钟中断处理函数

        1)更新时间

        2)检查当前时间片是否耗尽

                Linux操作系统是基于时间片轮询的,属于抢占式的内核

        3)jiffies++

        3、基本概念

        1)HZ

                HZ决定了1秒钟产生系统时钟中断的次数

        2)tick

                tick时钟滴答数

                tick = 1 / HZ

                记录了两次系统时钟中断之间的时间间隔

        3)jiffies

                记录了自开机以来,系统时钟中断产生的次数

        4、内核定时器

        1)timer_list

        2)操作系统定时器

                【1】定义变量

struct timer_list led_timer;

                【2】初始化

init_timer(timer)

                【3】启动定时器

void add_timer(struct timer_list *timer)

int mod_timer(struct timer_list *timer, unsigned long expires)

                【4】停止定时器

int del_timer(struct timer_list *timer)

        3)实验
           【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir timer_led

        【3】编写程序

        vim timer_led.c

#include <linux/init.h>
#include <linux/module.h>
#include <mach/platform.h>
#include <linux/gpio.h>
#include <linux/timer.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");// step_2 : define the gpio_b26
#define LED0 (PAD_GPIO_B + 26)
// step_5 : define the global var
struct timer_list led_timer;// step_7 : implement the fun when the time is over
void timer_func(unsigned long data)
{// step_8 : set the LED0 valuegpio_set_value(LED0, data);// step_9 : reverse the LED0led_timer.data = !data;// step_10 : begin count timer, second is 1smod_timer(&led_timer, jiffies + 1 * HZ);
}int __init timer_led_init(void)
{// step_1 : request the gpiogpio_request(LED0, "led0");// step_3 : set output mode, default value is 1, the turn_off ledgpio_direction_output(LED0, 1);// step_4 : initialize the timerinit_timer(&led_timer);// set the intervalue timerled_timer.expires = jiffies + 1 * HZ;// set the fun when the timer overled_timer.function = timer_func;// set the param of timer_funcled_timer.data = 0;// step_6 : turn on the timeradd_timer(&led_timer);return 0;
}void __exit timer_led_exit(void)
{// step_11 : stop the timerdel_timer(&led_timer);// step_12 : destory the gpiogpio_free(LED0);return ;
}module_init(timer_led_init);
module_exit(timer_led_exit);
        【4】编写Makefile

        vim Makefile

obj-m += timer_led.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/rootfsall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.ko $(ROOTFS_PATH)clean:make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】下位机测试

        telnet 192.168.1.6

        insmod timer_led.ko

        【7】流水灯

        vim water_led.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <mach/platform.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("zjd");#define LED0 (PAD_GPIO_B + 26)
#define LED1 (PAD_GPIO_C + 11)
#define LED2 (PAD_GPIO_C + 7)
#define LED3 (PAD_GPIO_C + 12)struct timer_list led_timer;
unsigned int led[] = {LED0, LED1, LED2, LED3};void led_function(unsigned long data)
{static int pre = 0;static int next = 0;led_timer.data = !data;gpio_set_value(led[pre], led_timer.data);next = (pre + 1) % (sizeof(led) / sizeof(led[0]));led_timer.data = !led_timer.data;gpio_set_value(led[next], led_timer.data);pre = next;mod_timer(&led_timer, jiffies + HZ / 2);
}int __init led_drv_init(void)
{gpio_request(LED0, "led0");gpio_request(LED1, "led1");gpio_request(LED2, "led2");gpio_request(LED3, "led3");gpio_direction_output(LED0, 1);gpio_direction_output(LED1, 1);gpio_direction_output(LED2, 1);gpio_direction_output(LED3, 1);init_timer(&led_timer);led_timer.expires = jiffies + HZ / 2;led_timer.function = led_function;led_timer.data = 0;add_timer(&led_timer);return 0;
}void __exit led_drv_exit(void)
{del_timer(&led_timer);gpio_free(LED0);gpio_free(LED1);gpio_free(LED2);gpio_free(LED3);return ;
}module_init(led_drv_init);
module_exit(led_drv_exit);

二、内核的竞态与并发

        1、介绍

        PC机上的串口设备是一种独占式访问的设备(只能有一个进程进行访问),独占式操作的实现是在串口的驱动程序中实现的,我们希望按键设备只允许一个进程进行访问。

        2、基础实验

          【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir single_btn

        【3】编写程序

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>MODULE_LICENSE("GPL");//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);return 0;
}//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);return 0;
}//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {.owner = THIS_MODULE,.open = btn_open,.release = btn_close
};int __init btn_drv_init(void)
{//[1]申请设备号alloc_chrdev_region(&dev, 0, 1, "btn");//[3]初始化 cdev 对象cdev_init(&btn_cdev, &btn_fops);//[5]注册 cdev 对象到内核中cdev_add(&btn_cdev, dev, 1);//[6]自动创建设备文件(创建树枝)btn_cls = class_create(THIS_MODULE, "mybuttons");//(创建果实)device_create(btn_cls, NULL, dev, NULL, "buttons");return 0;
}void __exit btn_drv_exit(void)
{//[8]销毁设备文件(销毁果实)device_destroy(btn_cls, dev);//[9]销毁设备文件依赖的设备树(销毁树枝)class_destroy(btn_cls);//[a]注销 cdev 对象cdev_del(&btn_cdev);//[b]释放设备号unregister_chrdev_region(dev, 1);return ;
}module_init(btn_drv_init);
module_exit(btn_drv_exit);
        【4】编写Makefile

        vim Makefile

obj-m += signal_btn.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.ko $(ROOTFS_PATH)clean:make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】编写应用层程序

        mkdir test

        cd test

        vim btn_test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(void)
{int fd = open("/dev/buttons", O_RDONLY);if(fd < 0){perror("open()");return -1;}printf("open success!\n");while (1) {;}close(fd);return 0;
}

     vim Makefile

SRC=btn_test.c
OBJ=btn_testARM_COMPILE=arm-cortex_a9-linux-gnueabi-
GCC=gccROOTFS_PATH=/nfs_share/_installall:$(ARM_COMPILE)$(GCC) $(SRC) -o $(OBJ)cp $(OBJ) $(ROOTFS_PATH)clean:rm -rf $(OBJ)
        【7】编译工程

        make

        【8】下位机测试

        insmod single_btn.ko

        ./btn_test

        telnet 192.168.1.6

        ./btn_test

        【9】总结

        我们发现,这两个进程都可以打开这个设备文件,这样与我们的需求不符,我们希望在同一时刻,只能有一个进程访问这个设备文件。

        两个进程属于竞争关系,都来抢设备文件这个资源,如何做到同一时刻只能一个进程对设备文件进行访问?(竞态)

        3、全局变量

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>#define ON	1
#define OFF	0MODULE_LICENSE("GPL");//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;int  btn_switch = OFF;//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);if (OFF == btn_switch)btn_switch = ON;elsereturn -EAGAIN;return 0;
}//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);if (ON == btn_switch)btn_switch = OFF;elsereturn -EAGAIN;return 0;
}//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {.owner = THIS_MODULE,.open = btn_open,.release = btn_close
};int __init btn_drv_init(void)
{//[1]申请设备号alloc_chrdev_region(&dev, 0, 1, "btn");//[3]初始化 cdev 对象cdev_init(&btn_cdev, &btn_fops);//[5]注册 cdev 对象到内核中cdev_add(&btn_cdev, dev, 1);//[6]自动创建设备文件(创建树枝)btn_cls = class_create(THIS_MODULE, "mybuttons");//(创建果实)device_create(btn_cls, NULL, dev, NULL, "buttons");return 0;
}void __exit btn_drv_exit(void)
{//[8]销毁设备文件(销毁果实)device_destroy(btn_cls, dev);//[9]销毁设备文件依赖的设备树(销毁树枝)class_destroy(btn_cls);//[a]注销 cdev 对象cdev_del(&btn_cdev);//[b]释放设备号unregister_chrdev_region(dev, 1);return ;
}module_init(btn_drv_init);
module_exit(btn_drv_exit);

注意:

        由于cpu调度策略的不确定型,这种使用全局变量的方法解决竞态是不靠谱的。

        4、竞态

                1)简介

        竞态就是竞争的状态,竞争的是共享资源,在硬件设备的角度说,所谓的共享资源指的是:UART、LCD、声卡、网卡

                2)临界区

        访问共享资源的代码就是临界区

                3)产生竞态的原因

        【1】多核处理器

        【2】多任务之间的抢占

        【3】中断和任务之间的抢占

        【4】中断和中断之间的抢占(中断优先级,优先级编号越小,优先级越高)

                4)解决竞态的策略

        【1】中断屏蔽

        【2】原子操作

                位原子操作

                整型原子操作

        【3】自旋锁

        【4】信号量

                5)中断屏蔽

可以解决【2】【3】【4】产生竞态的原因

        【1】中断操作

local_irq_enable()        // 中断使能

local_irq_disable()        // 中断失能

        【2】执行代码

        在中断失能和中断使能之间执行临界区的资源

注意:

        关中断的时间要特别的短,一但过长,内核将直接崩溃,因为Linux内核中有很多重要的机制是靠中断实现的。写驱动时,不建议用这种方式。

                6)原子操作

        原子操作是不可分割的,要么都执行,要么不执行。在执行完毕之前不会被任何其他的任务或事件中断。

        【1】位原子操作

        static inline void set_bit(int nr, volatile unsigned long *addr)

        static inline void clear_bit(int nr, volatile unsigned long *addr)

        static inline int test_and_set_bit(int nr, volatile unsigned long *addr)

        static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)

        static inline void change_bit(int nr, volatile unsigned long *addr)

        【2】整型原子操作

atomic_t        // 整型原子结构体

static inline void atomic_add(int i, atomic_t *v)        // 给整型原子变量 + i

static inline void atomic_sub(int i, atomic_t *v)        // 给整型原子变量 - i

static inline void atomic_inc(atomic_t *v)        // 给整型原子变量 + 1

static inline void atomic_dec(atomic_t *v)        // 给整型原子变量 - 1

        【3】实验

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>#define ON	1
#define OFF	0MODULE_LICENSE("GPL");//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;// define global variable
//int  btn_switch = OFF;
// define int atomic_t variable
atomic_t btn_tv;//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);
#if 0if (OFF == btn_switch)btn_switch = ON;elsereturn -EAGAIN;
#elseif (!atomic_dec_and_test(&btn_tv)) {atomic_inc(&btn_tv);return -EAGAIN;}
#endifreturn 0;
}//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);
#if 0if (ON == btn_switch)btn_switch = OFF;elsereturn -EAGAIN;
#elseatomic_inc(&btn_tv);
#endifreturn 0;
}//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {.owner = THIS_MODULE,.open = btn_open,.release = btn_close
};int __init btn_drv_init(void)
{//[1]申请设备号alloc_chrdev_region(&dev, 0, 1, "btn");//[3]初始化 cdev 对象cdev_init(&btn_cdev, &btn_fops);//[5]注册 cdev 对象到内核中cdev_add(&btn_cdev, dev, 1);//[6]自动创建设备文件(创建树枝)btn_cls = class_create(THIS_MODULE, "mybuttons");//(创建果实)device_create(btn_cls, NULL, dev, NULL, "buttons");// initialize int atomic_t variableatomic_set(&btn_tv, 1);return 0;
}void __exit btn_drv_exit(void)
{//[8]销毁设备文件(销毁果实)device_destroy(btn_cls, dev);//[9]销毁设备文件依赖的设备树(销毁树枝)class_destroy(btn_cls);//[a]注销 cdev 对象cdev_del(&btn_cdev);//[b]释放设备号unregister_chrdev_region(dev, 1);return ;
}module_init(btn_drv_init);
module_exit(btn_drv_exit);

当上位机对btn设备文件访问完毕时

注意:

        由于对整型变量的操作都是原子化的,所以这种方案是可行的

        7)自旋锁

        【1】核心数据结构

spinlock_t

        【2】使用方法

                1】获取自旋锁

                2】执行临界区代码,访问共享资源

                3】释放自旋锁

        【3】特点

                1】自旋锁只有一个持有单元

                2】若试图获取一个已经被其它执行单元持有的自旋锁,将会阻塞等待,直到原持有方释放自旋锁。

        【4】使用步骤

                 【1】定义变量

spinlock_t btn_lock;

                【2】初始化

spin_lock_init(_lock)

                【3】获取锁

static inline void spin_lock(spinlock_t *lock)

                【4】释放自旋锁

static inline void spin_unlock(spinlock_t *lock)

                【5】实验

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>#define ON	1
#define OFF	0// controller the different ways though to define value
//#define ATOMIC
#define SPINLOCKMODULE_LICENSE("GPL");//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;#if defined(ATOMC)
// define int atomic_t variable
atomic_t btn_tv;
#elif defined(SPINLOCK)
// define global variable the sources of compare
int  btn_switch = OFF;
// define the reverse lock
struct spinlock btn_lock;
#endif//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);
#if defined(SPINLOCK)if (OFF == btn_switch) {spin_lock(&btn_lock);btn_switch = ON;spin_unlock(&btn_lock);}elsereturn -EAGAIN;#elif defined(ATOMIC)if (!atomic_dec_and_test(&btn_tv)) {atomic_inc(&btn_tv);return -EAGAIN;}
#endifreturn 0;
}//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);
#if defined(SPINLOCK)spin_lock(&btn_lock);btn_switch = OFF;spin_unlock(&btn_lock);
#elif defined(ATOMIC)atomic_inc(&btn_tv);
#endifreturn 0;
}//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {.owner = THIS_MODULE,.open = btn_open,.release = btn_close
};int __init btn_drv_init(void)
{//[1]申请设备号alloc_chrdev_region(&dev, 0, 1, "btn");//[3]初始化 cdev 对象cdev_init(&btn_cdev, &btn_fops);//[5]注册 cdev 对象到内核中cdev_add(&btn_cdev, dev, 1);//[6]自动创建设备文件(创建树枝)btn_cls = class_create(THIS_MODULE, "mybuttons");//(创建果实)device_create(btn_cls, NULL, dev, NULL, "buttons");
#if defined(ATOMIC)// initialize int atomic_t variableatomic_set(&btn_tv, 1);
#elif defined(SPINLOCK)// initialize reverse lock variablespin_lock_init(&btn_lock);
#endifreturn 0;
}void __exit btn_drv_exit(void)
{//[8]销毁设备文件(销毁果实)device_destroy(btn_cls, dev);//[9]销毁设备文件依赖的设备树(销毁树枝)class_destroy(btn_cls);//[a]注销 cdev 对象cdev_del(&btn_cdev);//[b]释放设备号unregister_chrdev_region(dev, 1);return ;
}module_init(btn_drv_init);
module_exit(btn_drv_exit);

注意:

        1】持有自旋锁的事件要尽量短,临界区中不应该出现引起阻塞或休眠的函数

                会导致内核吐核

        2】避免死锁情况的发生

                static inline int spin_trylock(spinlock_t *lock)        // 获取不成功,返回错误

        8)信号量

当临界区的代码需要长时间执行时,我们可以使用信号量机制

         【1】核心数据结构

struct semaphore

        【2】使用方法

                1】获取信号量

                2】执行临界区代码,访问共享资源

                3】释放信号量

        【3】特点

                1】信号量可以持有多个单元

                2】获取信号量的本质就是引用计数 - 1(count--

                3】释放信号量的本质就是引用计数 + 1(count++

                4】最小减到0,如果到0了,再去获取信号量会失败,睡眠等待

        【4】使用步骤

                 【1】定义信号量

struct semaphore btn_sem;

                【2】初始化

static inline void sema_init(struct semaphore *sem, int val)

                【3】获取信号量

        1】void down(struct semaphore *sem)

        2int down_interruptible(struct semaphore *sem)

        3】int down_killable(struct semaphore *sem)

                【4】释放信号量

void up(struct semaphore *sem)

                【5】实验

        vim single_btn.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/delay.h>// only one sources
#define SOURCE_NUM	1
#define ON	1
#define OFF	0// controller the different ways though to define value
//#define ATOMIC
//#define SPINLOCK
#define SEMAPHOREtypedef int source_t;MODULE_LICENSE("GPL");//[2]定义全局变量----设备号
dev_t dev;
//[4]定义全局变量----cdev对象
struct cdev btn_cdev;
//[7]定义全局指针----class类
struct class *btn_cls = NULL;#if defined(ATOMC)
// define int atomic_t variable
atomic_t btn_tv;
#elif defined(SPINLOCK)
// define global variable the sources of compare
int  btn_switch = OFF;
// define the reverse lock
struct spinlock btn_lock;
#elif defined(SEMAPHORE)
// define global variable the sources of compare
source_t src_num = SOURCE_NUM;
// define the semaphore
struct semaphore btn_sem;
#endif//[d]按照原型实现btn_open
int btn_open(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);
#if defined(SEMAPHORE)down(&btn_sem);mdelay(6000);	// delay 6sif (!src_num--) {src_num++;up(&btn_sem);return -EAGAIN;}up(&btn_sem);#elif defined(SPINLOCK)if (OFF == btn_switch) {spin_lock(&btn_lock);msleep(100);btn_switch = ON;spin_unlock(&btn_lock);}elsereturn -EAGAIN;#elif defined(ATOMIC)if (!atomic_dec_and_test(&btn_tv)) {atomic_inc(&btn_tv);return -EAGAIN;}
#endifreturn 0;
}//[e]按照原型实现btn_close
int btn_close(struct inode *inode, struct file *fp)
{printk("<0>" "enter : %s\n", __func__);
#if defined(SEMAPHORE)up(&btn_sem);src_num++;
#elif defined(SPINLOCK)spin_lock(&btn_lock);btn_switch = OFF;spin_unlock(&btn_lock);
#elif defined(ATOMIC)atomic_inc(&btn_tv);
#endifreturn 0;
}//[c]定义全局变量---操作函数集合
struct file_operations btn_fops = {.owner = THIS_MODULE,.open = btn_open,.release = btn_close
};int __init btn_drv_init(void)
{//[1]申请设备号alloc_chrdev_region(&dev, 0, 1, "btn");//[3]初始化 cdev 对象cdev_init(&btn_cdev, &btn_fops);//[5]注册 cdev 对象到内核中cdev_add(&btn_cdev, dev, 1);//[6]自动创建设备文件(创建树枝)btn_cls = class_create(THIS_MODULE, "mybuttons");//(创建果实)device_create(btn_cls, NULL, dev, NULL, "buttons");
#if defined(SEMAPHORE)// initialize semaphore variablesema_init(&btn_sem, 1);
#elif defined(ATOMIC)// initialize int atomic_t variableatomic_set(&btn_tv, 1);
#elif defined(SPINLOCK)// initialize reverse lock variablespin_lock_init(&btn_lock);
#endifreturn 0;
}void __exit btn_drv_exit(void)
{//[8]销毁设备文件(销毁果实)device_destroy(btn_cls, dev);//[9]销毁设备文件依赖的设备树(销毁树枝)class_destroy(btn_cls);//[a]注销 cdev 对象cdev_del(&btn_cdev);//[b]释放设备号unregister_chrdev_region(dev, 1);return ;
}module_init(btn_drv_init);
module_exit(btn_drv_exit);

        9)区别

在解决竞态问题时:

        【1】信号量保护的临界区对执行的时间的长短没有要求

                 自旋锁保护的临界区要求执行速度尽量快

                 执行时间一旦过长,造成另外一个进程获取锁不成功,内核就会崩溃

                 此外,多核系统时,自旋锁原地自旋,系统性能下降

        【2】获取信号量不成功,进程睡眠等待

                 获取自旋锁不成功,进程原地自旋

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

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

相关文章

OCP迎来新版本,让OceanBase的运维管理更高效

近期&#xff0c;OceanBase的OCP发布了新版本&#xff0c;全面支持 OceanBase 内核 4.3.2 及更低版本。新版本针对基础运维、性能监控、运维配置、外部集成等多个方面实现了 20余项的优化及强化措施&#xff0c;增强产品的易用性和稳定性&#xff0c;从而帮助用户更加高效地管理…

中国地级市生态韧性数据及城市生态韧性数据(2000-2022年)

一测算方式&#xff1a; 参考C刊《管理学刊》楚尔鸣&#xff08;2023&#xff09;老师的做法&#xff0c;城市生态韧性主要衡量一个城市在面临生态环境系统压力或突发冲击时&#xff0c;约束污染排放、维护生态环境状态和治理能力提升的综合水平。 参考郭海红和刘新民的研究&a…

Redis持久化机制(RDBAOF详解)

目录 一、Redis持久化介绍二、Redis持久化方式1、RDB持久化(1) 介绍(2) RDB持久化触发机制(3) RDB优点和缺点(4) RDB流程 2、AOF(append only file)持久化(1) 介绍(2) AOF优点和缺点(3) AOF文件重写(4) AOF文件重写流程 三、AOF和RDB持久化注意事项 一、Redis持久化介绍 Redis…

【小工具分享】下载保存指定网页的所有图片

一、保存百度首页所有的图片 先看一下保存的图片情况 二、思路 1、打开网页 2、获取所有图片 3、依次下载保存图片到指定路径 三、完整代码 from selenium import webdriver from selenium.webdriver.common.by import By b webdriver.Firefox() import urllib.request…

C++系统教程004-数据类型(03)

一 .变量 变量是指在程序运行期间其值可以发生改变的量。每个变量都必须有一个名称作为唯一的标识&#xff0c;且具有一个特定的数据类型。变量使用之前&#xff0c;一定要先进行声明或定义。 1.变量的声明和定义 C中&#xff0c;变量声明是指为变量提供一个名称&#xff0c…

嵌入式面试——FreeRTOS篇(七) 软件定时器

本篇为&#xff1a;FreeRTOS 软件定时器篇 一、软件定时器的简介 1、定时器介绍 答&#xff1a; 定时器&#xff1a;从指定的时刻开始&#xff0c;经过一个指定时间&#xff0c;然后触发一个超时事件&#xff0c;用户可以自定义定时器周期。 硬件定时器&#xff1a;芯片本…

基于差分进化灰狼混合优化的SVM(DE-GWO-SVM)数据预测算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 DE优化 4.2 GWO优化 5.完整程序 1.程序功能描述 基于差分进化灰狼混合优化的SVM(DE-GWO-SVM)数据预测算法matlab仿真&#xff0c;对比SVM和GWO-SVM。 2.测试软件版本以及运行结果展示…

论文阅读:Split-Aperture 2-in-1 Computational Cameras (二)

Split-Aperture 2-in-1 Computational Cameras (一) Coded Optics for High Dynamic Range Imaging 接下来&#xff0c;文章介绍了二合一相机在几种场景下的应用&#xff0c;首先是高动态范围成像&#xff0c;现有的快照高动态范围&#xff08;HDR&#xff09;成像工作已经证…

自然语言处理(NLP)论文数量的十年趋势:2014-2024

引言 近年来&#xff0c;自然语言处理&#xff08;NLP&#xff09;已成为人工智能&#xff08;AI&#xff09;和数据科学领域中的关键技术之一。随着数据规模的不断扩大和计算能力的提升&#xff0c;NLP技术从学术研究走向了广泛的实际应用。通过观察过去十年&#xff08;2014…

处理 Vue3 中隐藏元素刷新闪烁问题

一、问题说明 页面刷新&#xff0c;原本隐藏的元素会一闪而过。 效果展示&#xff1a; 页面的导航栏通过路由跳转中携带的 meta 参数控制导航栏的 显示/隐藏&#xff0c;但在实践过程中发现&#xff0c;虽然元素隐藏了&#xff0c;但是刷新页面会出现闪烁的问题。 项目源码&…

ros2:从github上下载源码进行编译

首先&#xff0c;创建工作空间 # 1. 递归创建工作空间目录 mkdir -p catkin_ws/src # 2. 进入src目录 cd catkin_ws/src然后如果你没有安装git&#xff0c;需要 sudo apt install git然后输入。 git clone https://github.com/6-robot/wpr_simulation.git这时候&#xff0c;…

MYSQL 常见锁机制详解,常见锁问题排查及分析

1&#xff0c;锁分类 锁冲突是影响数据库性能的重要指标&#xff0c;本章节介绍MYSQL常见锁&#xff0c;及各种说的常用示例&#xff0c;mysql锁的分类如下&#xff1a; 从操作类型分类&#xff1a;读锁、写锁&#xff1b; 从操作粒度分类&#xff1a;表锁、页锁、行锁&#x…

文献阅读Prov-GigaPath模型--相关知识点罗列

文章链接&#xff1a;A whole-slide foundation model for digital pathology from real-world data | NatureDigital pathology poses unique computational challenges, as a standard gigapixel slide may comprise tens of thousands of image tiles1–3. Prior models hav…

Java中的二维数组

二维数组 使用方式1&#xff1a;动态初始化1.语法&#xff1a;2.比如&#xff1a;3.二维数组在内存的存在形式 使用方式2&#xff1a;动态初始化使用方法3&#xff1a;动态初始化--列数不确定使用方式4&#xff1a;静态初始化1.定义2.使用 使用方式1&#xff1a;动态初始化 1.…

HiRT | 异步控制策略,告别VLA时延问题

论文&#xff1a;HiRT: Enhancing Robotic Control with Hierarchical Robot Transformers 前言&#xff1a;HiRT 通过异步处理的策略&#xff0c;将 VLM 作为低频慢思考过程&#xff0c;将轻量的动作策略模型作为高频快响应过程 &#xff0c;以此解决 VLA 驱动带来的控制时延问…

RNN经典案例——构建人名分类器

RNN经典案例——人名分类器 一、数据处理1.1 去掉语言中的重音标记1.2 读取数据1.3 构建人名类别与人名对应关系字典1.4 将人名转换为对应的onehot张量 二、构建RNN模型2.1 构建传统RNN模型2.2 构建LSTM模型2.3 构建GRU模型 三、构建训练函数并进行训练3.1 从输出结果中获得指定…

TON生态小游戏开发:推广、经济模型与UI设计的建设指南

随着区块链技术的快速发展&#xff0c;基于区块链的Web3游戏正引领行业变革。而TON生态小游戏&#xff0c;借助Telegram庞大的用户基础和TON&#xff08;The Open Network&#xff09;链上技术&#xff0c;已成为这一领域的明星之一。国内外开发者正迅速涌入&#xff0c;开发和…

基于SpringBoot+Vue的船舶监造系统(带1w+文档)

基于SpringBootVue的船舶监造系统(带1w文档) 基于SpringBootVue的船舶监造系统(带1w文档) 大概在20世纪90年代&#xff0c;我国才开始研发船舶监造系统&#xff0c;与一些发达国家相比&#xff0c;系统研发起步比较晚。当时的计算机技术刚开始发展起来&#xff0c;国家经济力量…

SEO(搜索引擎优化)指南

SEO&#xff08;Search Engine Optimization&#xff09;是通过优化网站内容、结构和外部链接&#xff0c;提升网页在搜索引擎结果中的排名&#xff0c;从而增加网站流量的过程。SEO 涉及多个层面&#xff0c;包括技术 SEO、内容优化、外部链接建设等。以下是 SEO 的核心优化策…

京东零售数据湖应用与实践

作者&#xff1a;陈洪健&#xff1a;京东零售大数据架构师&#xff0c;深耕大数据 10 年&#xff0c;2019 年加入京东&#xff0c;主要负责 OLAP 优化、大数据传输工具生态、流批一体、SRE 建设。 当前企业数据处理广泛采用 Lambda 架构。Lambda 架构的优点是保证了数据的完整性…