Linux驱动编程 - RTC子系统

目录

简介:

一、代码分析

1、RTC子系统初始化

2、注册RTC设备驱动

2.1 rtc_dev_prepare(rtc)

3、总结

二、ds1302 驱动分析

三、rtc设置和测试工具

1、date读/写系统时间

2、hwclock读/写RTC


简介:


        Linux中RTC设备驱动是一个标准的字符设备驱动,应用程序通过 open、 release、 read、 write 和 ioctl 等函数完成对 RTC 设备的操作。

rtc子系统分为三部分:

  • rtc core:负责rtc设备注册注销;对用户空间提供rtc字符设备文件,以及rtc类sysfs接口;
  • rtc driver:将rtc设备注册到rtc子系统,提供针对rtc设备的底层操作函数集;
  • 用户空间sysfs节点:/dev/rtcX字符设备文件,以及其他调试接口;

        图中,RTC Core已经在kernel中实现了,它初始化RTC子系统并向用户空间提供 file_operations 操作集(open、 read、 write 和 ioctl 等)。我们只需实现 RTC Driver(RTC 设备驱动) 和 设备树中对RTC的配置,RTC Driver中实现对RTC芯片的底层操作集。

一、代码分析


        Linux 内核将 RTC 设备抽象为 rtc_device 结构体,因此 RTC 设备驱动就是申请并初始化 rtc_device,最后将 rtc_device 注册到 Linux 内核里面。

/* 路径:include/linux/rtc.h */
struct rtc_device
{
	struct device dev;
	struct module *owner;

	int id;                            //当前rtc设备在rtc子系统的子序号
	char name[RTC_DEVICE_NAME_SIZE];

	const struct rtc_class_ops *ops;
	struct mutex ops_lock;

	struct cdev char_dev;             //rtc设备对应的字符设备
	unsigned long flags;

	unsigned long irq_data;
	spinlock_t irq_lock;
	wait_queue_head_t irq_queue;       //和用户空间同步的poll调用所使用的等待队列,由中断唤醒
	struct fasync_struct *async_queue; //和用户空间同步基于文件的fasync调用,由中断触发

	struct rtc_task *irq_task;
	spinlock_t irq_task_lock;
	int irq_freq;
	int max_user_freq;

	struct timerqueue_head timerqueue;
	struct rtc_timer aie_timer;
	struct rtc_timer uie_rtctimer;
	struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
	int pie_enabled;
	struct work_struct irqwork;
	/* Some hardware can't support UIE mode */
	int uie_unsupported;

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
	struct work_struct uie_task;
	struct timer_list uie_timer;
	/* Those fields are protected by rtc->irq_lock */
	unsigned int oldsecs;
	unsigned int uie_irq_active:1;
	unsigned int stop_uie_polling:1;
	unsigned int uie_task_active:1;
	unsigned int uie_timer_active:1;
#endif
};

        重点关注 struct rtc_class_ops *ops 成员变量,rtc_class_ops 为 RTC 设备的最底层操作函数集合,包括从 RTC 设备中读取时间、向 RTC 设备写入新的时间等。因此,rtc_class_ops 操作集需要用户根据所使用的 RTC 设备自己实现。

/* 路径:include/linux/rtc.h */
struct rtc_class_ops {
	int (*open)(struct device *);
	void (*release)(struct device *);
	int (*ioctl)(struct device *, unsigned int, unsigned long);
	int (*read_time)(struct device *, struct rtc_time *);
	int (*set_time)(struct device *, struct rtc_time *);
	int (*read_alarm)(struct device *, struct rtc_wkalrm *);
	int (*set_alarm)(struct device *, struct rtc_wkalrm *);
	int (*proc)(struct device *, struct seq_file *);
	int (*set_mmss64)(struct device *, time64_t secs);
	int (*set_mmss)(struct device *, unsigned long secs);
	int (*read_callback)(struct device *, int data);
	int (*alarm_irq_enable)(struct device *, unsigned int enabled);
};

