当前位置: 首页 > article >正文

驱动开发(2)|鲁班猫rk3568简单GPIO波形操控

   上篇文章写了如何下载内核源码、编译源码的详细步骤,以及一个简单的官方demo编译,今天分享一下如何根据板子的引脚写自己控制GPIO进行高低电平反转。
想要控制GPIO之前要学会看自己的引脚分布图,我用的是鲁班猫RK3568,引脚分布图如下所示:
在这里插入图片描述
具体板子的引脚示意图可以在这里看:教程官网

1 通过shell命令进行GPIO控制

1.1 使用GPIO sysfs接口控制IO

#以下所有操作均需要打开管理者权限使用
#使能引脚GPIO1_A0
echo 32 > /sys/class/gpio/export#设置引脚为输入模式
echo in > /sys/class/gpio/gpio32/direction
#读取引脚的值
cat /sys/class/gpio/gpio32/value#设置引脚为输出模式
echo out > /sys/class/gpio/gpio32/direction
#设置引脚为低电平
echo 0 > /sys/class/gpio/gpio32/value
#设置引脚为高电平
echo 1 > /sys/class/gpio/gpio32/value#复位引脚
echo 32 > /sys/class/gpio/unexport

这里需要注意的是,设置引脚模式为输出模式之后才能对引脚高低电平进行设置,其他的没什么好主意的,多敲几遍啥都懂了。至于官方给的计算引脚的位置,我根本没看,因为图中都给你算好了,文章开头的引脚分布图中的编号一列就为引脚具体的值。

1.2 使用libgpiod控制IO

首先要下载libgpio:

sudo apt install gpiod

具体使用一共就这几个接口:
在这里插入图片描述

设置GPIO1_A1为高电平:

gpioset 1 1=1

设置GPIO1_A1为低电平:

gpioset 1 1=0

这里的gpioset后面第一个1是GPIO的组号,因为是GPIO1所以为1,我想换个板子一共五组(GPIO0-GPIO4),所以范围是0-4。第二个是索引号,具体计算方式可以参照下图:
在这里插入图片描述

2 通过代码来操控GPIO接口

2.1 通过GPIO 子系统设置引脚

直接使用gpio_set_value,这种方式与1.1类似,很简单,也是直接看引脚途中的编号就可以:

//设置GPIO1_A0为高电平
gpio_set_value(32,1);
//设置GPIO1_A0为低电平
gpio_set_value(32,0);

这种方式操作GPIO,就算直接拉高在拉低操作延时都会在500ns左右。
这是我写的部分代码,感兴趣的兄弟可以看一下:
pin_ctl.c:

#include "pin_ctl.h"void set_ce_high()
{gpio_set_value(GPIO_A1, data & 0x01);
}
static void set_pinA_value(u8 data, int signal)
{if(signal == 0){//如果是上电时序和下电时序0gpio_set_value(GPIO_A1, data & 0x01);}else{//不是上电时序,是选择喷头gpio_set_value(GPIO_A1, data & 0x01);gpio_set_value(GPIO_A2, (data >> 1) & 0x01);gpio_set_value(GPIO_A3, (data >> 2) & 0x01);gpio_set_value(GPIO_A4, (data >> 3) & 0x01);gpio_set_value(GPIO_A5, (data >> 4) & 0x01);gpio_set_value(GPIO_A6, (data >> 5) & 0x01);gpio_set_value(GPIO_A7, (data >> 6) & 0x01);}
}
static void set_pinD_value(u8 data, int signal)
{if(signal == 0){//如果是上电时序和下电时序0gpio_set_value(GPIO_D1, data & 0x01);}else{//不是上电时序gpio_set_value(GPIO_D1, data & 0x01);gpio_set_value(GPIO_D2, (data >> 1) & 0x01);gpio_set_value(GPIO_D3, (data >> 2) & 0x01);gpio_set_value(GPIO_D4, (data >> 3) & 0x01);}}
static void set_pinS_value(u8 data, int signal)
{if(signal == 0){//如果是上电时序和下电时序0gpio_set_value(GPIO_S1, data & 0x01);}else{//不是上电时序gpio_set_value(GPIO_S1, data & 0x01);gpio_set_value(GPIO_S2, (data >> 1) & 0x01);gpio_set_value(GPIO_S3, (data >> 2) & 0x01);gpio_set_value(GPIO_S4, (data >> 3) & 0x01);}}// 上电函数
static void power_on_sequence(void)
{gpio_set_value(GPIO_VL, 1);msleep(TPO_MS);gpio_set_value(GPIO_VPK, 1);gpio_set_value(GPIO_VPC, 1);// 参考书上最小0.5us//udelay(TW_US);udelay(TW_US);gpio_set_value(GPIO_CE, 1);// 6. 等待tn时间(5us),参考书上最小5usudelay(TN_US);gpio_set_value(GPIO_CK, 1);//1111->0x0Fset_pinD_value(0x0F, 0);gpio_set_value(GPIO_SH, 1); //1111111->0x7Fset_pinA_value(0x7F, 0);//1111->0x0Fset_pinS_value(0x0F, 0);printk(KERN_INFO "Full power sequence completed\n");
}
//下电函数
static void power_off_sequence(void)
{//下电顺序S->A->SH->D->CH->5us->CE->0.5/1us->VPC->VPK->1ms->VLset_pinS_value(0x00, 0);set_pinA_value(0x00, 0);gpio_set_value(GPIO_SH, 0);set_pinD_value(0x00, 0);gpio_set_value(GPIO_CK, 0);udelay(TN_US);gpio_set_value(GPIO_CE, 0);udelay(TW_US);gpio_set_value(GPIO_VPK, 0);gpio_set_value(GPIO_VPC, 0);msleep(TPO_MS);gpio_set_value(GPIO_VL, 0);}
void SetPinDValue(u8 data)
{(data & 0x01) == 1 ? set_hig(5):set_low(5);((data >> 1) & 0x01) == 1 ? set_hig(6):set_low(6);((data >> 2) & 0x01) == 1 ? set_hig(7):set_low(7);((data >> 3) & 0x01) == 1 ? set_hig(8):set_low(8);}// 打印时序
static void print_sequence(void)
{//上电顺序//CE->CK->D1-D4->SH->A1-A7->S1-S4//选择第一个喷头(A1-A7)set_pinA_value(0x07, 1);gpio_set_value(GPIO_CK, 0);ndelay(50);gpio_set_value(GPIO_CE, 1);ndelay(50);gpio_set_value(GPIO_CK, 1);ndelay(10);set_pinD_value(0x0F, 1);ndelay(40);}static int ck_thread_func(void *data)
{while (!kthread_should_stop()) {//ck信号测试//set_ck_one_cycle();}printk(KERN_INFO "Power CK thread is stopping...\n");return 0;
}MODULE_LICENSE("GPL");
MODULE_AUTHOR("limingzhao");
MODULE_DESCRIPTION("inkjet enable pro");
//上电时序信号
EXPORT_SYMBOL(power_on_sequence);
//下电时序信号
EXPORT_SYMBOL(power_off_sequence);
//设置引脚
EXPORT_SYMBOL(set_pinA_value);
EXPORT_SYMBOL(set_pinD_value);
EXPORT_SYMBOL(set_pinS_value);
//测试程序pin_s_test
EXPORT_SYMBOL(pin_s_test);
EXPORT_SYMBOL(pin_s_test1);
EXPORT_SYMBOL(pin_s_test2);

2.2 直接操作硬件寄存器

这是官方给的示例代码,功能是点亮板子上的一个led灯:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>#define DEV_NAME            "led_chrdev"
#define DEV_CNT                 (1)#define GPIO0_BASE (0xfdd60000)
//每组GPIO,有2个寄存器,对应32个引脚,每个寄存器负责16个引脚;
//一个寄存器32位,其中高16位都是使能位,低16位对应16个引脚,每个引脚占用1比特位
#define GPIO0_DR_L (GPIO0_BASE + 0x0000)
#define GPIO0_DR_H (GPIO0_BASE + 0x0004)
#define GPIO0_DDR_L (GPIO0_BASE + 0x0008)
#define GPIO0_DDR_H (GPIO0_BASE + 0x000C)static dev_t devno;
struct class *led_chrdev_class;struct led_chrdev {struct cdev dev;unsigned int __iomem *va_dr; 	// 数据寄存器,设置输出的电压unsigned int __iomem *va_ddr; 	// 数据方向寄存器,设置输入或者输出unsigned int led_pin; // 偏移
};static int led_chrdev_open(struct inode *inode, struct file *filp)
{	unsigned int val = 0;struct led_chrdev *led_cdev = (struct led_chrdev *)container_of(inode->i_cdev, struct led_chrdev,dev);filp->private_data = container_of(inode->i_cdev, struct led_chrdev, dev);printk("open\n");//设置输出模式val = ioread32(led_cdev->va_ddr);val |= ((unsigned int)0x1 << (led_cdev->led_pin+16));val |= ((unsigned int)0X1 << (led_cdev->led_pin));iowrite32(val,led_cdev->va_ddr);//输出高电平val = ioread32(led_cdev->va_dr);val |= ((unsigned int)0x1 << (led_cdev->led_pin+16));val |= ((unsigned int)0x1 << (led_cdev->led_pin));iowrite32(val, led_cdev->va_dr);return 0;
}static int led_chrdev_release(struct inode *inode, struct file *filp)
{return 0;
}static ssize_t led_chrdev_write(struct file *filp, const char __user * buf,size_t count, loff_t * ppos)
{unsigned long val = 0;char ret = 0;struct led_chrdev *led_cdev = (struct led_chrdev *)filp->private_data;printk("write \n");get_user(ret, buf);val = ioread32(led_cdev->va_dr);printk("val = %lx\n", val);if (ret == '0'){val |= ((unsigned int)0x1 << (led_cdev->led_pin+16));val &= ~((unsigned int)0x01 << (led_cdev->led_pin));   /*设置GPIO引脚输出低电平*/}else{val |= ((unsigned int)0x1 << (led_cdev->led_pin+16));val |= ((unsigned int)0x01 << (led_cdev->led_pin));    /*设置GPIO引脚输出高电平*/}iowrite32(val, led_cdev->va_dr);printk("val = %lx\n", val);return count;
}static struct file_operations led_chrdev_fops = {.owner = THIS_MODULE,.open = led_chrdev_open,.release = led_chrdev_release,.write = led_chrdev_write,
};static struct led_chrdev led_cdev[DEV_CNT] = {{.led_pin = 7}, 	//偏移,高16引脚,GPIO0_C7
};static __init int led_chrdev_init(void)
{int i = 0;dev_t cur_dev;printk("led_chrdev init (lubancat2  GPIO0_C7)\n");led_cdev[0].va_dr   = ioremap(GPIO0_DR_H, 4);	 //led_cdev[0].va_ddr  = ioremap(GPIO0_DDR_H, 4);	 // alloc_chrdev_region(&devno, 0, DEV_CNT, DEV_NAME);led_chrdev_class = class_create(THIS_MODULE, "led_chrdev");for (; i < DEV_CNT; i++) {cdev_init(&led_cdev[i].dev, &led_chrdev_fops);led_cdev[i].dev.owner = THIS_MODULE;cur_dev = MKDEV(MAJOR(devno), MINOR(devno) + i);cdev_add(&led_cdev[i].dev, cur_dev, 1);device_create(led_chrdev_class, NULL, cur_dev, NULL,DEV_NAME "%d", i);}return 0;
}module_init(led_chrdev_init);static __exit void led_chrdev_exit(void)
{int i;dev_t cur_dev;printk("led chrdev exit (lubancat2  GPIO0_C7)\n");for (i = 0; i < DEV_CNT; i++) {iounmap(led_cdev[i].va_dr); 		// 释放模式寄存器虚拟地址iounmap(led_cdev[i].va_ddr); 	// 释放输出类型寄存器虚拟地址}for (i = 0; i < DEV_CNT; i++) {cur_dev = MKDEV(MAJOR(devno), MINOR(devno) + i);device_destroy(led_chrdev_class, cur_dev);cdev_del(&led_cdev[i].dev);}unregister_chrdev_region(devno, DEV_CNT);class_destroy(led_chrdev_class);}module_exit(led_chrdev_exit);MODULE_AUTHOR("embedfire");
MODULE_LICENSE("GPL");

具体源代码位置:led_cdev.c
这种方式操作GPIO,直接拉高在拉低操作延时在160ns左右。
这里如果兄弟们想封装自己的GPIO接口,可以通过官方文档去查找:引脚说明
简单来说就是将寄存器分为了高位寄存器和低位寄存器,其中A、B两组为地位寄存器,C和D为高位寄存器,查看Rockchip_RK3568_TRM_Part1_V1.3-20220930P.PDF可以知道GPIO0的基地址为:
在这里插入图片描述
GPIO1、GPIO2、GPIO3、GPIO4的基地址为:
在这里插入图片描述
所以可以知道上述代码中地址宏定义:

//GPIO0
#define GPIO0_BASE (0xfdd60000)
//AB用这俩
#define GPIO0_DR_L (GPIO0_BASE + 0x0000)
#define GPIO0_DDR_L (GPIO0_BASE + 0x0008)
//CD用这俩
#define GPIO0_DR_H (GPIO0_BASE + 0x0004)
#define GPIO0_DDR_H (GPIO0_BASE + 0x000C)

的出处,明显可以看到这是使用的GPIO0,至于引脚设置,可以看这里:
在这里插入图片描述
具体出处及来源,看这里5.4.2.1. 定义GPIO寄存器物理地址

总结

  本文主要分享了GPIO控制的四种方式,shell两种控制方式,和使用代码控制的两种方式,重点要说的是如果你追求极致性能和对硬件有较高的控制要求,ioread32/iowrite32 可能更合适。反之,若追求易用性和代码的可维护性,gpio_set_value 更为推荐。

相关文章:

驱动开发(2)|鲁班猫rk3568简单GPIO波形操控

上篇文章写了如何下载内核源码、编译源码的详细步骤&#xff0c;以及一个简单的官方demo编译&#xff0c;今天分享一下如何根据板子的引脚写自己控制GPIO进行高低电平反转。 想要控制GPIO之前要学会看自己的引脚分布图&#xff0c;我用的是鲁班猫RK3568&#xff0c;引脚分布图如…...

《软件工程》第 3 章 -需求工程概论

在软件工程的开发流程中&#xff0c;需求工程是奠定项目成功基础的关键环节。它专注于获取、分析、定义和管理软件需求&#xff0c;确保开发出的软件能真正满足用户需求。接下来&#xff0c;我们将按照目录内容&#xff0c;结合 Java 代码和实际案例&#xff0c;深入讲解需求工…...

VMware-MySQL主从

MySQL主从 服务器信息 服务器类型角色主机地址主机名称虚拟机master192.168.40.128test-1虚拟机slave192.168.40.129test-2 Master 配置&#xff08;192.168.40.128&#xff09; 删除自动生成的配置 /var/lib/mysql/auto.cnf [roottest-1 ~]# rm -rf /var/lib/mysql/auto.…...

ArcGIS Pro 3.4 二次开发 - 几何

环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 几何1 空间参考1.1 从已知ID构建空间参考1.2 从字符串构建空间参考1.3 使用 WGS84 空间参考1.4 使用已知ID构建带有垂直坐标系的空间参考1.5 使用垂直坐标系从字符串构建SpatialReference1.6 使用自定义投影坐标系(PCS)构建空间参…...

2023-ICLR-ReAct 首次结合Thought和Action提升大模型解决问题的能力

关于普林斯顿大学和Google Research, Brain Team合作的一篇文章, 在语言模型中协同Reasoning推理和Action行动。 论文地址&#xff1a;https://arxiv.org/abs/2210.03629 代码&#xff1a;https://github.com/ysymyth/ReAct.git 其他复现 langchain &#xff1a;https://pytho…...

Rust 开发的一些GUI库

最近考虑用Rust干点什么&#xff0c;于是搜集了下资料——根据2025年最新调研结果和社区实践&#xff0c;Rust GUI库生态已形成多个成熟度不同的解决方案。以下是当前主流的GUI库分类及特点分析&#xff0c;结合跨平台支持、开发体验和实际应用场景进行综合评估&#xff1a; 一…...

【第四十六周】文献阅读:从 RAG 到记忆:大型语言模型的非参数持续学习

目录 摘要Abstract从 RAG 到记忆&#xff1a;大型语言模型的非参数持续学习研究背景方法论1. 离线索引&#xff08;Offline Indexing&#xff09;2. 在线检索&#xff08;Online Retrieval&#xff09;具体细节 创新性实验结果局限性总结 摘要 本论文旨在解决当前检索增强生成…...

从智能提效到产品赋能的架构实践

摘要 本文深入探讨了企业级系统从智能化提效阶段向产品赋能阶段演进的架构实践路径。通过分析传统架构的局限性,提出了以用户价值为导向的现代化架构设计理念,并结合实际案例展示了如何构建可扩展、高可用、智能化的产品架构体系。 1. 引言 在数字化转型的浪潮中,企业技术…...

《Python 虚拟环境完全指南:如何管理项目依赖,避免版本冲突》

《Python 虚拟环境完全指南:如何管理项目依赖,避免版本冲突》 1. 引言 在 Python 开发中,依赖管理是至关重要的环节。不同项目可能需要不同的库版本,而全局安装库可能导致版本冲突或环境污染。为解决这一问题,Python 提供了虚拟环境(venv、virtualenv),帮助开发者隔离…...

微信小程序带数组参数跳转页面,微信小程序跳转页面带数组参数

在微信小程序中&#xff0c;带数组参数跳转页面需要通过JSON序列化和URL编码处理&#xff0c;以下是具体实现方法 传递数组参数‌&#xff08;发送页面&#xff09; wx.navigateTo({url: /pages/targetPage?arr encodeURIComponent(JSON.stringify(yourArray)) });接收数组参…...

服务器开机自启动服务

前言&#xff1a; 将服务器中脚本开启自启动执行 步骤&#xff1a; 1.创建一个 systemd 服务文件: /etc/systemd/system/ 目录下创建一个新的服务文件。例如&#xff0c;命名为 myapp.service&#xff1a; sudo nano /etc/systemd/system/myapp.service2.编写 [Unit] Descri…...

关于OT IIOT系统远程访问的零信任安全

什么是OT & IIOT&#xff1f;—— 工业领域的“操作基石”与“智能升级” 在工业数字化转型的浪潮中&#xff0c;OT&#xff08;运营技术&#xff09;与IIoT&#xff08;工业物联网&#xff09;是两个核心概念。前者是工业生产的“神经中枢”&#xff0c;后者是驱动智能升…...

【Doris基础】Apache Doris vs 传统数据仓库:架构与性能的全面对比

目录 1 引言 1.1 传统数据仓库的发展 1.2 现代分析型数据库的崛起 2 核心架构对比 2.1 传统数据仓库的架构 2.2 Doris的架构设计 3 关键技术差异 3.1 存储引擎对比 3.2 查询执行对比 3.3 数据摄入方式对比 4 性能与扩展性对比 4.1 性能基准对比 4.2 扩展性对比 5…...

【VScode】python初学者的有力工具

还记得23年11月&#xff0c;我还在欣喜Spyder像Rstudio一样方便。 但苦于打开软件打开太卡、太耗时&#xff08;初始化-再加载一些东西&#xff09;&#xff0c;一度耗费了我学习的热情。 就在24年5月份&#xff0c;别人推荐下发现了一个更加轻量级、方便又快速的ID&#xff0…...

Linux系统中为Qt项目封装一个udp客户端类

Linux系统中为Qt项目封装一个udp客户端类 一、场景 在日常的Qt项目中,我们常用的就是网络通信协议是TCP/UDP, 对于网络协议,Qt都已经封装好了自己的TCP/UDP类,QTcpSocket/QUdpSocket,这些类非常的好用,也非常的易用。 这些类继承自QAbstractSocket,而QAbstractSocket类…...

443端口:HTTPS通信的安全基石

在互联网通信中&#xff0c;端口是数据传输的虚拟通道&#xff0c;每个端口对应特定的服务或协议。其中&#xff0c;443端口 作为 HTTPS协议 的默认端口&#xff0c;在现代网络安全中扮演着至关重要的角色。 一、443端口的核心作用 HTTPS加密通信 443端口是HTTPS&#xff08;…...

宝塔安装WordPress程序

宝塔安装WordPress程序 一、提前准备1&#xff0c;下载WordPress2&#xff0c;在宝塔创建站点 二、部署项目1&#xff0c;上传下载的wordpress压缩包至创建的项目根目录下并解压 三、wordpress安装1&#xff0c;在浏览器打开创建的网站2&#xff0c;开始按照流程安装配置数据库…...

Agent 的7 中设计模式

这里写自定义目录标题 建立有效的Agent什么是Agent&#xff1f;何时&#xff08;以及何时不使用&#xff09;使用代理何时以及如何使用框架构建块、工作流和Agent构建模块&#xff1a;增强型LLM(The augmented LLM)工作流程&#xff1a;提示链接(Prompt chaining)工作流程&…...

OpenGAN:基于开放数据生成的开放集识别

简介 简介&#xff1a;这次学习的OpenGAN主要学习一个思路&#xff0c;跳出传统GAN对于判断真假的识别到判断是已知种类还是未知种类。重点内容不在于代码而是思路&#xff0c;会简要给出一个设计的代码。 论文题目&#xff1a;OpenGAN: Open-Set Recognition via Open Data …...

【node】Express创建服务器

Express是基于Node.js平台&#xff0c;快速、开放、极简的Web开发框架。基于http的express是专门用来创建web服务器的&#xff0c;可以极大的提高开发效率。 Express的创建的服务器 1 web网站服务器 专门对外提供web网页资源的服务器 2 Api接口服务器 专门对外提供Api接口的服…...

使用 OpenCV 实现哈哈镜效果

在计算机视觉和图像处理领域&#xff0c;OpenCV 提供了非常强大的图像几何变换能力&#xff0c;不仅可以用于纠正图像&#xff0c;还能制造各种“有趣”的视觉效果。今天&#xff0c;我们就来实现一个经典的“哈哈镜”效果&#xff0c;让图像像在游乐园里一样被拉伸、压缩、扭曲…...

DeepSeek-R1-0528 模型最新发布:编程推理能力跃升

2025年5月28日&#xff0c;深度求索&#xff08;DeepSeek&#xff09;通过Hugging Face平台悄然发布推理模型DeepSeek-R1-0528 Hugging Face Deepseek-R1-0528模型地址。尽管官方称其为"minor update"&#xff0c;但社区实测显示&#xff0c;该版本在编程能力、复杂推…...

git仓库服务gogs详解

Gogs&#xff08;Go Git Service&#xff09;是一个使用 Go 编写的自助 Git 服务&#xff0c;旨在提供一个轻量级、易部署、高效的 Git 代码托管平台。它类似于 GitHub、GitLab&#xff0c;但更轻量&#xff0c;非常适合私有化部署、小型团队和嵌入式环境。下面是对 Gogs 的详细…...

PaddleNLP 的文本分类项目

以下是一个基于 PaddleNLP 的文本分类项目&#xff0c;按照标准工程结构组织&#xff0c;并包含测试数据集和完整流程。这个示例使用ERNIE模型处理IMDB电影评论情感分析任务。 项目工程结构 ernie_sentiment_analysis/ ├── data/ # 数据集目录 │ ├─…...

git 一台电脑一个git账户,对应多个仓库ssh

生成ssh # 为账户A生成SSH密钥 ssh-keygen -t rsa -b 4096 -C "your_email_for_account_Aexample.com" -f ~/.ssh/id_ed25519 # 为账户B生成SSH密钥 ssh-keygen -t rsa -b 4096 -C "your_email_for_account_Bexample.com" -f ~/.ssh/id_rsa_yswq进入文件…...

node-DeepResearch开源ai程序用于深入调查查询,继续搜索、阅读网页、推理,直到找到答案

​一、软件介绍 文末提供程序和源码下载 node-DeepResearch开源ai程序用于深入调查查询&#xff0c;继续搜索、阅读网页、推理&#xff0c;直到找到答案。 重要提示 与 OpenAI/Gemini/Perfasciity 的“深度研究”不同&#xff0c;我们只专注于通过迭代过程找到正确的答案 。我…...

Asp.Net Core 托管服务

文章目录 前言一、说明二、使用步骤1.创建托管服务方式一&#xff1a;继承 BackgroundService方式二&#xff1a;直接实现 IHostedService 2.注册托管服务3.处理作用域服务4.使用定时器&#xff08;System.Threading.Timer&#xff09;5.结合 Quartz.NET 实现复杂调度 三、. 注…...

Dockerfile 编写经验:优化大小与效率

文章目录 Dockerfile 通用的技巧总结1. 使用多阶段构建2. 最小化层数3. 彻底清理4. 选择合适的基镜像5. 仅安装必要的依赖6. 利用构建缓存 常见陷阱总结 Dockerfile 通用的技巧总结 1. 使用多阶段构建 利用多阶段构建分离构建和运行时环境&#xff0c;仅将必要的产物&#xff…...

JMeter 是什么

JMeter 是一款由 Apache 基金会开发的 开源性能测试工具&#xff0c;主要用于对 Web 应用、API、数据库、消息队列等系统进行 压力测试、负载测试和功能测试。它通过模拟大量用户并发操作&#xff0c;帮助开发者评估系统的性能、稳定性和扩展能力。以下是其核心特性和使用详解&…...

压测服务器和线上环境的区别

在进行服务器压测时&#xff0c;测试环境与线上环境的差异会直接影响测试结果的可靠性。以下是两者的关键区别及注意事项&#xff1a; ​1. 压测服务器的常见类型​ ​本地开发机​&#xff1a;低配虚拟机&#xff08;如4核8GB&#xff09;&#xff0c;仅用于功能验证&#xf…...