驱动开发学习之新旧字符设备接口,自动创建设备的点灯

1.前言

本章将介绍新旧字符设备接口,以及自动创建设备节点的点灯实验。

2.实验原理介绍

2.1.寄存器知识

学习过单片机的兄弟都知道,点灯有以下步骤:

(1)开启相应的GPIO时钟

(2)如果需要配置复用,就配置一下复用寄存器

(3)设置一下电气属性

(4)设置一下GPIO口的输入输出方向

(5)设置或者读取数据

按照这些步骤依次配置寄存器就可。(驱动里面直接操作寄存器是不是感觉有点low,没事,后面会介绍设备树的写法)

2.2.mmu介绍

在linux里面,是不能直接操作寄存器的物理地址的,操作的都是虚拟地址,因为linux里面有mmu这个器件,这是linux和单片机最大的一个区别所在了。MMU 全称叫做 Memory Manage Unit,也就是内存管理单元。

MMU 主要完成的功能如下:
①、完成虚拟空间到物理空间的映射。
②、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。 

2.3.虚拟地址与物理地址转换接口

既然不能操作物理地址,那得将物理地址换虚拟地址,驱动里面才可以使用了,linux提供了下面两个接口:

2.3.1.ioremap 函数

作用:ioremap 函数用于获取指定物理地址空间对应的虚拟地址空间,定义在

arch/arm/include/asm/io.h 文件中
#define ioremap(cookie,size) __arm_ioremap((cookie), (size), 
MT_DEVICE)

void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, 
unsigned int mtype)
{
    return arch_ioremap_caller(phys_addr, size, mtype,
    __builtin_return_address(0));
}

参数:

phys_addr:要映射的物理起始地址

size:要映射的内存空间大小,字节大小

mtype:ioremap 的类型,可以选择 MT_DEVICE、MT_DEVICE_NONSHARED、 MT_DEVICE_CACHED 和 MT_DEVICE_WC,ioremap 函数选择 MT_DEVICE

返回值:__iomem 类型的指针,指向映射后的虚拟空间首地址。

2.3.2.iounmap 函数

作用:卸载驱动的时候需要使用 iounmap 函数释放掉 ioremap 函数所做的映射

void iounmap (volatile void __iomem *addr)

参数:

addr,此参数就是要取消映射的虚拟地址空间首地址

2.4.内存访问函数

2.4.1.读操作函数

u8 readb(const volatile void __iomem *addr)
u16 readw(const volatile void __iomem *addr)
u32 readl(const volatile void __iomem *addr)

 readb、readw 和 readl 这三个函数分别对应 8bit、16bit 和 32bit 读操作

参数 addr 就是要 读取写内存地址,返回值就是读取到的数据

2.4.2.写操作函数

void writeb(u8 value, volatile void __iomem *addr)
void writew(u16 value, volatile void __iomem *addr)
void writel(u32 value, volatile void __iomem *addr)

 writeb、writew 和 writel 这三个函数分别对应 8bit、16bit 和 32bit 写操作

参数 value 是要 写入的数值,addr 是要写入的地址

3.测试例子

3.1.旧字符设备的接口例子

#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>


#define LED_MAJOR		200		/* 主设备号 */
#define LED_NAME		"led" 	/* 设备名字 */

#define LEDOFF 	0				/* 关灯 */
#define LEDON 	1				/* 开灯 */
 
/* 寄存器物理地址 */
#define CCM_CCGR1_BASE				(0X020C406C)	/** GPIO1 时钟 */
#define SW_MUX_GPIO1_IO03_BASE		(0X020E0068)	/** GPIO1复用寄存器 */
#define SW_PAD_GPIO1_IO03_BASE		(0X020E02F4)	/** GPIO1 电气属性 */
#define GPIO1_DR_BASE				(0X0209C000)	/** GPIO1数据 */
#define GPIO1_GDIR_BASE				(0X0209C004)	/** GPIO1方向*/

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

/*
 * @description		: LED打开/关闭
 * @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED
 * @return 			: 无
 */