注意:rtc_class_ops 中的这些函数只是最底层对 RTC 设备的操作函数,并不是提供给应用层的file_operations 函数操作集。Linux 内核提供了一个 RTC 通用字符设备驱动文件 drivers/rtc/rtc-dev.c,rtc-dev.c 文件提供了所有 RTC 设备共用的 file_operations 函数操作集。

1、RTC子系统初始化


        drivers/rtc/class.c 中运行 rtc_init() 函数,实现对RTC子系统的初始化。此部分Linux已经做好了,不需要我们实现。


/* linux/drivers/rtc/class.c */
static int __init rtc_init(void)
{
	rtc_class = class_create(THIS_MODULE, "rtc");
	if (IS_ERR(rtc_class)) {
		pr_err("couldn't create class\n");
		return PTR_ERR(rtc_class);
	}
	rtc_class->pm = RTC_CLASS_DEV_PM_OPS;
	rtc_dev_init();
	return 0;
}
subsys_initcall(rtc_init);

RTC子系统初始化,主要分配rtc_class类,以及rtc设备的rtc_devt。alloc_chrdev_region 用来动态分配号,调用过程如下:

rtc_init
--->class_create(THIS_MODULE, "rtc")         //创建rtc_class类
--->rtc_dev_init()
--->--->alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc")    //为rtc设备分配子设备号范围0~15。主设备号随机分配。最终结果放入rtc_devt

2、注册RTC设备驱动


        devm_rtc_device_register() 函数用来注册RTC设备驱动。编写RTC设备驱动时,我们需要先实现 struct rtc_class_ops 结构体,它是对RTC设备最底层的操作函数集合。然后调用 devm_rtc_device_register() 将 rtc_class_ops 注册到内核中。

/* linux/drivers/rtc/class.c */
/*
参数:
    dev: 设备
    name:设备名字
    ops: RTC 底层驱动函数集
    owner:驱动模块拥有者
*/
struct rtc_device *devm_rtc_device_register(struct device *dev,
					    const char *name,
					    const struct rtc_class_ops *ops,
					    struct module *owner)
{
	struct rtc_device *rtc;
	int err;

	rtc = devm_rtc_allocate_device(dev);    //分配 struct rtc_device 结构
	if (IS_ERR(rtc))
		return rtc;

	rtc->ops = ops;        //设置 rtc_class_ops 底层操作

	err = __rtc_register_device(owner, rtc);    //注册rtc设备
	if (err)
		return ERR_PTR(err);

	return rtc;
}

rtc->ops = ops 设置 rtc_class_ops 底层操作集。主要分析下 __rtc_register_device()

int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
{
    /* ... ... */
    dev_set_name(&rtc->dev, "rtc%d", id);    //设置device名字

	rtc_dev_prepare(rtc);					//初始化cdev结构体,file_operations

	err = cdev_device_add(&rtc->char_dev, &rtc->dev);	//添加设备到内核 cdev_add、device_add 注册设备
	/* ... ... */

	rtc_proc_add_device(rtc);

    /* ... ... */

#ifdef CONFIG_RTC_HCTOSYS_DEVICE
	if (!strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE))
		rtc_hctosys();
#endif

	return 0;
}

rtc_dev_prepare(rtc) 和 cdev_device_add 其实就是实现字符设备那套固定流程,初始化cdev并添加到kernel,注册device等。

2.1 rtc_dev_prepare(rtc)

        rtc_dev_prepare先分配设备号,调用cdev_init初始化cdev并添加file_operations操作集。file_operations 操作集提供给应用层调用:

/* 路径:linux/drivers/rtc/dev.c */
static const struct file_operations rtc_dev_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.read		= rtc_dev_read,
	.poll		= rtc_dev_poll,
	.unlocked_ioctl	= rtc_dev_ioctl,
	.open		= rtc_dev_open,
	.release	= rtc_dev_release,
	.fasync		= rtc_dev_fasync,
};

/* insertion/removal hooks */

