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_KEEPALIVEPING
	const struct watchdog_ops *ops;    // watchdog 操作接口
	const struct watchdog_governor *gov;   //  pretimeout使用的governor
	unsigned 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] DEV

Periodically 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 foreground

Use 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));
	else
		virt_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_keepalive
	return __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设置为当前gov
	if (!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_pretimeout
	if (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);
		else
			hrtimer_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子系统概述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

防火墙综合实验一

目录 实验要求 防火墙准备 IP地址分配 需求一 需求二 需求三 需求四 需求五 需求六 实验要求 1、DMZ区内的服务器,办公区仅能在办公时间内(9:00-18:00)可以访问,生产区的设备全天可以访问。 2、生产区不允许访问互联网,办公区和游客…

单向链表队列

实现单向链表队列的&#xff0c;创建&#xff0c;入队&#xff0c;出队&#xff0c;遍历&#xff0c;长度&#xff0c;销毁。 queue.h #ifndef __QUEUE_H__ #define __QUEUE_H__#include <stdio.h> #include <stdlib.h> #include <string.h> #define max 30…

图论---无向图中国邮路的实现

开始编程前分析设计思路和程序的整体的框架&#xff0c;以及作为数学问题的性质&#xff1a; 程序流程图&#xff1a; 数学原理&#xff1a; 本质上是找到一条欧拉回路&#xff0c;考虑图中的边权重、顶点的度数以及如何通过添加最少的额外边来构造欧拉回路&#xff0c;涉及到欧…

PLC数采网关在实际应用中有哪些效能?天拓四方

在工业自动化领域中&#xff0c;PLC扮演着至关重要的角色&#xff0c;它负责控制和监测生产线的各个环节。然而&#xff0c;随着工业4.0的推进和智能制造的快速发展&#xff0c;单纯依靠PLC进行现场控制已无法满足企业对数据集中管理、远程监控和智能分析的需求。因此&#xff…

筑牢代码安全之盾 —— 沙箱在源代码防泄密中四大特性

在这个数字化飞速发展的时代&#xff0c;源代码作为企业的核心资产&#xff0c;其安全性显得尤为重要。一旦泄露&#xff0c;不仅可能导致知识产权的损失&#xff0c;还可能引发一系列连锁反应&#xff0c;威胁企业的生存和发展。在这样的背景下&#xff0c;SDC沙盒以其独特的产…

洛杉矶裸机云大宽带服务器的特性和优势

洛杉矶裸机云大宽带服务器是结合了物理服务器性能和云服务灵活性的高性能计算服务&#xff0c;为用户提供高效、安全的计算和存储能力。在了解如何使用洛杉矶裸机云大宽带服务器之前&#xff0c;需要了解其基本特性和优势。以下是对洛杉矶裸机云大宽带服务器的具体分析&#xf…

ZFT9-7VE8043-Z同期脉冲发送装置100V JOSEF约瑟 柜内安装

ZFT9(PIG)同期脉冲发送装置 系列型号 ZFT9(PIG) 7VE8033同期脉冲发送装置; ZFT9(PIG) 7VE8043同期脉冲发送装置; ZFT9 7VE8033同期脉冲发送装置; ZFT9 7VE8043同期脉冲发送装置; 用途&#xff1a; ZFT9同期脉冲发送装置用于船舶的三相系统&#xff0c;根据发电机和电力系…

突发,众多网站流量被盗刷!事情没那么简单。。

这两天发生了一件震惊 IT 圈的大事&#xff0c;很多程序员博主的网站竟然 同时 被恶意攻击&#xff0c;盗刷了大把流量费&#xff0c;我这个老倒霉蛋自然也中招了&#xff0c;作为受害人&#xff0c;专门做了本次分享&#xff0c;希望其他有网站的朋友们也都小心点。 那为什么…

【UE5】调用ASR接口,录制系统输出。录制音频采样率不匹配

暂时测出window能用。阿里的ASR接口当前仅支持8000和16000。UE默认采样44100。

【postgresql】视图(View)

PostgreSQL 中的视图&#xff08;View&#xff09;是一种虚拟表&#xff0c;其内容由 SQL 查询定义。视图可以简化复杂的 SQL 操作&#xff0c;使得用户能够以一种更直观、更易于理解的方式来访问和操作数据。 PostgreSQL 视图是只读的&#xff0c;因此可能无法在视图上执行 D…

pd虚拟机去虚拟化是什么意思?pd虚拟机去虚拟化教程 PD虚拟机优化设置

Parallels Desktop for Mac&#xff08;PD虚拟机&#xff09;去虚拟化是指在虚拟机&#xff08;Virtual Machine&#xff0c;简称 VM&#xff09;中禁用或减少虚拟化层的影响&#xff0c;使其表现更接近于物理机。这种操作通常用于提高虚拟机的性能或解决某些软件兼容性问题。具…

基于JAVA+SpringBoot+Vue的社区普法平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 社区普法平台旨在为社…

10个JavaScript One-Liners让初学者看起来很专业

原文链接&#xff1a;https://pinjarirehan.medium.com/10-javascript-one-liners-for-beginner-developers-to-look-pro-b9548353330a 原文作者&#xff1a;Rehan Pinjari 翻译&#xff1a;小圆 你是不是在辛苦码字时&#xff0c;看到别人轻松甩出一行 JavaScript 就搞定难题…

ubuntu 上vscode +cmake的debug调试配置方法

在ubuntu配置pcl点云库以及opencv库的时候&#xff0c;需要在CMakeLists.txt中加入相应的代码。配置完成后&#xff0c;无法调试&#xff0c;与在windows上体验vs studio差别有点大。 找了好多调试debug配置方法&#xff0c;最终能用的有几种&#xff0c;但是有一种特别好用&a…

如何学习一门新技术,十年 MarkDown 程序员怎么做

案例源码仓库地址&#xff1a; https://github.com/Rodert/go-demo官方文档&#xff1a; https://etcd.io/视频教程&#xff1a; https://space.bilibili.com/404747369 文章目录 介绍使用场景 安装&搭建搭建 ETCD与 ETCD 交互集群 GoETCD 编码 介绍 谈使用场景之前&#…

C#知识|账号管理系统:UI层-添加账号窗体设计思路及流程。

哈喽,你好啊,我是雷工! 前边练习过详情页窗体的设计思路及流程: 《C#知识|上位机UI设计-详情窗体设计思路及流程(实例)》 本节练习添加账号窗体的UI设计,以下为学习笔记。 01 效果展示 02 添加窗体 在UI层添加Windows窗体,设置名称为:FrmAddAcount.cs 设置窗体属…

【算法入门-栈】逆波兰表达式求值

&#x1f4d6;逆波兰表达式求值 ✅描述✅扩展&#xff1a;什么是逆波兰表达式✅题解方法一&#xff1a;栈✅题解方法二&#xff08;数组模拟栈&#xff09; 今天又刷了一道题&#xff0c;奥利给 刷题地址&#xff1a; 点击跳转 ✅描述 给定一个逆波兰表达式&#xff0c;求表达…

vue3+vite项目添加项目环境变量配置文件(.env),import.meta.env

.env: VITE_KEY 123获取环境变量&#xff1a; let env import.meta.env console.log(env, env) 人工智能学习网站 https://chat.xutongbao.top

RAG应用的典型工作流程

下面是RAG应用的典型工作流程&#xff1a; 具体步骤如下&#xff1a; 输入&#xff1a; 是指LLM系统需要回答的问题。如果不使用RAG&#xff0c;问题直接由LLM回答。 索引&#xff1a; 使用RAG时&#xff0c;会先将相关文档分块&#xff0c;为这些块生成嵌入向量&#xff0c;并…

期权交易必须弄懂的期权波动率是什么?

今天带你了解期权交易必须弄懂的期权波动率是什么&#xff1f;波动率是金融资产价格波动的度量&#xff0c;它衡量了资产的收益率的不确定性&#xff0c;常用于反映金融资产的风险水平。 期权波动率是衡量资产价格偏离平均值的程度&#xff0c;偏离程度越大&#xff0c;期权波…