void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);	
		writel(val, GPIO1_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO1_DR);
		val|= (1 << 3);	
		writel(val, GPIO1_DR);
	}	
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int led_open(struct inode *inode, struct file *filp)
{
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	ledstat = databuf[0];		/* 获取状态值 */

	if(ledstat == LEDON) {	
		led_switch(LEDON);		/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);	/* 关闭LED灯 */
	}
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备操作函数 */
static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init led_init(void)
{
	int retvalue = 0;
	u32 val = 0;

	/* 初始化LED */
	/* 1、寄存器地址映射 */
  	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
  	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
	GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

	/* 2、使能GPIO1时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	/* 清楚以前的设置 */
	val |= (3 << 26);	/* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);

	/* 3、设置GPIO1_IO03的复用功能,将其复用为
	 *    GPIO1_IO03,最后设置IO属性。
	 */
	writel(5, SW_MUX_GPIO1_IO03);
	
	/*寄存器SW_PAD_GPIO1_IO03设置IO属性
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
	 */
	writel(0x10B0, SW_PAD_GPIO1_IO03);

	/* 4、设置GPIO1_IO03为输出功能 */
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	/* 清除以前的设置 */
	val |= (1 << 3);	/* 设置为输出 */
	writel(val, GPIO1_GDIR);

	/* 5、默认关闭LED */
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);

	/* 6、注册字符设备驱动 */
	retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
	if(retvalue < 0){
		printk("register chrdev failed!\r\n");
		return -EIO;
	}
	return 0;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

	/* 注销字符设备驱动 */
	unregister_chrdev(LED_MAJOR, LED_NAME);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("seven");

3.2.新字符设备-自动创建设备例子

#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>


#define LED_MAJOR		200		/* 主设备号 */
#define LED_NAME		"led" 	/* 设备名字 */

#define LEDOFF 	0				/* 关灯 */
#define LEDON 	1				/* 开灯 */
 
#define NEWCHR_CNT			1		  	/* 设备号个数 */
#define NEWCHR_NAME			"newchr_led"	/* 设备名 */

/* 寄存器物理地址 */
#define CCM_CCGR1_BASE				(0X020C406C)	/** GPIO1 时钟 */
#define SW_MUX_GPIO1_IO03_BASE		(0X020E0068)	/** GPIO1复用寄存器 */
#define SW_PAD_GPIO1_IO03_BASE		(0X020E02F4)	/** GPIO1 电气属性 */
#define GPIO1_DR_BASE				(0X0209C000)	/** GPIO1数据 */
#define GPIO1_GDIR_BASE				(0X0209C004)	/** GPIO1方向*/

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;



 


/* newchr设备结构体 */
struct newchr_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
};

struct newchr_dev newchr;	/* newchr设备 */

/*
 * @description		: LED打开/关闭
 * @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED
 * @return 			: 无
 */
void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);	
		writel(val, GPIO1_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO1_DR);
		val|= (1 << 3);	
		writel(val, GPIO1_DR);
	}	
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int led_open(struct inode *inode, struct file *filp)
{
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	ledstat = databuf[0];		/* 获取状态值 */

	if(ledstat == LEDON) {	
		led_switch(LEDON);		/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);	/* 关闭LED灯 */
	}
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备操作函数 */
static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init led_init(void)
{
	u32 val = 0;

	/* 初始化LED */
	/* 1、寄存器地址映射 */
  	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
  	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
	GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

	/* 2、使能GPIO1时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	/* 清楚以前的设置 */
	val |= (3 << 26);	/* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);

	/* 3、设置GPIO1_IO03的复用功能,将其复用为
	 *    GPIO1_IO03,最后设置IO属性。
	 */
	writel(5, SW_MUX_GPIO1_IO03);
	
	/*寄存器SW_PAD_GPIO1_IO03设置IO属性
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
	 */
	writel(0x10B0, SW_PAD_GPIO1_IO03);

	/* 4、设置GPIO1_IO03为输出功能 */
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	/* 清除以前的设置 */
	val |= (1 << 3);	/* 设置为输出 */
	writel(val, GPIO1_GDIR);

	/* 5、默认关闭LED */
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (newchr.major) {		/*  定义了设备号 */
		newchr.devid = MKDEV(newchr.major, 0);
		register_chrdev_region(newchr.devid, NEWCHR_CNT, NEWCHR_NAME);
	} else {						/* 没有定义设备号 */
		alloc_chrdev_region(&newchr.devid, 0, NEWCHR_CNT, NEWCHR_NAME);	/* 申请设备号 */
		newchr.major = MAJOR(newchr.devid);	/* 获取分配号的主设备号 */
		newchr.minor = MINOR(newchr.devid);	/* 获取分配号的次设备号 */
	}
	printk("newchenewchr major=%d,minor=%d\r\n",newchr.major, newchr.minor);	
	
	/* 2、初始化cdev */
	newchr.cdev.owner = THIS_MODULE;
	cdev_init(&newchr.cdev, &led_fops);
	
	/* 3、添加一个cdev */
	cdev_add(&newchr.cdev, newchr.devid, NEWCHR_CNT);

	/* 4、创建类 */
	newchr.class = class_create(THIS_MODULE, NEWCHR_NAME);
	if (IS_ERR(newchr.class)) {
		return PTR_ERR(newchr.class);
	}

	/* 5、创建设备 */
	newchr.device = device_create(newchr.class, NULL, newchr.devid, NULL, NEWCHR_NAME);
	if (IS_ERR(newchr.device)) {
		return PTR_ERR(newchr.device);
	}
	return 0;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

	/* 注销字符设备驱动 */
	cdev_del(&newchr.cdev);/*  删除cdev */
	unregister_chrdev_region(newchr.devid, NEWCHR_CNT); /* 注销设备号 */

	device_destroy(newchr.class, newchr.devid);
	class_destroy(newchr.class);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("seven");

3.3.测试的应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	int fd, retvalue;
	char *filename;
	unsigned char databuf[1];
	
	if(argc != 3){
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];

	/* 打开led驱动 */
	fd = open(filename, O_RDWR);
	if(fd < 0){
		printf("file %s open failed!\r\n", argv[1]);
		return -1;
	}

	databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 */

	/* 向/dev/led文件写入数据 */
	retvalue = write(fd, databuf, sizeof(databuf));
	if(retvalue < 0){
		printf("LED Control Failed!\r\n");
		close(fd);
		return -1;
	}

	retvalue = close(fd); /* 关闭文件 */
	if(retvalue < 0){
		printf("file %s close failed!\r\n", argv[1]);
		return -1;
	}
	return 0;
}