void rtc_dev_prepare(struct rtc_device *rtc)
{
	if (!rtc_devt)
		return;

	if (rtc->id >= RTC_DEV_MAX) {
		dev_dbg(&rtc->dev, "too many RTC devices\n");
		return;
	}

	rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
	INIT_WORK(&rtc->uie_task, rtc_uie_task);
	timer_setup(&rtc->uie_timer, rtc_uie_timer, 0);
#endif

	cdev_init(&rtc->char_dev, &rtc_dev_fops);
	rtc->char_dev.owner = rtc->owner;
}

应用程序可以通过 ioctl 函数来设置/读取时间、设置/读取闹钟等操作,那么对应的 rtc_dev_ioctl 函数就会执行, rtc_dev_ioctl 最终会通过操作 rtc_class_ops 底层操作集中的 read_time、 set_time 等函数来对具体 RTC 设备的读写操作。

/* linux/drivers/rtc/dev.c */
static long rtc_dev_ioctl(struct file *file,
			  unsigned int cmd, unsigned long arg)
{
	/* ... ... */
	case RTC_RD_TIME:
		mutex_unlock(&rtc->ops_lock);

		err = rtc_read_time(rtc, &tm);    //最终会调用 rtc->ops->read_time
		if (err < 0)
			return err;

		if (copy_to_user(uarg, &tm, sizeof(tm)))
			err = -EFAULT;
		return err;

	case RTC_SET_TIME:
		mutex_unlock(&rtc->ops_lock);

		if (copy_from_user(&tm, uarg, sizeof(tm)))
			return -EFAULT;

		return rtc_set_time(rtc, &tm);
    /* ... ... */
}

以读RTC为例,rtc_read_time会调用到 __rtc_read_time()

/* 路径:linux/drivers/rtc/interface.c */
static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
	... ...
    /* devm_rtc_device_register() 中将 rtc->ops 赋值为我们实现的 rtc_class_ops */
	err = rtc->ops->read_time(rtc->dev.parent, tm);
	... ...
}

__rtc_read_time 函数会通过调用 rtc_class_ops 中的read_time 来从 RTC 设备中获取当前时间。 rtc_dev_ioctl 函数对其他的命令处理都是类似的。

3、结论

3.1 编写RTC驱动时,我们只用实现 rtc_class_ops ,然后调用devm_rtc_device_register() 将其注册到 Linux 内核中即可

3.2 Linux 内核中 RTC 驱动调用流程如图

注意,系统启动时注册RTC设备调用 rtc_hctosys,会将RTC时间设置到系统时间:

rtc_hctosys
--->rtc_read_time
--->rtc_tm_to_time64
--->do_settimeofday64

二、ds1302 驱动分析


Linux中自带的linux/drivers/rtc/rtc-ds1302.c驱动代码如下:

#include <linux/bcd.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>

#define DRV_NAME	"rtc-ds1302"

#define	RTC_CMD_READ	0x81		/* Read command */
#define	RTC_CMD_WRITE	0x80		/* Write command */

#define	RTC_CMD_WRITE_ENABLE	0x00		/* Write enable */
#define	RTC_CMD_WRITE_DISABLE	0x80		/* Write disable */

#define RTC_ADDR_RAM0	0x20		/* Address of RAM0 */
#define RTC_ADDR_TCR	0x08		/* Address of trickle charge register */
#define RTC_CLCK_BURST	0x1F		/* Address of clock burst */
#define	RTC_CLCK_LEN	0x08		/* Size of clock burst */
#define	RTC_ADDR_CTRL	0x07		/* Address of control register */
#define	RTC_ADDR_YEAR	0x06		/* Address of year register */
#define	RTC_ADDR_DAY	0x05		/* Address of day of week register */
#define	RTC_ADDR_MON	0x04		/* Address of month register */
#define	RTC_ADDR_DATE	0x03		/* Address of day of month register */
#define	RTC_ADDR_HOUR	0x02		/* Address of hour register */
#define	RTC_ADDR_MIN	0x01		/* Address of minute register */
#define	RTC_ADDR_SEC	0x00		/* Address of second register */

