linux watchdog 子系统

目录

  • 一、watchdog 子系统
  • 二、关键数据结构
    • 2.1 watchdog_device
    • 2.2 watchdog_ops
    • 2.3 watchdog_info
  • 三、重要流程
    • 3.1 watchdog 初始化
    • 3.2 watchdog 设备注册
    • 3.3 watchdog 设备文件操作函数
    • 3.4 watchdog 喂狗
      • 用户空间 watchdog(busybox)
      • 内核空间喂狗
      • 疑问 or bug?
    • 3.5 watchdog pretimeout 和 governor
  • 四、softdog
    • 4.1 softdog_init
    • 4.2 softdog_ping
    • 4.3 softdog_fire

一、watchdog 子系统

linux 中 watchdog 子系统是用于防止系统发生长时间故障、将系统从死循环或者死锁等异常状态中退出并重启的一种机制。

linux 内核支持基于 hrtimer 的 softdog 和基于硬件的硬件看门狗,创建 /dev/watchdog* 设备文件与用户空间程序进行交互。

用户空间的 watchdog 程序,会通过/dev/watchdog* 设备进行周期性喂狗。
在这里插入图片描述

二、关键数据结构

2.1 watchdog_device

watchdog_device 表示一个 watchdog 设备,保存了 watchdog 的各种操作参数和操作 ops。

struct watchdog_device {int id;struct device *parent;const struct attribute_group **groups;    // 创建watchdog device的sysfs属性列表const struct watchdog_info *info;    // 记录watchdog的特性,例如WDIOF_SETTIMEOUT WDIOF_KEEPALIVEPINGconst struct watchdog_ops *ops;    // watchdog 操作接口const struct watchdog_governor *gov;   //  pretimeout使用的governorunsigned int bootstatus;    // boot 时 watchdog 的状态unsigned int timeout;unsigned int pretimeout;unsigned int min_timeout;unsigned int max_timeout;unsigned int min_hw_heartbeat_ms;unsigned int max_hw_heartbeat_ms;struct notifier_block reboot_nb;struct notifier_block restart_nb;void *driver_data;struct watchdog_core_data *wd_data;unsigned long status;    // watchdog 状态,active\running……
/* Bit numbers for status flags */
#define WDOG_ACTIVE		0	/* Is the watchdog running/active */
#define WDOG_NO_WAY_OUT		1	/* Is 'nowayout' feature set ? */
#define WDOG_STOP_ON_REBOOT	2	/* Should be stopped on reboot */
#define WDOG_HW_RUNNING		3	/* True if HW watchdog running */
#define WDOG_STOP_ON_UNREGISTER	4	/* Should be stopped on unregister */struct list_head deferred;
};

2.2 watchdog_ops

watchdog_ops 定义了 watchdog device 操作函数。

struct watchdog_ops {struct module *owner;/* mandatory operations */int (*start)(struct watchdog_device *);int (*stop)(struct watchdog_device *);/* optional operations */int (*ping)(struct watchdog_device *);unsigned int (*status)(struct watchdog_device *);int (*set_timeout)(struct watchdog_device *, unsigned int);int (*set_pretimeout)(struct watchdog_device *, unsigned int);unsigned int (*get_timeleft)(struct watchdog_device *);int (*restart)(struct watchdog_device *, unsigned long, void *);long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
};

2.3 watchdog_info

struct watchdog_info {__u32 options;		/* Options the card/driver supports */__u32 firmware_version;	/* Firmware version of the card */__u8  identity[32];	/* Identity of the board */
};

三、重要流程

3.1 watchdog 初始化

watchdog_init 接口对 watchdog 子系统做初始化:

watchdog_init->watchdog_dev_init->kthread_create_worker(0, "watchdogd")->sched_setscheduler  // 设置watchdog_kworker的线程调度策略为SCHED_FIFO, 优先级MAX_RT_PRIO - 1->class_register->alloc_chrdev_region->watchdog_deferred_registration // 如果watchdog设备驱动早于watchdog_init则将设备加入到wtd_deferred_reg_list,此处集中进行列表上watchdog设备注册。