4.实验结果:

4.1.新字符设备接口,自动创建设备的测试程序

加载驱动之后,会自动创建/dev/newchr_led这个设备,应用层就可以对该设备操作,完成点灯操作了。

4.2.旧版本字符设备

加载字符设备之后,需要自己通过mknod命令创建设备,如下:

5.总结

点灯已经完成了,但是不难发现还是有一些不好的问题出现:

(1)寄存器的地址写在代码里面,耦合程度太高,移植难度提高

(2)自动创建设备这种接口,感觉像模板一样,linux系统是否有直接可以使用的框架,或者子系统,帮我们快速开发呢?

答案是有的。带着问题出发,后面章节,我们将一 一探索这些问题的答案。

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

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

相关文章

聊聊如何感知项目引入哪些功能特性

前言 使用过springcloud全家桶朋友&#xff0c;应该知道springcloud涉及的组件很多&#xff0c;为了让开发者快速了解项目引入了springcloud哪些组件&#xff0c;springcloud引入了HasFeatures&#xff0c;配合Actuator&#xff0c;可以让开发者感知到项目引入的组件功能类型、…

C++ std::reference_wrapper:让引用更强大

std::reference_wrapper 的通俗易懂解释 一、简介二、std::reference_wrapper 的初衷三、常用示例3.1、与 make_pair 和 make_tuple 一起使用3.2、引用容器3.3、通过 std::thread 按引用传递参数给启动函数3.4、引用作为类成员3.5、按引用传递函数对象3.6、与绑定表达式一起使用…

苹果手机备忘录共享到微信,为何显示不支持的类型

作为一名苹果手机用户&#xff0c;我深知其系统的流畅与便捷。然而&#xff0c;在日常使用中&#xff0c;我发现了一个令人不解的问题&#xff1a;为何苹果手机的原生备忘录无法直接分享到微信&#xff1f;每次当我尝试将备忘录里的内容共享给微信好友时&#xff0c;总会遇到“…

代码随想录算法训练营第四十二天|62.不同路径、63. 不同路径 II

62.不同路径 文档讲解&#xff1a;代码随想录 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 记录每个格子的状态 二维矩阵-->二维dp数组 dp数组 题目是要求到达最后一个格子有多少种路径 所以dp[i,j]: 到达第(i,j)个格子有多少种路径 递推公式 到达一…

WPS文件没有保存怎么恢复?5个解决方案轻松恢复!

“我在WPS上编辑了一个文件&#xff0c;但是还没来得及将它保存&#xff0c;我不小心就退出软件了&#xff0c;现在不知道有什么方法可以恢复WPS文件呢&#xff1f;大家可以帮帮我吗” WPS作为一款功能强大且用户友好的软件&#xff0c;给我们的工作带来了很多的便利。但我们在…

记录一次内存取证

1.情景复现 我姐姐的电脑坏了。我们非常幸运地恢复了这个内存转储。你的工作是从系统中获取她所有的重要文件。根据我们的记忆&#xff0c;我们突然看到一个黑色的窗口弹出&#xff0c;上面有一些正在执行的东西。崩溃发生时&#xff0c;她正试图画一些东西。这就是我们从崩溃…

# WIN10/WIN11 找不到【应用商店 Microsoft.WindowsStore】怎么办?

WIN10/WIN11 找不到【应用商店 Microsoft.WindowsStore】怎么办&#xff1f; 解决方法&#xff1a; 1、右键【开始】菜单&#xff0c;点击【Windows PowerShell (管理员)】&#xff0c;输入&#xff1a; Get-AppxPackage -allusers | Select Name, PackageFullName 2、查询…

期望薪资30k字节java2面,A给B转账的同时B给A转账怎么并发量最高