static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *time)
{
	struct spi_device	*spi = dev_get_drvdata(dev);
	u8		buf[1 + RTC_CLCK_LEN];
	u8		*bp;
	int		status;

	/* spi将时间写入ds1302 */
	/* Enable writing */
	bp = buf;
	*bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE;
	*bp++ = RTC_CMD_WRITE_ENABLE;

	status = spi_write_then_read(spi, buf, 2,
			NULL, 0);
	if (status)
		return status;

	/* Write registers starting at the first time/date address. */
	bp = buf;
	*bp++ = RTC_CLCK_BURST << 1 | RTC_CMD_WRITE;

	*bp++ = bin2bcd(time->tm_sec);
	*bp++ = bin2bcd(time->tm_min);
	*bp++ = bin2bcd(time->tm_hour);
	*bp++ = bin2bcd(time->tm_mday);
	*bp++ = bin2bcd(time->tm_mon + 1);
	*bp++ = time->tm_wday + 1;
	*bp++ = bin2bcd(time->tm_year % 100);
	*bp++ = RTC_CMD_WRITE_DISABLE;

	/* use write-then-read since dma from stack is nonportable */
	return spi_write_then_read(spi, buf, sizeof(buf),
			NULL, 0);
}

static int ds1302_rtc_get_time(struct device *dev, struct rtc_time *time)
{
	struct spi_device	*spi = dev_get_drvdata(dev);
	u8		addr = RTC_CLCK_BURST << 1 | RTC_CMD_READ;
	u8		buf[RTC_CLCK_LEN - 1];
	int		status;

	/* spi从ds1302读取时间数据 */
	status = spi_write_then_read(spi, &addr, sizeof(addr),
			buf, sizeof(buf));
	if (status < 0)
		return status;

	/* Decode the registers */
	time->tm_sec = bcd2bin(buf[RTC_ADDR_SEC]);
	time->tm_min = bcd2bin(buf[RTC_ADDR_MIN]);
	time->tm_hour = bcd2bin(buf[RTC_ADDR_HOUR]);
	time->tm_wday = buf[RTC_ADDR_DAY] - 1;
	time->tm_mday = bcd2bin(buf[RTC_ADDR_DATE]);
	time->tm_mon = bcd2bin(buf[RTC_ADDR_MON]) - 1;
	time->tm_year = bcd2bin(buf[RTC_ADDR_YEAR]) + 100;

	return 0;
}

static const struct rtc_class_ops ds1302_rtc_ops = {
	.read_time	= ds1302_rtc_get_time,
	.set_time	= ds1302_rtc_set_time,
};

static int ds1302_probe(struct spi_device *spi)
{
	struct rtc_device	*rtc;
	u8		addr;
	u8		buf[4];
	u8		*bp;
	int		status;

	/* spi初始化ds1302 */
	... ...

	spi_set_drvdata(spi, spi);

	rtc = devm_rtc_device_register(&spi->dev, "ds1302",
			&ds1302_rtc_ops, THIS_MODULE);
	if (IS_ERR(rtc)) {
		status = PTR_ERR(rtc);
		dev_err(&spi->dev, "error %d registering rtc\n", status);
		return status;
	}

	return 0;
}