3.2 watchdog 设备注册

devm_watchdog_register_device 接口用于注册 watchdog 设备:

int devm_watchdog_register_device(struct device *dev, struct watchdog_device *wdd)-> watchdog_register_device(wdd);-> __watchdog_register_device-> // 参数检查-> watchdog_check_min_max_timeout-> watchdog_dev_register-> watchdog_cdev_register-> kthread_init_work // 创建pingwork任务-> hrtimer_init // 创建一个高精度定时器,定时器结束后调用的接口是 watchdog_timer_expired-> misc_register  // if (wdd->id == 0),如果是注册第一个watchdog,则注册misc设备-> device_initialize-> cdev_init-> cdev_device_add // 创建并初始化watchdog设备,设备名为/dev/watchdog*-> watchdog_register_pretimeout-> watchdog_reboot_notifier // if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status))-> register_restart_handler // if (wdd->ops->restart)

3.3 watchdog 设备文件操作函数

watchdog创建的misc 设备和 cdev 的文件操作函数都是 watchdog_fops

static const struct file_operations watchdog_fops = {.owner		= THIS_MODULE,.write		= watchdog_write,.unlocked_ioctl	= watchdog_ioctl,.open		= watchdog_open,.release	= watchdog_release,
};static struct miscdevice watchdog_miscdev = {.minor		= WATCHDOG_MINOR,.name		= "watchdog",.fops		= &watchdog_fops,
};

watchdog_open 接口中调用 watchdog_start 接口,start watchdog 和更新内核喂狗定时器。

static int watchdog_open(struct inode *inode, struct file *file)-> watchdog_start(wdd);-> wdd->ops->start(wdd)-> watchdog_update_worker // 更新喂狗定时器-> stream_open(inode, file);

watchdog_write 接口实现喂狗功能,写任意值都能喂狗; “V” 是 magic 字符,写 “V” 之后使能 watchdog 的魔法关闭功能:

static ssize_t watchdog_write(struct file *file, const char __user *data,size_t len, loff_t *ppos)->set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) // if (c == 'V')-> watchdog_ping(wdd)

watchdog_ioctl 支持一系列 watchdog 设置和信息获取操作。

static long watchdog_ioctl(struct file *file, unsigned int cmd,unsigned long arg)case WDIOC_GETSUPPORT: // 返回watchdog_info结构体case WDIOC_GETSTATUS:  // 返回watchdog状态case WDIOC_GETBOOTSTATUS:case WDIOC_SETOPTIONS:  // start 和stop操作case WDIOC_KEEPALIVE:  // 喂狗case WDIOC_SETTIMEOUT:  // 设置超时时间case WDIOC_GETTIMEOUT:  // 获取超时时间case WDIOC_GETTIMELEFT:  // 获取超时剩余时间case WDIOC_SETPRETIMEOUT:  // 设置pretimeout case WDIOC_GETPRETIMEOUT:  // 获取pretimeout 

3.4 watchdog 喂狗

用户空间 watchdog(busybox)

watchdog 是 busybox 中的一个工具,通过watchdog -T 60 -t /dev/watchdog0,每 30秒喂一次狗,60秒没有喂狗则重启。

watchdog 是 busybox 中的一个工具,通过watchdog -T  60 -t /dev/watchdog0,每 30秒喂一次狗,60秒没有喂狗则重启。
Usage: watchdog [-t N[ms]] [-T N[ms]] [-F] DEVPeriodically write to watchdog device DEV-T N    Reboot after N seconds if not reset (default 60)-t N    Reset every N seconds (default 30)-F      Run in foregroundUse 500ms to specify period in milliseconds

watchdog 内部大概实现如下:

watchdog_main->shutdown_on_signal // watchdog收到异常信号退出时调用,关闭watchdog。->shutdown_watchdog // 关闭watchdog。->watchdog_open  // 打开 /dev/watchdog* 设备->WDIOC_SETOPTIONS  // 启动watchdog。->WDIOC_SETTIMEOUT  // 设置timeout。->while(1)  // 循环写空内容,然后睡眠。

内核空间喂狗

在内核空间也有一个定时器定时喂狗,流程如下:

1、在 watchdog_dev_init 接口中,注册了一个优先级为 MAX_RT_PRIO - 1、调度策略为 SCHED_FIFO、名为 watchdogd 的内核线程;

struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1,};
watchdog_kworker = kthread_create_worker(0, "watchdogd");
sched_setscheduler(watchdog_kworker->task, SCHED_FIFO, &param);

2、在注册 watchdog 设备时,初始化了一个 kthread_work 结构体,这个结构体表示一个内核线程工作项,用于调度和执行异步任务。kthread_init_work 函数将工作项与一个 watchdog_ping_work 关联起来,这个处理函数将在工作项被执行时调用;

kthread_init_work(&wd_data->work, watchdog_ping_work);

3、 在注册 watchdog 设备时,还会创建一个定时器,定时器超时接口是 watchdog_timer_expired,定时器在 watchdog_start 时会被开启;

hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
wd_data->timer.function = watchdog_timer_expired;
hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL_HARD);

4、在定时器超时接口 watchdog_timer_expired 中,使用 kthread_queue_work 接口将 kthread_work 工作项 wd_data->work 添加到了 watchdog 工作线程 watchdog_kworker 的工作队列中,使得工作线程可以异步执行该工作项。

static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer)
{struct watchdog_core_data *wd_data;wd_data = container_of(timer, struct watchdog_core_data, timer);kthread_queue_work(watchdog_kworker, &wd_data->work);return HRTIMER_NORESTART;
}

5、 wd_data->work 调用接口为 watchdog_ping_work ,改接口中执行看门狗的 ping 或者 start 动作实现看门狗喂狗,然后在watchdog_update_worker 接口中计算合适的下次喂狗时间,并重启定时器。

static void watchdog_ping_work(struct kthread_work *work)-> __watchdog_ping // if (watchdog_worker_should_ping(wd_data))-> wdd->ops->ping(wdd);  // if (wdd->ops->ping)-> wdd->ops->start(wdd); // else-> watchdog_update_worker(wdd);-> hrtimer_start

总结一下,watchdog 驱动中,通过创建一个定时器,并在定时器的超时函数中将看门狗喂狗函数添加到看门狗线程的工作队列中,实现当定时器超时时调用喂狗函数。并在喂狗函数中,计算合适的下次喂狗时间并重启定时器,由此实现了反复喂狗。

疑问 or bug?

上面提到的内核定时器喂狗需要配合用户空间喂狗时才能生效,单独内核空间定时器喂狗一段时间后定时器将不再刷新,只有在用户空间喂狗的前提下,内核空间喂狗的定时器才会刷新!!?那么内核空间定时器喂狗保留的意义是什么?这地方是个 bug 还是设计的就是如此,有大佬清楚的话可以一起讨论下。

代码分析如下:

上面提到,内核喂狗定时器任务 watchdog_ping_work 接口中,执行看门狗的 ping 或者 start 动作实现看门狗喂狗,然后在watchdog_update_worker 接口中计算合适的下次喂狗时间,并重启定时器

static inline void watchdog_update_worker(struct watchdog_device *wdd)-> ktime_t t = watchdog_next_keepalive(wdd);  // 计算下次喂狗时间t-> hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL_HARD) // if (t > 0),更新定时器

下次喂狗时间通过 watchdog_next_keepalive 接口计算:

static ktime_t watchdog_next_keepalive(struct watchdog_device *wdd)
{struct watchdog_core_data *wd_data = wdd->wd_data;unsigned int timeout_ms = wdd->timeout * 1000;ktime_t keepalive_interval;ktime_t last_heartbeat, latest_heartbeat;ktime_t virt_timeout;unsigned int hw_heartbeat_ms;if (watchdog_active(wdd))virt_timeout = ktime_add(wd_data->last_keepalive,ms_to_ktime(timeout_ms));elsevirt_timeout = wd_data->open_deadline;hw_heartbeat_ms = min_not_zero(timeout_ms, wdd->max_hw_heartbeat_ms);keepalive_interval = ms_to_ktime(hw_heartbeat_ms / 2);/** To ensure that the watchdog times out wdd->timeout seconds* after the most recent ping from userspace, the last* worker ping has to come in hw_heartbeat_ms before this timeout.*/last_heartbeat = ktime_sub(virt_timeout, ms_to_ktime(hw_heartbeat_ms));latest_heartbeat = ktime_sub(last_heartbeat, ktime_get());if (ktime_before(latest_heartbeat, keepalive_interval))return latest_heartbeat;return keepalive_interval;
}

分析该接口,主要有这么几个变量:

virt_timeout = ktime_add(wd_data->last_keepalive,  ms_to_ktime(timeout_ms)); 
// 下次超时时间,是 last_keepalive 时间 + watchdog timeout 时间last_heartbeat = ktime_sub(virt_timeout, ms_to_ktime(hw_heartbeat_ms));  
// 理论最近一次的喂狗时间,要根据 max_hw_heartbeat_ms 时间和 timeout 时间稍微提前一点返回值1:latest_heartbeat = ktime_sub(last_heartbeat, ktime_get());  
// 理论时间减去当前时间,是还差多久需要喂狗的时间返回值2:keepalive_interval = ms_to_ktime(hw_heartbeat_ms / 2); 
// keepalive_interval是根据看门狗设置的 max_hw_heartbeat_ms 计算的还差多久需要喂狗的时间

该接口返回值当前时间到下次喂狗时间的差值,有两种可能的返回值:

  • 返回值 keepalive_interval 是根据watchdog驱动设置的 wdd->timeoutwdd->max_hw_heartbeat_ms 计算出的,timeout 和 max_hw_heartbeat_ms 设置确定后,该值就不变了;
  • 返回值 latest_heartbeat 是先根据上次更新 wd_data->last_keepalive 值的时间计算出一个理论下次喂狗时间 last_heartbeat,该理论时间减去当前时间 ktime_sub(last_heartbeat, ktime_get()) 得到 latest_heartbeat
  • 返回值是判断 latest_heartbeat 和 keepalive_interval 的较小值,返回较小值;

问题:
但是,wd_data->last_keepalive 只在 wdg_start 和用户空间喂狗函数 watchdog_ping 接口中会更新,在内核定时器喂狗任务接口中,不会更新 wd_data->last_keepalive 值!!

static int watchdog_ping(struct watchdog_device *wdd)
{struct watchdog_core_data *wd_data = wdd->wd_data;if (!watchdog_active(wdd) && !watchdog_hw_running(wdd))return 0;set_bit(_WDOG_KEEPALIVE, &wd_data->status);wd_data->last_keepalive = ktime_get();  // 更新 wd_data->last_keepalivereturn __watchdog_ping(wdd);
}
// 内核定时器的喂狗接口,接口内不会更新 wd_data->last_keepalive
static void watchdog_ping_work(struct kthread_work *work)
{struct watchdog_core_data *wd_data;wd_data = container_of(work, struct watchdog_core_data, work);mutex_lock(&wd_data->lock);if (watchdog_worker_should_ping(wd_data))__watchdog_ping(wd_data->wdd);mutex_unlock(&wd_data->lock);
}
  • 当 last_keepalive 不再更新,通过 latest_heartbeat = ktime_sub(last_heartbeat, ktime_get()); 计算出的值将是个负值(linux 中 ktime_t 类型是一个有符号数);
  • watchdog_next_keepalive 接口返回一个负值
  • hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL_HARD); 设置的定时器超时时间入参 t 是一个负值

