嵌入式Linux的AXI平台(platform)驱动教程

本文以JFMQL100的Linux系统的AXI接口的平台驱动为例,介绍嵌入式Linux的平台驱动编写、测试软件编写以及验证方式。本文的方法适用于任意嵌入式芯片Linux的物理地址映射的平台(platform)驱动的编写、测试与应用。

       本文中AXI的开始地址为0x80000000,长度为0x20008KB)的地址空间进行地址映射。Vivado中查看AXI的地址范围如下所示:

图1 AXI地址映射

        本文PL-PS的中断(IRQ_F2P[0]),提醒PS端进行AXI数据读取。该中断在进口ZYNQ7的中断号为61,在JFMQL100TAI的中断号为57。在Linux系统设备树配置上均需要中断号减32,即ZYNQ7的中断号配置为29,JFMQL100TAI的中断号配置为25

图2  PL-PS的IRQ_F2P中断使能

1 设备树修改

在设备树的根节点添加自定义的axi驱动节点,如下所示:

图3 添加自定义的axi平台驱动

2 驱动源码

1.新建axi驱动目录,如下图所示:

图4 新建custom_axi_driver目录

2.在axi驱动目录下,新建axi驱动源文件custom_axi_driver.c,驱动源文件如下所示:

图5 新建custom_axi_driver.c的源文件