一面 1、自我介绍 2、详细介绍一下自己的做的项目&#xff1f;根据项目提了一些问题 3、hashmap原理 4、B树原理&#xff1f; 5、final禁止重排序原理&#xff1f; 6、设计一个榨汁机类&#xff0c;面向对象怎么设计&#xff1f; 7、get、post区别&#xff0c;使用场景&…

AI大模型探索之路-实战篇9:探究Agent智能数据分析平台的架构与功能

系列篇章&#x1f4a5; AI大模型探索之路-实战篇4&#xff1a;深入DB-GPT数据应用开发框架调研 AI大模型探索之路-实战篇5&#xff1a;探索Open Interpreter开放代码解释器调研 AI大模型探索之路-实战篇6&#xff1a;掌握Function Calling的详细流程 AI大模型探索之路-实战篇7…

kettle学习之子映射组件

映射组件就跟java中的函数方法一样&#xff0c;类似一个子流程。 练习开始 根据数据库表中的id查询出想要的字段&#xff0c;并把字段存到excel表中 一、表输入 二、子映射 映射输入规范&#xff0c;类似java方法中的形参 name vsxcd是方法返回的参数 三、excel输出 运行结果…

前端基于word模板导出word文档

项目环境 vue2 js vue-cli等 依赖包都可以在npm官网找到对应文档 npm官网(英文) 1、依赖 安装依赖 docxtemplater npm i docxtemplaterfile-saver npm i file-saverjszip-utils npm i jszip-utilsjszip npm i jszip在对应页面或模块中引入依赖 import Docxtemplater …

关于(苍穹外卖)Cache问题的一点困惑与思路

因为本人&#xff0c;em---昨天看的&#xff0c;今天早上就有点迷惑了&#xff0c;在这里记录一下&#xff0c;并捋一下思路&#xff0c; 首先要明确的一点就是&#xff0c;我们是在uesr端进行缓存的&#xff0c;并且&#xff0c;菜品缓存是中的key是 String key "dish…

ora-00392 ora-00312错误处理

检查当前日志组状态 对日志组进行clear操作 重新开库无报错

CSS 一些常见的大坝设备仪器标识绘制

文章目录 需求分析1. 坝基测压管2. 坝体测压管3. 竖向位移兼水平位移测点4. 竖向位移测点5. 三角量水堰6. 水平位移观测工作基点7. 竖向位移观测水准基点8. 其他 需求 绘制一些常见的设备仪器标识符 分析 1. 坝基测压管 <!DOCTYPE html> <html lang"en"…

中国1KM分辨率年平均气温数据集

该数据为中国逐年平均温度数据&#xff0c;空间分辨率为0.0083333&#xff08;约1km&#xff09;&#xff0c;时间为1901年-2022年。该数据集是根据全国2472个气象观测点数据进行插值获取&#xff0c;验证结果可信。本数据集包含的地理空间范围是全国主要陆地&#xff08;包含港…

【ARM+Codesys案例】基于全志T3+Codesys的快递物流单件分离器控制系统

物流涉及国计民生&#xff0c;是在社会发展中不可或缺的一环。随着社会的改革开放&#xff0c;工业发展迅猛&#xff0c;此时也伴随着物流业的快速发展。电商、快递等行业业务量爆发以及人工成本的不断上涨&#xff0c;自动化输送分拣设备市场呈现井喷式发展。物流行业从传统方…

探索智能零售的未来商机与运营策略

探索智能零售的未来商机与运营策略 在智能零售的广阔图景中&#xff0c;无人售货机加盟赫然矗立为一股不可小觑的力量&#xff0c;预示着零售业态未来的转型与机遇。其核心优势多维展开&#xff0c;具体阐述如下&#xff1a; 1. **全天候服务**&#xff1a;无人售货机的运行跨…

ACS美国化学学会文献去哪里查找

今天有位同学求助一篇ACS美国化学学会文献&#xff0c;下面就以这篇文献实例讲解一下如何自己解决文献下载的过程。篇名&#xff1a;Biomimetic Superstructured Interphase for Aqueous Zinc-Ion Batteries 知道文献的来源数据库&#xff0c;直接去文献来源数据库下载文献又准…

两台电脑怎么互传文件?这些方法你值得一试

在日常生活和工作中&#xff0c;我们经常需要在不同电脑之间传输文件&#xff0c;这可能是文档、照片、音乐或其他类型的文件。两台电脑怎么互传文件是非常有用的技能&#xff0c;可以提高工作效率并简化文件共享过程。本文将介绍三种常见的方法&#xff0c;帮助您了解如何在两…

【Pandas】深入解析`pd.to_sql()`函数

【Pandas】深入解析pd.to_sql()函数 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我深耕Python编程、机器学习和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并乐于分享知识与经验的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1…