对于 hrtimer_start 设置一个负值的超时时间是怎么处理的没有再细追,但是实际测试下来,的确没有再继续喂狗!

所以如果没有用户空间的喂狗情况下,只靠内核空间是无法实现定时喂狗的。

3.5 watchdog pretimeout 和 governor

  • pretimeout 区别于 timeout,某些 watchdog 触发 timeout 后立即重启了,来不及做一些现场保存等动作。
  • pretimeout 是指在 timeout 之前的设置的一个超时时间,此时软件还处于可控状态,可以做一些预警和提前保存的动作。
  • 触发 pretimeout 调用指定的 governor 可以执行不同动作。
  • 支持 pretimeout 的 watchdog options 包含 WDIOF_PRETIMEOUT。
int watchdog_register_governor(struct watchdog_governor *gov);--governor的注册和注销接口。
void watchdog_unregister_governor(struct watchdog_governor *gov);int watchdog_register_pretimeout(struct watchdog_device *wdd);--pretimeout注册和注销接口。
void watchdog_unregister_pretimeout(struct watchdog_device *wdd);
int watchdog_pretimeout_available_governors_get(char *buf);
int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf);--设置/获取pretimeout对应的governor。
int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,const char *buf);

注册governor:

int watchdog_register_governor(struct watchdog_governor *gov)-> list_add(&priv->entry, &governor_list) // 将gov添加到governor_list// 如果gov是default gov,将所有wdd的gov设置为当前govif (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV,WATCHDOG_GOV_NAME_MAXLEN)) {default_gov = gov;list_for_each_entry(p, &pretimeout_list, entry)if (!p->wdd->gov)p->wdd->gov = default_gov;}

设置 watchdog 设备的 governor:

int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,const char *buf)-> find_governor_by_name(buf);-> wdd->gov = priv->gov;

调用 governor:

void watchdog_notify_pretimeout(struct watchdog_device *wdd)-> wdd->gov->pretimeout(wdd);

四、softdog

softdog 驱动通过软件模拟 watchdog 实现看门狗功能。

4.1 softdog_init

softdog_init 流程与正常硬件驱动的 watchdog 类似,只是实现了一个 softwatchdog 的定时器,该定时器在 ping 接口会被反复重启,当定时器超时也就说明软件没有及时喂狗,触发定时器超时函数,在超时函数里执行重启或者 panic 操作。

static int __init softdog_init(void)// 正常看门狗设备初始化watchdog_init_timeout(&softdog_dev, soft_margin, NULL);watchdog_set_nowayout(&softdog_dev, nowayout);watchdog_stop_on_reboot(&softdog_dev); // softdog使用的定时器hrtimer_init(&softdog_ticktock, CLOCK_MONOTONIC, HRTIMER_MODE_REL);softdog_ticktock.function = softdog_fire;// 设置pretimeout的定时器,超时接口softdog_pretimeout中调用watchdog_notify_pretimeoutif (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) {softdog_info.options |= WDIOF_PRETIMEOUT;hrtimer_init(&softdog_preticktock, CLOCK_MONOTONIC,HRTIMER_MODE_REL);softdog_preticktock.function = softdog_pretimeout;}// 看门狗设备注册ret = watchdog_register_device(&softdog_dev);if (ret)return ret;
}
module_init(softdog_init);

4.2 softdog_ping

softdog_ping 实现 softdog 喂狗,喂狗函数里是重新启动 softdog timeout 和 pretimeout 的定时器。