#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/delay.h>#include <linux/string.h>
#include <linux/uaccess.h>#include <linux/errno.h>    
#include <linux/interrupt.h>
#include <linux/init.h>#include <linux/of_address.h>
#include <linux/of_dma.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>#define DEVICE_NAME "custom_axi_driver"#define AXI_BASE_ADDR_LEN       0x4000
#define AXI_BASE_ADDR           0x80000000enum IOCTL_CMD
{CMD_GET_FPGA_VERSION = 10,      //获得FPGA版本号 10CMD_GET_FPGA_TEMP ,             //获得FPGA温度   11CMD_READ_REG,                   //读axi寄存器,单个寄存器操作  12CMD_WRITE_REG                   //写axi寄存器,单个寄存器操作  13
};void __iomem *axi_base_addr;        //ioremap映射的虚拟地址/*定义一个平台*/
typedef struct axi_struct
{int major;struct class            *axi_class;     //struct device           *axi_class_dev; //设备struct cdev             axi_cdev;       //cdev 结构struct file_operations  *drv_fops;      //操作函数接口char                    axi_name[20];   //设备名称struct platform_device  *pdev;          //平台设备struct device_node      *nd;            //设备节点
}struct_axi_driver,*ptstruct_axi_driver;static dev_t dev_id; //设备编号
static int irq; //中断号static struct fasync_struct *irq_async;
//设备打开
static int custom_axi_open(struct inode *inode, struct file *file)
{printk(KERN_INFO"%s axi_open!!\n",__FUNCTION__);return 0;
}
//设备关闭
static int custom_axi_release(struct inode *inode, struct file *file)
{printk(KERN_INFO"%s axi_release!!\n",__FUNCTION__);return 0;
}
//设备写
static ssize_t custom_axi_write(struct file *flip,const char __user *buf,size_t cnt,loff_t *offt)
{unsigned int start_addr = 0;unsigned int len = 0;if(copy_from_user(&start_addr, buf, sizeof(unsigned int))){printk(KERN_ERR"%s copy_from_user is error!!\n",__FUNCTION__);return -1;}if(copy_from_user(&len, buf + sizeof(unsigned int), sizeof(unsigned int))){printk(KERN_ERR"%s copy_from_user is error!!\n",__FUNCTION__);return -1;}if(len <= AXI_BASE_ADDR_LEN){if(copy_from_user((unsigned int *)(axi_base_addr + start_addr), buf + sizeof(unsigned int)+ sizeof(unsigned int), len)){printk("%s copy_from_user is error!!\n",__FUNCTION__);return -1;}}else{printk("%s write num beyond AXI_ADDRESS_LEN!!\n",__FUNCTION__);return -1;}return len;
}
//设备读
static ssize_t custom_axi_read(struct file *flip, char __user *buf, size_t cnt, loff_t *offt)
{unsigned int start_addr = 0;unsigned int len = 0;if(copy_from_user(&start_addr, buf, sizeof(unsigned int))){printk(KERN_ERR"%s copy_from_user start_addr is error!!\n",__FUNCTION__);return -1;}if(copy_from_user(&len, buf + sizeof(unsigned int), sizeof(unsigned int))){printk(KERN_ERR"%s copy_from_user len is error!!\n",__FUNCTION__);return -1;}if(len <= AXI_BASE_ADDR_LEN){if(copy_to_user(buf + sizeof(unsigned int) + sizeof(unsigned int), (unsigned int *)(axi_base_addr + start_addr), len) != 0){printk(KERN_ERR"copy to user error!!\n");return -1;}}else{printk("%s read num beyond AXI_ADDRESS_LEN!!\n",__FUNCTION__);return -1;}return len;
}
//设备IO Control
static long custom_axi_ioctl(struct file *flip, unsigned int cmd, unsigned long arg)
{void __user *argp = (void __user *)arg;unsigned int reg_value = 0;unsigned int reg_addr = 0;switch(cmd){case CMD_GET_FPGA_VERSION://读FPGA版本号reg_value=__raw_readl(axi_base_addr);if(copy_to_user(argp,(unsigned int *)&reg_value ,sizeof(unsigned int))){printk("%s copy_to_user is error!!\n",__FUNCTION__);return -1;}break;case CMD_GET_FPGA_TEMP://读FPGA温度reg_value=__raw_readl((axi_base_addr + sizeof(unsigned int)));if(copy_to_user(argp,(unsigned int *)&reg_value ,sizeof(unsigned int))){printk("%s copy_to_user is error!!\n",__FUNCTION__);return -1;}break;case CMD_READ_REG://读axi寄存器if(copy_from_user((unsigned int *)&reg_addr,argp,sizeof(unsigned int))){printk("%s copy_from_user is error!!\n",__FUNCTION__);return -1;}reg_value=__raw_readl((axi_base_addr + reg_addr));if(copy_to_user(argp + sizeof(unsigned int),(unsigned int *)&reg_value ,sizeof(unsigned int))){printk("%s copy_to_user is error!!\n",__FUNCTION__);return -1;}break;case CMD_WRITE_REG://写axi寄存器if(copy_from_user((unsigned int *)&reg_addr,argp,sizeof(unsigned int))){printk("%s copy_from_user is error!!\n",__FUNCTION__);return -1;}if(copy_from_user((unsigned int *)&reg_value,argp+sizeof(unsigned int),sizeof(unsigned int))){printk("%s copy_from_user is error!!\n",__FUNCTION__);return -1;}__raw_writel(reg_value,(axi_base_addr + reg_addr));break;default:break;}return sizeof(unsigned int);//返回操作字节数
}
//中断
static int irq_drv_fasync (int fd, struct file *filp, int on)
{return fasync_helper (fd, filp, on, &irq_async);
}//中断触发函数
static irqreturn_t irq_interrupt(int irq, void *dev_id)
{kill_fasync (&irq_async, SIGIO, POLL_IN);return 0;
}struct file_operations axi_fops = {.owner = THIS_MODULE,.open = custom_axi_open,.release = custom_axi_release,.read = custom_axi_read,.write = custom_axi_write,.fasync = irq_drv_fasync,.unlocked_ioctl = custom_axi_ioctl
};static int custom_axi_probe(struct platform_device *pdev)
{int ret;ptstruct_axi_driver pt_axidriver = NULL;printk("axi probe start!\n");/************************************************************************ register character device    ***********************************************************************/printk("axi-reg-driver: register  device...\n");pt_axidriver = (ptstruct_axi_driver)kmalloc(sizeof(struct_axi_driver),GFP_KERNEL);if (pt_axidriver == NULL){printk(KERN_ERR"%s kmalloc is error!\n",__FUNCTION__);return -1;}axi_base_addr = ioremap(AXI_BASE_ADDR, AXI_BASE_ADDR_LEN);//映射本地地址if (!axi_base_addr) {printk(KERN_ERR"%s ioremap AXI_base is error!!\n",__FUNCTION__);goto error5;}pt_axidriver->pdev = pdev;platform_set_drvdata(pdev, pt_axidriver);pt_axidriver->drv_fops = &axi_fops;strcpy(pt_axidriver->axi_name,DEVICE_NAME);/*从系统获取主设备编号*/ret = alloc_chrdev_region(&dev_id, 0, 5, pt_axidriver->axi_name);if (ret < 0){printk(KERN_ERR"cannot alloc_chrdev_region!\n");goto error4;}pt_axidriver->major = MAJOR(dev_id);printk("MAJOR dev is successed!\n");cdev_init(&pt_axidriver->axi_cdev , &axi_fops); //初始化axi结构,fopsprintk("axi_cdev init!\n");if(cdev_add(&pt_axidriver->axi_cdev , dev_id, 5) != 0){printk(KERN_ERR"add axi error\n");goto error3;}printk("axi_cdev add ok!\n");pt_axidriver->axi_class = class_create(THIS_MODULE, pt_axidriver->axi_name);if(pt_axidriver->axi_class==NULL){printk(KERN_ERR"creat axi class error\n");goto error2;}printk("axi_cdev class init!\n");pt_axidriver->axi_class_dev = device_create(pt_axidriver->axi_class, NULL, MKDEV(pt_axidriver->major, 0), NULL, pt_axidriver->axi_name);if(pt_axidriver->axi_class_dev == NULL){printk(KERN_ERR"device_create is error!\n");goto error1;}printk("axi_cdev class dev init!\n");irq = platform_get_irq(pdev,0);if (irq <= 0){goto error0;}printk("system irq  = %d\n", irq);//申请上升沿中断触发ret = request_irq(irq,irq_interrupt,IRQF_TRIGGER_RISING | IRQF_ONESHOT,pt_axidriver->axi_name, NULL);if (ret) {printk(KERN_ERR"irq_probe irq	error!\n");goto error0;}return 0;free_irq(irq, NULL);
error0:device_destroy(pt_axidriver->axi_class,MKDEV(pt_axidriver->major, 0));
error1:class_destroy(pt_axidriver->axi_class);
error2:cdev_del(&pt_axidriver->axi_cdev);   //移除字符设备
error3:unregister_chrdev_region(dev_id, 5);
error4:iounmap(axi_base_addr);//释放地址映射
error5:kfree(pt_axidriver);return -1;
}static int  custom_axi_remove(struct platform_device *pdev)
{ptstruct_axi_driver  pt_axidriver = (ptstruct_axi_driver)platform_get_drvdata(pdev);free_irq(irq, NULL);//释放中断device_destroy(pt_axidriver->axi_class,MKDEV(pt_axidriver->major, 0));class_destroy(pt_axidriver->axi_class);cdev_del(&pt_axidriver->axi_cdev);       //移除字符设备unregister_chrdev_region(dev_id, 5);    //释放设备iounmap(axi_base_addr);                 //释放axi虚拟地址映射kfree(pt_axidriver);                    //释放驱动return 0;
}/*定义,初始化平台驱动*/
static const struct of_device_id custom_axi_of_match[] = {{.compatible = "lsl,custom_axi_driver"},{},
};
MODULE_DEVICE_TABLE(of, custom_axi_of_match);
static struct platform_driver custom_axi_platform_driver = {.driver = {.name = "custom_axi_driver",.of_match_table = custom_axi_of_match,},.probe = custom_axi_probe,.remove = custom_axi_remove,
};module_platform_driver(custom_axi_platform_driver);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("lsl custom_axi_driver platform driver");
MODULE_AUTHOR("LSL");

3.在axi驱动目录下,新建Makefile文件,Makefile文件中指定内核源码路径。如下图所示:

图6 新建Makefile文件
图7 Makefile文件内容
图8 给Makefile文件添加可执行权限

4.在axi驱动目录下,新建编译脚本build.sh,编译脚本内容如下所示:

图9 新建编译脚本
图10 编译脚本build.sh内容
图11  编译脚本添加可执行权限

5.运行编译脚本,对驱动进行动态编译,生成驱动的ko文件custom_axi_driver.ko,编译结果如下所示:

图12 生成驱动的ko文件

 3 驱动测试程序

1.新建驱动测试程序目录app_driver_test,如下图所示:

图13 新建app_driver_test目录

2.在驱动测试目录下,新建驱动测试软件app_driver_test.c,驱动测试源文件如下图所示:

图14 新建app_driver_test.c的源文件

3.在驱动测试目录下,新建驱动测试软件app_driver_test.c,驱动测试源文件如下图所示:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <linux/ioctl.h>
#include "app_driver_test.h"int pl_axi_fd;
static int irq_count = 0;int axi_writeReg(unsigned int reg,unsigned int value, unsigned int axi_offset_addr)
{Axi_CtrlRwStruct Axi_CtrlRwStructure;int ret;Axi_CtrlRwStructure.reg_addr=REG_ADDR(reg)+axi_offset_addr;Axi_CtrlRwStructure.reg_value=value;ret = ioctl(pl_axi_fd,CMD_WRITE_REG,&Axi_CtrlRwStructure);  if(ret<0){printf("axi_writeReg addr %xis error!\n",Axi_CtrlRwStructure.reg_addr);return -1;}return ret;
}int axi_readReg(unsigned int reg,unsigned int *value,unsigned int axi_offset_addr)
{int ret=0;Axi_CtrlRwStruct Axi_CtrlRwStructure;Axi_CtrlRwStructure.reg_addr=REG_ADDR(reg)+axi_offset_addr;ret = ioctl(pl_axi_fd,CMD_READ_REG,&Axi_CtrlRwStructure);  if(ret<0){printf("axi_readReg addr %xis error!\n",Axi_CtrlRwStructure.reg_addr);return -1;}*value=Axi_CtrlRwStructure.reg_value;return ret;
}int axi_GetFpga_Version(unsigned int *version)
{unsigned int buf;int ret;ret = ioctl(pl_axi_fd,CMD_GET_FPGA_VERSION,&buf);  if(ret<0){printf("axi_GetFpga_Version is error!\n");return -1;}*version=buf;return ret;
}int axi_GetFpga_Temp(unsigned int *temp)
{unsigned int buf;int ret;ret = ioctl(pl_axi_fd,CMD_GET_FPGA_TEMP,&buf);  if(ret<0){printf("axi_GetFpga_Temp is error!\n");return -1;}*temp=buf;return ret;
}int axi_writeData(unsigned int start_addr, unsigned int len, unsigned char *value)
{int ret;Axi_DataRwStruct Axi_DataRwStructure;if(len>MAX_DATA_BUFF_SIZE){printf("write the data len beyond Max buffer!\n");return -1;}Axi_DataRwStructure.start_addr=(start_addr/4)*4;//开始地址4字节对齐Axi_DataRwStructure.len=(len/4)*4;//长度4字节对齐memcpy(Axi_DataRwStructure.buff,value,Axi_DataRwStructure.len);ret=write(pl_axi_fd, &Axi_DataRwStructure, Axi_DataRwStructure.len+2*sizeof(unsigned int));if(ret<0){printf("axi_writeData is error!\n");return -1;}return ret;
}int axi_readData(unsigned int start_addr, unsigned int len, unsigned char *value)
{int ret;Axi_DataRwStruct Axi_DataRwStructure;if(len>MAX_DATA_BUFF_SIZE){printf("read the data len beyond Max buffer!\n");return -1;}Axi_DataRwStructure.start_addr=(start_addr/4)*4;//开始地址4字节对齐Axi_DataRwStructure.len=(len/4)*4;//长度4字节对齐ret=read(pl_axi_fd, &Axi_DataRwStructure, Axi_DataRwStructure.len+2*sizeof(unsigned int));if(ret<0){printf("axi_readData is error!\n");return -1;}memcpy(value,Axi_DataRwStructure.buff,ret);return ret;
} void pl_axi_device_init(void)
{int Oflags; pl_axi_fd = open(PL_AXI_DEV, O_RDWR);if (pl_axi_fd < 0){printf("can't open PL_AXI_DEV!\n");}fcntl(pl_axi_fd, F_SETOWN, getpid());Oflags = fcntl(pl_axi_fd, F_GETFL); fcntl(pl_axi_fd, F_SETFL, Oflags | FASYNC);}void sigio_irq_service(int signum)
{irq_count+=1;printf("recv the irq_count=%d\n",irq_count);//打印irq中断计数}  void pl_sigio_irq_init(void)
{signal(SIGIO, sigio_irq_service);//注册irq服务函数
}//main入口函数
int main(int argc, char **argv)
{unsigned int ver,temp;//定义版本号和温度的变量unsigned int reg_in=0x5aa51234;unsigned int reg_out;unsigned char data_in[12]={1,2,3,4,5,6,7,8,9,10,11,12};unsigned char data_out[12];unsigned int i,flag;pl_sigio_irq_init(); //中断使能pl_axi_device_init();//pl_axi设备打开axi_GetFpga_Version(&ver);printf("the fpga version is=0x%x\n",ver);//打印版本号axi_GetFpga_Temp(&temp);printf("the fpga temp is=0x%x\n",temp);//打印温度while(1){axi_writeReg(8,reg_in, 0);//向8号寄存器写入reg_in的值axi_readReg(8,&reg_out,0);if(reg_in==reg_out)//判断读写寄存器函数是否正确{printf("write read reg is right!\n");}else{printf("write read reg is error!\n");}axi_writeData(8,sizeof(data_in),data_in);//向8号寄存器写入连续的数据axi_readData(8,sizeof(data_out),data_out);for(i=0;i<sizeof(data_out);i++){if(data_in[i]!=data_out[i])//判断读写寄存器函数是否正确{flag=1;printf("write read data is error!\n");}}if(flag==0)printf("write read data is right!\n"); }return 0;
}

4.在axi驱动目录下,新建app_driver_test.h的头文件。头文件内容如下图所示:

图15 app_driver_test.h头文件

5.在axi驱动测试程序目录下,新建Makefile文件,Makefile指定交叉编译器环境变量。如下图所示:

图16 新建Makefile文件
图17 Makefile文件内容

6.在axi驱动目录下,新建编译脚本build.sh,编译脚本内容如下所示:

图18 新建编译脚本
图19 编译脚本build.sh内容
图20 编译脚本添加可执行权限

7.运行编译脚本,对驱动测试程序进行编译,生成驱动测试程序的可执行文件app_driver_test,编译结果如下所示:

图21 生成可执行文件

4 驱动与测试软件的验证

1.拷贝驱动ko文件和驱动测试程序可执行文件到嵌入式系统的工作目录下。

tftp -g -r custom_axi_driver.ko 192.168.0.11

tftp -g -r app_driver_test 192.168.0.11

chmod +x app_driver_test

图22 驱动和测试软件的下载

2.驱动加载

insmod custom_axi_driver.ko

图23 驱动程序加载

3.运行嵌入式程序的可执行文件。

./app_driver_test

图24 驱动程序测试,获得AXI的信息

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

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

相关文章

Python浪漫之画星星

效果图&#xff08;动态的哦&#xff01;&#xff09;&#xff1a; 完整代码&#xff08;上教程&#xff09;&#xff1a; import turtle import random import time # 导入time模块# 创建一个画布 screen turtle.Screen() screen.bgcolor("red")# 创建一个海龟&a…

Coppelia Sim (v-REP)仿真 机器人3D相机手眼标定与实时视觉追踪 (一)

coppelia sim[V-REP]仿真实现 机器人于3D相机手眼标定与实时视觉追踪 一 标定板的制作生成标定的PDF文件PDF转为图像格式图像加载到仿真中 二 仿真场景设置加载机器人加载的控制dummy ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b48549d355d8441d8dfc20bc7ba7196…

MYSQL作业三

准备工作 查询操作

Linux复习-C++

参考博客&#xff1a; https://blog.csdn.net/qq_45254369/article/details/126023482?ops_request_misc%257B%2522request%255Fid%2522%253A%252277629891-A0F3-4EFC-B1AC-410093596085%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&req…

力扣 167. 两数之和 II - 输入有序数组

目录 一、题目二、思路三、代码 一、题目 二、思路 由于数组是 非递减顺序排列 的&#xff0c;利用该先验知识&#xff0c;可以设置两个指针在数组的起始和末尾&#xff0c;相向前进&#xff0c;与暴力枚举相比可以加快寻找速度。 三、代码 class Solution {public int[] tw…

面向对象(上)

1.初始对象 学习目标&#xff1a;理解使用对象完成数据组织的思路 可以看到加入了登记表后&#xff0c;我们学生所提供的信息就组织的简洁明了 程序中也是有多种多样的方式来记录数据 思考&#xff1a;使用变量记录数据太乱了&#xff0c;如果程序中也和生活中一样&#xff…

Linux - 动静态库

文章目录 一、静态库1、定义2、文件扩展名3、生成静态库4、使用静态库5、静态库的优缺点 二、动态库1、定义2、文件扩展名3、生成动态库4、使用动态库5、动态库的优缺点6、简单动态库加载 三、补充 一、静态库 1、定义 静态库是在编译时链接到最终的可执行文件中的库。这意味着…

Java Web项目实战:从零基础到项目开发全流程

Java Web开发环境搭建 Java Web开发需要Java运行环境、IDE&#xff08;如IntelliJ IDEA或Eclipse&#xff09;以及Maven集成开发工具等。首先&#xff0c;确保电脑上安装有Java 8或更高版本&#xff0c;可以通过访问Java官网获取最新版本。其次&#xff0c;安装IDE&#xff0c…

深入理解跳出率:如何利用百度统计优化网站用户体验

在数字营销的世界中&#xff0c;跳出率是一个关键指标&#xff0c;它衡量了用户对网站的第一印象和内容的吸引力。最近&#xff0c;我在分析我的网站数据时发现&#xff0c;跳出率常常高达100%&#xff0c;这促使我深入研究了跳出率的含义和影响因素。 跳出率的定义与计算 跳…

AndroidStudio Koala更改jdk版本 2024-1-2

升级android studio版本到 AndroidStudio Koala之后&#xff0c;发现修改jdk版本的方式不同了。如下&#xff1a; 修改jdk版本

《Python网络安全项目实战》

《Python网络安全项目实战》 项目1 Python 环境安装任务1.1 Windows上安装Python任务1.2 Ubuntu环境下安装Python 项目2 Python基础练习任务2.1 使用数据类型任务2.2 使用组合数据类型任务2.3 使用控制结构任务2.4 使用函数任务2.5 使用模块 项目3 处理文件中的数据任务3.1 读文…

Ajax:XMLHttpRequest

Ajax&#xff1a;XMLHttpRequest XMLHttpRequestgeturlpost 数据交换格式XMLjson XMLHttpRequest Level 2请求时限表单数据操纵文件上传 XMLHttpRequest XMLHttpRequest简称 xhr&#xff0c;是浏览器提供的 Javascript 对象&#xff0c;通过它可以请求服务器上的数据资源。 jQ…

如何在vscode中使用鼠标滑轮滚动来改变字体大小

实现内容&#xff1a;如何在vscode中使用鼠标滑轮滚动来改变字体大小 使用场景&#xff1a;我是在Ubuntu中安装的vscode 需求&#xff1a;因为最近在用这个&#xff0c;但是在使用过程中发现vscode的字体大小有点小&#xff0c;所以想改变下 实现滚轮滑动改变字体大小的具体步…

Spring Boot框架下的酒店住宿登记系统

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

2024 7月算法刷题记录

小米真题 手机流畅运行的秘密 时间限制&#xff1a;1.000S 空间限制&#xff1a;256MB 题目描述 8 月份发布会一结束&#xff0c;米小兔就在公司领到了一台最新发布的 Xiaomi MIX Fold 3 手机&#xff0c;这是一款小米旗舰折叠屏手机&#xff0c;并搭载了全新升级架构的 MI…

Redis 线程控制 总结

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 线程控制 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 线程控制 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis &a…

架构师备考-数据库设计、实施和维护

数据库设计 数据库设计的任务是针对一个给定的应用环境&#xff0c;在给定的硬件环境和操作系统及数据库管理系统等软件环境下&#xff0c;创建一个性能良好的数据库模式&#xff0c;建立数据库及其应用系统&#xff0c;使之能有效地存储和管理数据&#xff0c;满足各类用户的需…

Node.js 模块化

1. 介绍 1.1 什么是模块化与模块 ? 将一个复杂的程序文件依据一定规则&#xff08;规范&#xff09;拆分成多个文件的过程称之为 模块化其中拆分出的 每个文件就是一个模块 &#xff0c;模块的内部数据是私有的&#xff0c;不过模块可以暴露内部数据以便其他模块使用 1.2 什…

Stable Diffusion视频插件Ebsynth Utility使用方法

在Stable Diffusion中安装完Ebsynth Utility后&#xff0c;就可以开始试用了。 启动Stable Diffusion后&#xff0c;出面画面&#xff1a; 1、步骤1&#xff1a;视频分帧及生成蒙板帧 填入工程目录&#xff0c;选择上传所用的视频文件&#xff1a;注意对目录命名的要求-不能有…

尽管加密货币被禁,中国仍是比特币挖矿巨头!不过主导地位正在转向美国?

尽管中国政府全面禁止了加密货币交易和挖矿活动&#xff0c;但中国依然在比特币挖矿领域保持着全球领先地位。然而&#xff0c;随着美国在该领域的快速崛起&#xff0c;中国在比特币挖矿主导地位方面正面临来自美国的强劲竞争。根据最新的市场数据和行业分析&#xff0c;中国的…