static int ds1302_remove(struct spi_device *spi)
{
	spi_set_drvdata(spi, NULL);
	return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id ds1302_dt_ids[] = {
	{ .compatible = "maxim,ds1302", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ds1302_dt_ids);
#endif

static struct spi_driver ds1302_driver = {
	.driver.name	= "rtc-ds1302",
	.driver.of_match_table = of_match_ptr(ds1302_dt_ids),
	.probe		= ds1302_probe,
	.remove		= ds1302_remove,
};

module_spi_driver(ds1302_driver);

MODULE_DESCRIPTION("Dallas DS1302 RTC driver");
MODULE_AUTHOR("Paul Mundt, David McCullough");
MODULE_LICENSE("GPL v2");

rtc_class_ops结构体中实现了 ds1302_rtc_get_time 和 ds1302_rtc_set_time 分别读取/设置时间。读取/设置时间就是通过spi总线读写ds1302的寄存器。最终,ds1302_probe() 函数调用 devm_rtc_device_register() 将 rtc_class_ops 注册到RTC子系统中。我们可以参照此代码来添加自己的RTC设备驱动。

三、rtc设置和测试工具


1、date读/写系统时间

$ date -s "2024-10-14 10:10:10"        #设置当前系统时间

“ date -s”命令仅仅是将当前系统时间设置了,此时间还没有写入到RTC设备中
 

2、hwclock读/写RTC

$ hwclock -w                #将当前系统时间设置到RTC硬件中。

$ hwclock -r                 #读取当前RTC硬件时间

$ hwclock -s                #将RTC时间设置到系统时间。

如果要设置RTC时间,先通过date设置系统时间,然后通过hwclock将系统时间设置到rtc硬件中。

$ date -s "2024-10-14 10:10:10"

$ hwclock -s


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

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

相关文章

ZBrush和3D-Coat各自的优缺点是什么?

zbrush支持的模型面数高英文界面&#xff0c;3d coat支持的模型面数比zbrsh低有中文界 ZBrush优缺点 1、ZBrush优点&#xff1a; zbrush是高精度建模poser制作的首选。可搭配雕刻版使用&#xff0c;主要为烘焙高细节的铁图建模。因为是高精度模型&#xff0c;不适用于动画和游…

《Spring Cloud Config与Bus整合实现微服务配置自动刷新》

目录 Config与Bus整合自动刷新步骤1&#xff1a;安装RabbitMQ并启动RabbitMQ的安装 步骤2&#xff1a;创建项目创建Eureka Server创建config-server 步骤3&#xff1a; 添加依赖步骤4&#xff1a;Config Client步骤5&#xff1a;测试运行问题一问题二 总结 Config与Bus整合自动…

python创建树状图

python创建树状图 想法&#xff1a;如何去记住每个页面的元素&#xff0c;如何实现不同页面的导航&#xff0c;如何从主页面遍历每一个页面的每一个元素 1.创建数据结构存储 2.树状图正好是我们想要的结构体 class TreeNode:def __init__(self, data):self.data dataself.ch…

电感电容谐振原理及Matlab仿真

一、电感电容谐振原理概述 电感电容谐振&#xff08;LC谐振&#xff09;是一种电路现象&#xff0c;它发生在电感器&#xff08;L&#xff09;和电容器&#xff08;C&#xff09;通过适当的方式连接时&#xff0c;电路中电流和电压之间形成共振。在这种共振状态下&#xff0c;…

2025选题推荐|基于SpringBoot的幼儿园智能管理与监控系统的设计与实现

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

测试代理IP速度的方法有哪些?

了解代理的连接速度是否快速是确保网络使用效率和体验的关键因素之一。本文来为大家如何有效地评估和测试代理IP的连接速度&#xff0c;以及一些实用的方法和工具&#xff0c;帮助用户做出明智的选择和决策。 一、如何评估代理IP的连接速度 1. 使用在线速度测试工具 为了快速…

MySQL从入门到跑路

SQL语言 SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是用于管理和操作关系数据库的一种标准编程语言。 SQL分类&#xff1a; DDL(Data Definition Language)&#xff1a;数据定义语言&#xff0c;用于操作数据库、表、字段&#xff0c…

外包干了3周,技术退步太明显了。。。。。

先说一下自己的情况&#xff0c;大专生&#xff0c;21年通过校招进入武汉某软件公司&#xff0c;干了差不多3个星期的功能测试&#xff0c;那年国庆&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我才在一个外包企业干了3周的功…

XCTF通关记录

XCTF通关记录 地址1、Banmabanma2、适合作为桌面一、变暗反转过后&#xff0c;仔细看是有个二维码的、调节然后调节亮度与对比度、得到它二、二维码解码出来之后1 、首先想到的是ASCII码&#xff0c;但是结果好像不对2、然后去搜索这是个啥 3、心仪的公司 地址 xtcf&#xff1…

uniapp,获取头部高度

头部自定义时候&#xff0c;设置获取安全区域&#xff0c;可以用 uni.getSystemInfoSync();接口。 <view class"statusBar" :style"{height:statusBarHeightpx}"> let SYSuni.getSystemInfoSync(); let statusBarHeightref(SYS.statusBarHeight) …

Java基于SSM微信小程序物流仓库管理系统设计与实现(lw+数据库+讲解等)

选题背景 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个…

webm格式怎么转换成mp4?几个操作简单的视频格式转换方法

webm格式怎么转换成mp4&#xff1f;webm&#xff0c;一种由Google推出的开源视频格式&#xff0c;以其高效的压缩率和流媒体传输能力而备受青睐。它特别适用于网络视频播放&#xff0c;能够在保证视频质量的同时&#xff0c;大大节省带宽和存储空间。然而&#xff0c;尽管webm格…

Selenium打开外部应用程序的弹窗处理

问题 selenium自动化操作页面跳转到外部应用程序进行下载等操作&#xff0c;各种窗口处理方式无法解决 原因 该窗口属于浏览器窗口&#xff0c;与访问页面无关&#xff08;已经脱离页面操作层面&#xff09; 解决 selenium启动浏览器时&#xff0c;对浏览器进行相关窗口设…

RDD优化:缓存和checkpoint机制、数据共享(广播变量、累加器)、RDD的依赖关系、shuffle过程、并行度说明

文章目录 1. 缓存和checkpoint机制1.1 缓存使用1.2 checkpoint1.3 缓存和checkpoint的区别 2. 数据共享2.1 广播变量2.2 累加器 3. RDD依赖关系4.shuffle过程4.1 shuffle介绍4.2 spark计算要尽量避免shuffle 5. 并行度 1. 缓存和checkpoint机制 缓存和checkpoint也叫作rdd的持…

SAM应用:医学图像和视频中的任何内容分割中的基准测试与部署

医学图像和视频中的任何内容分割&#xff1a;基准测试与部署 目录 摘要&#xff1a;一、引言1.1 SAM2 在医学图像和视频中的应用 二.结果2.1 数据集和评估协议2.2 二维图像分割的评估结果 三 讨论四 局限性和未来的工作五、方法5.1数据来源和预处理5.2 微调协议5.3 评估指标 总…

无人机视角下火灾检测数据集 共12736张 标注文件为YOLO适用的txt格式。已划分为训练集、验证集、测试集。类别:Fire yolov5-v10通用

无人机视角下火灾检测数据集 共12736张 标注文件为YOLO适用的txt格式。已划分为训练集、验证集、测试集。类别&#xff1a;Fire yolov5-v10通用 无人机视角下火灾检测数据集 共12736张 标注文件为YOLO适用的txt格式。已划分为训练集、验证集、测试集。类别&#xff1a;Fire yol…

Apple Find My介绍

0 Preface/Foreword 1 Apple Find My介绍

【Vue】Vue(八)Vue3.0 使用ref 和 reactive创建响应式数据

ref 创建&#xff1a;基本类型的响应式数据 **作用&#xff1a;**定义响应式变量。语法&#xff1a;let xxx ref(初始值)。**返回值&#xff1a;**一个RefImpl的实例对象&#xff0c;简称ref对象或ref&#xff0c;ref对象的value属性是响应式的。注意点&#xff1a; JS中操作…

达梦8-SQL日志配置与分析工具

以 dmsql_数据库实例名.log 类型命名的文件为跟踪日志文件&#xff0c;跟踪日志内容包含系统各会话执行的 SQL 语句、参数信息、错误信息等。跟踪日志主要用于分析错误和分析性能问题&#xff0c;比如&#xff0c;可以挑出系统现在执行速度较慢的 SQL 语句&#xff0c;进而对其…

以JavaScript的学习角度看Axios,并以spring boot+vue3为例具体分析实现

什么是Axios Axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;用于在浏览器和 后端 中发送异步的 HTTP 请求。它功能强大、易用&#xff0c;常用于与 API 交互&#xff0c;发送 GET、POST、PUT、DELETE 等请求。 Axios 的主要特点&#xff1a; 支持 Promise Axios 基于 …