static int softdog_ping(struct watchdog_device *w)
{if (!hrtimer_active(&softdog_ticktock))__module_get(THIS_MODULE);hrtimer_start(&softdog_ticktock, ktime_set(w->timeout, 0),HRTIMER_MODE_REL);if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) {if (w->pretimeout)hrtimer_start(&softdog_preticktock,ktime_set(w->timeout - w->pretimeout, 0),HRTIMER_MODE_REL);elsehrtimer_cancel(&softdog_preticktock);}return 0;
}

4.3 softdog_fire

softdog_fire 是 softdag timeout 定时器超时函数,函数内部执行重启或者 panic 动作。

static enum hrtimer_restart softdog_fire(struct hrtimer *timer)
{module_put(THIS_MODULE);if (soft_noboot) {pr_crit("Triggered - Reboot ignored\n");} else if (soft_panic) {pr_crit("Initiating panic\n");panic("Software Watchdog Timer expired");} else {pr_crit("Initiating system reboot\n");emergency_restart();pr_crit("Reboot didn't ?????\n");}return HRTIMER_NORESTART;
}
参考:
Linux watchdog子系统概述

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

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

相关文章

生成随机密码

生成8位无重复的密码(可以包含数字、大小写字母) import random import string character string.digits string.ascii_letters password .join(random.sample(character, 8)) print(f"生成的随机密码为:{password}")

如何快速实现一个无缝轮播效果

🧑‍💻 写在开头 点赞 收藏 学会🤣🤣🤣 需求简介 轮播图是我们前端开发中的一个常见需求,在项目开发中,我们可以使用element、ant等UI库实现。某些场景,为了一个简单的功能安装一…

IDEA新建项目并撰写Java代码的方法

本文介绍在IntelliJ IDEA软件中,新建项目或打开已有项目,并撰写Java代码的具体方法;Groovy等语言的代码也可以基于这种方法来撰写。 在之前的文章IntelliJ IDEA社区版在Windows电脑中的下载、安装方法(https://blog.csdn.net/zheb…

在任何岗位都可以把自己当成一个项目经理

这几天跟一个刚入职场的姐妹交流的时候,她问了我一个问题,如果让你总结三年从助理升到经理的关键点,你觉得是什么?我思考了那么几秒钟,大概就是——在任何岗位都把自己当项目经理。 今天给大家介绍我的项目管理工具——…

头歌资源库(21)走方格

一、 问题描述 二、算法思想 首先,确定方格中间下方人所在的位置,即(row, col) (n//2, m//2)。初始化路径和为0。从初始位置开始,按照给定的5个方向进行移动:上(U),下(D),左(L),右(R)&#x…

关于腾讯的那些事(6月新闻纪要)

祝贺!深圳市科技进步一等奖 这份文件是关于腾讯云数据库TDSQL获得深圳市科技进步一等奖的详细报道。以下是详细总结分析: 获奖荣誉: 腾讯云数据库TDSQL荣获2022年深圳市科技进步一等奖,成为唯一入选的数据库产品。该奖项由深圳市…

10、Python之写出更加Pythonic的代码:unpacking拆包机制

引言 有些小伙伴有其他编程语言的学习、使用的经验,然后迁移到Python。一般会比完全的新手小白,更快速地把Python用起来。这是他们的优势,但也是他们的劣势。 之所以这么说,是因为从其他编程语言带过来的,除了相通的编…

第十八节 LLaVA如何按需构建LORA训练(视觉、语言、映射多个组合训练)

文章目录 前言一、基于llava源码构建新的参数1、添加lora_vit参数2、训练命令脚本设置二、修改源码,构建lora训练1、修改源码-lora训练2、LLM模型lora加载3、VIT模型加载4、权重冻结操作5、结果显示三、实验结果前言 如果看了我前面文章,想必你基本对整个代码有了更深认识。…

Raylib 实现超大地图放大缩小与两种模式瓦片地图刷新

原理: 一种刷新模式: 在宫格内整体刷新,类似九宫格移动到边缘,则九宫格整体平移一个宫格,不过这里是移动一个瓦片像素,实际上就是全屏刷新,这个上限是 笔记本 3060 70帧 100*100个瓦片每帧都…

数据库之MQL

1,查询所有 mysql> select * from grade;2, mysql> select id,firstname,lastname from grade;3, mysql> select firstname,lastname from grade where id > 4;4, mysql> select * from grade where sex f;5&…

odoo17 常见升级问题

通用问题 模型名变更 字段变更 方法名变更 方法参数变更 xml数据结构定义变化 xml的id变更 view视图变化,导致xpath路径出差 template结构变化,,导致xpath路径出差,或者id不存在 升16问题 前端owl的架构变化 升17问题 前端 标…

什么,有狗快跑!慢着,这次手把手教你怎么过安全狗!(sql注入篇)

前言 在记忆里上次绕安全狗还是在上次,开开心心把自己之前绕过狗的payload拿出来,发现全部被拦截了,事情一下子就严肃起来了,这就开整。 环境 本次环境如下sqli-lab的sql注入靶场 网站安全狗APACHE版V4.0版本的最高防护等级绕过…

秋招Java后端开发冲刺——并发篇2(ThreadLocal、Future接口)

本文对ThreadLocal类和Future接口进行了总结概括,包括ThreadLocal类的原理、内存泄露等问题,和Future接口的使用等问题。 一、ThreadLocal 1. 介绍 ThreadLocal(线程局部变量)是Java中的一个类,线程通过维护一个本地…

一文带你彻底搞懂什么是责任链模式!!

文章目录 什么是责任链模式?详细示例SpingMVC 中的责任链模式使用总结 什么是责任链模式? 在我们日常生活中,经常会出现一种场景:一个请求需要经过多个对象的处理才能得到最终的结果。比如,一个请假申请,需…

STM32智能仓库管理系统教程

目录 引言环境准备智能仓库管理系统基础代码实现:实现智能仓库管理系统 4.1 数据采集模块 4.2 数据处理与控制算法 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景:仓库管理与优化问题解决方案与优化收尾与总结 1. 引言 智能仓库管理系统通…

藏汉翻译通作为翻译软件的优势有哪些?

藏汉翻译通作为一款专业的藏汉双语翻译软件,具有以下优势: 人工智能技术应用:藏汉翻译通利用了人工智能翻译和语音识别合成技术,提供智能藏文翻译服务。 高准确率:文字识别准确率可达90%,语音识别转化文字…

苍穹外卖--导入分类模块功能代码

把各层代码拷贝到所需文件夹下, 进行编译 在运行 提交和推送仓库

解锁AI大模型潜能:预训练、迁移学习与中间件编程的协同艺术

在人工智能的浩瀚星空中,大型预训练模型(Large Language Models, LLMs)犹如璀璨的星辰,引领着技术革新的浪潮。这些模型通过海量数据的滋养,学会了理解语言、生成文本乃至执行复杂任务的能力。然而,要让这些…

【正点原子i.MX93开发板试用连载体验】项目计划和开箱体验

本文最早发表于电子发烧友:【   】【正点原子i.MX93开发板试用连载体验】基于深度学习的语音本地控制 - 正点原子学习小组 - 电子技术论坛 - 广受欢迎的专业电子论坛! (elecfans.com)https://bbs.elecfans.com/jishu_2438354_1_1.html 有一段时间没有参加电子发…

Pyspider WebUI 未授权访问致远程代码执行漏洞复现

0x01 产品简介 Pyspider是由国人binux编写的强大的网络爬虫系统,它带有强大的WebUI(Web用户界面),为用户提供了可视化的编写、调试和管理爬虫的能力。这一特点使得Pyspider在爬虫框架中脱颖而出,尤其适合那些希望快速上手并高效开发爬虫的用户。允许用户直接在网页上编写…