从uCOSii中抠出来的内存管理程序

news2024/11/26 15:47:58

从uCOSii中抠出来的内存管理程序

1、学习uCOSii的内存管理的原因

操作系统和内存管理是分不开的,每个操作系统都有自己的一套内存管理方法。在实际应用中,我们尽量使用其自带的内存管理。学习和使用uCOSii也有一段时间了,觉得它的内存管理方法比较传统,所以就系统学习了一下。为了便于理解,特地把内存管理的主要函数从uCOSii中抠出来,即使没有用过uCOSii的人也可以理解了。

重点掌握内存控制块初始化、内存池初始化、节点的申请和释放、内存的信息及使用情况。

2、程序代码分析

#define OS_MAX_MEM_PART   2  //内存池数量为2

#define InternalRAM1_StartAddress  0x20001000

#define InternalRAM1_TotalNumberOfBytes  20  //定义内部堆区的大小为20字节

#define InternalRAM1_BlockSize      4  //每个存储块大小为4,必须为4的倍数

#define InternalRAM1_NumberOfBlock         InternalRAM1_TotalNumberOfBytes/InternalRAM1_BlockSize

//存储块的数量

//由于一个指针变量占用4字节,所以块的大小一定要为4的倍数

__align(4) uint8_t InternalRAM1_Array[InternalRAM1_NumberOfBlock][InternalRAM1_BlockSize] __attribute__((at(InternalRAM1_StartAddress)));

#if OS_MAX_MEM_PART >= 2u  //至少有2个内存池

#define InternalRAM2_StartAddress  (InternalRAM1_StartAddress+InternalRAM1_TotalNumberOfBytes)

#define InternalRAM2_TotalNumberOfBytes  24  //定义内部堆区的大小为24字节

#define InternalRAM2_BlockSize      8  //每个存储块大小为8,必须为4的倍数

#define InternalRAM2_NumberOfBlock         InternalRAM2_TotalNumberOfBytes/InternalRAM2_BlockSize

//存储块的数量

//由于一个指针变量占用4字节,所以块的大小一定要为4的倍数

__align(4) uint8_t InternalRAM2_Array[InternalRAM2_NumberOfBlock][InternalRAM2_BlockSize] __attribute__((at(InternalRAM2_StartAddress)));

#endif

typedef struct os_mem

{

         void   *OSMemAddr;   /*根节点地址*/

         void   *OSMemFreeList;/*下一个节点的地址*/

         uint32_t  OSMemBlkSize;/*节点的字节数*/

         uint32_t  OSMemNBlks; /*节点的总数量”*/

         uint32_t  OSMemNFree;/*可用节点数量*/

}OS_MEM;

OS_MEM *MemoryControlBlock1_Pointer;//内存控制块指针

OS_MEM *MemoryControlBlock2_Pointer;//内存控制块指针

typedef struct os_mem_data

{

         void   *OSAddr;     //根节点地址

         void   *OSFreeList; //可用的下一个节点的地址

         int32_t  OSBlkSize; //节点的字节数

  int32_t  OSNBlks;   //节点的总数量

  int32_t  OSNFree;   //可用节点数量

  int32_t  OSNUsed;   //已用节点数量

}OS_MEM_DATA;

OS_MEM_DATA My_OS_MEM_DATA;

#define OS_SUCCESS  0u

#define OS_FAIL     1u

#define OS_FULL     2u

函数功能:对两个内存控制块进行初始化,OSMemFreeList指向"内存控制块0的首地址"

void OS_MemInit(void)

{

         int8_t *pdest;//声明pdest"一维指针"

         int16_t size;

         OS_MEM  *pmem;//声明pmem"OS_MEM型结构指针"

         int16_t current,next;

         pdest=(int8_t *)&OSMemTbl[0];//"内存控制块0的首地址"保存到pdest

         size=sizeof(OSMemTbl);//求内存控制块OSMemTbl[]有多少个字节

         while(size>0)//将内存控制块清0

         {

                   *pdest++ = (int8_t)0;

                   size--;

         }

#if OS_MAX_MEM_PART == 1u  //只有1个内存池

         OSMemFreeList=(OS_MEM *)&OSMemTbl[0];

         //"内存控制块0的首地址"保存到OSMemFreeList

#endif

#if OS_MAX_MEM_PART >= 2u  //至少有2个内存池

         for(current=0;current< OS_MAX_MEM_PART-1;current++)

         {

                   pmem = &OSMemTbl[current];

        //"内存控制块current的首地址"保存到pmem

                   next=current+1;

                   pmem->OSMemFreeList = (void *)&OSMemTbl[next];

       //"内存控制块next的首地址"保存到pmem->OSMemFreeList

         }

         pmem= &OSMemTbl[current];

         //"内存控制块current的首地址"保存到pmem

         //这里的OSMemTbl[current]是最后一个内存控制块

         pmem->OSMemFreeList=(void *)0;

         /*由于"最后一个内存控制块"没有"下一个内存控制块",所有设置"下一个内存控制块的地址"0*/

         OSMemFreeList=(OS_MEM *)&OSMemTbl[0];

    //"内存控制块0的首地址"保存到OSMemFreeList

#endif

}

//函数功能:初始化内存池和内存控制块

//addr为根节点地址

//nblks为节点的总数量

//blksize为节点的字节数

//返回值为内存控制块指针

OS_MEM  *OSMemCreate (void *addr,uint32_t nblks,uint32_t blksize,uint8_t *perr)

{

         OS_MEM  *pmem;//声明pmem"OS_MEM型结构指针"

         uint8_t *pblk;//声明pblk"一维指针"

         void  **plink;//声明plink"void型二维指针"

         uint32_t loops;

         uint32_t i;

         if(addr == (void *)0)//根节点地址addr0

         {

                   *perr = OS_FAIL;

                   return ((OS_MEM *)0);

         }

         if(((int32_t)addr & (sizeof(void *) - 1u)) != 0u) // addr不是4字节对齐

         {

                   *perr = OS_FAIL;

                   return ((OS_MEM *)0);

         }

         if(nblks < 2)//节点的总数量只有1

         {

                   *perr = OS_FAIL;

                   return ((OS_MEM *)0);

         }

         if (blksize < sizeof(void *))//节点的字节数小于4

         {

                   *perr = OS_FAIL;

                   return ((OS_MEM *)0);

         }

         pmem = OSMemFreeList;//读取内存控制块指针

         if (OSMemFreeList != (OS_MEM *)0)

         {

                   OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;

                   /*"下一个内存控制块的首地址"保存到OSMemFreeList,为初始化下一个内存池做准备*/

         }

         plink = (void **)addr;

    //"void型一维指针addr"强制转换为"void型二维指针"保存到plink

         pblk = (uint8_t *)addr;

    //"void型一维指针addr"强制转换为"uint8_t型一维指针"保存到pblk

         loops = nblks-1;//nblks为节点的总数量

         for(i=0;i<loops;i++)

         {

                   pblk +=blksize;

       //"下一个节点的首地"址保存到"uint8_t型一维指针pblk"

                   *plink = (void  *)pblk;

/*"uint8_t型一维指针pblk"强制转换为"void型一维指针"保存到"void型一维指针(*plink)"*/

                   plink = (void **)pblk;

/*"uint8_t型一维指针pblk"强制转换为"void型二维指针",保存到"void型二维指针plink"*/

         }

         *plink = (void *)0;

         //由于"最后一个节点"没有"下一个节点",所有设置"下一个节点的地址"0

         pmem->OSMemAddr= addr;

         pmem->OSMemFreeList = addr;

         pmem->OSMemNFree= nblks;

         pmem->OSMemNBlks    = nblks;

         pmem->OSMemBlkSize  = blksize;

         return (pmem);//返回内存控制块指针

}

//函数功能:根据"内存控制块指针pmem",申请一个节点

//pmem为内存控制块指针

void *OSMemGet(OS_MEM *pmem,uint8_t *perr)

{

         void *pblk;//声明pblk"void型一维指针"

         if (pmem == (OS_MEM *)0)

         {//内存控制块指针pmem0

                   *perr = OS_FAIL;

                   return ((void *)0);

         }

         if(pmem->OSMemNFree > 0u)//"空闲节点计数器"大于0,说明有空闲的节点存在

         {

                   pblk = pmem->OSMemFreeList;

                   /*从内存控制块中,读取下一个节点的首地址,并将它保存到pblk,这就是"申请到的节点"的首地址*/

                   pmem->OSMemFreeList = *(void **)pblk;

                   /*在内存池中,"pblk为首地址数据块"强制转换为"void型二维指针","所指向单元的内容"为指针数据*/

                   /*将读到的指针数据保存到pmem->OSMemFreeList,这就是"申请到的节点"的下一个节点的首地址*/

                   pmem->OSMemNFree--;//空闲节点计数器减1;

                   *perr = OS_SUCCESS;//建立成功标志

                   return (pblk);//返回"申请到的节点"的首地址

         }

         *perr=OS_FAIL;//建立失败标志

         return ((void *)0);//返回0

}

//函数功能:释放一个节点

//pmem为内存控制块指针

//pblk为待释放的节点指针

int8_t OSMemPut(OS_MEM *pmem,void *pblk)

{

         if(pmem == (OS_MEM *)0) return (OS_FAIL);

         if(pblk == (void *)0)   return (OS_FAIL);

         if(pmem->OSMemNFree >= pmem->OSMemNBlks) return (OS_FULL);

         *(void **)pblk = pmem->OSMemFreeList;

//pblk强制转换为"void型二维指针","所指向单元的内容"为指针域

//从内存控制块中,读取"待释放的节点的下一个节点的首地址",并将它保存到指针域

         pmem->OSMemFreeList = pblk;

//"待释放的节点指针"保存到内存控制块中,记录"下一个节点的首地址"

         pmem->OSMemNFree++;//空闲节点计数器加1;

         return (OS_SUCCESS);

}

int8_t OSMemQuery(OS_MEM *pmem,OS_MEM_DATA  *p_mem_data)

{

         if (pmem == (OS_MEM *)0) return (OS_FAIL);

         if (p_mem_data == (OS_MEM_DATA *)0) return (OS_FAIL);

         p_mem_data->OSAddr     = pmem->OSMemAddr;

    //从内存控制块中读取"根节点地址"

         p_mem_data->OSFreeList = pmem->OSMemFreeList;

    //从内存控制块中读取"下一个节点的地址"

         p_mem_data->OSBlkSize  = pmem->OSMemBlkSize;

    //从内存控制块中读取"节点的字节数量"

         p_mem_data->OSNBlks    = pmem->OSMemNBlks;

    //从内存控制块中读取"节点的总数量"

         p_mem_data->OSNFree    = pmem->OSMemNFree;

    //从内存控制块中读取"可用节点的数量"

         p_mem_data->OSNUsed    = p_mem_data->OSNBlks - p_mem_data->OSNFree;

         //"已用节点数量" = "节点的总数量" - "可用节点的数量"

         return(OS_SUCCESS);

}

const char RootNode_REG[]="\r\nRootNode=0x";

const char NextNode_REG[]="   NextNode=0x";

const char TotalNumberOfNode_REG[]="   TotalNumberOfNode=";

const char SizeOfNode_REG[]="   SizeOfNode=";

const char Bytes_REG[]="Bytes";

const char NumberOfUsedNode_REG[]="   NumberOfUsedNode=";

const char NumberOfFreeNode_REG[]="   NumberOfFreeNode=";

const char Mymemory_REG[]="\r\nMymemory: ";

void Do_OSMemQuery(void)

{

         u32 *pblk;//声明pblk为"u32型一维指针"

         u8 i;

         OSMemQuery(MemoryControlBlock1_Pointer,&My_OS_MEM_DATA);

         printf("%s",RootNode_REG);printf("%p",My_OS_MEM_DATA.OSAddr);

   //打印根节点地址

         printf("%s",NextNode_REG);printf("%p",My_OS_MEM_DATA.OSFreeList);

   //打印下一节点地址

         //printf("%s",SizeOfNode_REG);printf("%u",My_OS_MEM_DATA.OSBlkSize);printf("%s",Bytes_REG);//打印节点字节数

         //printf("%s",NumberOfUsedNode_REG);printf("%u",My_OS_MEM_DATA.OSNUsed);//打印已用节点数量

         //printf("%s",NumberOfFreeNode_REG);printf("%u",My_OS_MEM_DATA.OSNFree);//打印未用节点数量

         //printf("%s",TotalNumberOfNode_REG);printf("%u",My_OS_MEM_DATA.OSNBlks);//打印节点总数量

         printf("   %u + %u = %u",My_OS_MEM_DATA.OSNFree,My_OS_MEM_DATA.OSNUsed,My_OS_MEM_DATA.OSNBlks);

         printf("%s",Mymemory_REG);

         pblk=(u32 *)&InternalRAM1_Array[0];

         for(i=0;i<InternalRAM1_TotalNumberOfBytes/4;i++)

         {

                   printf("  0x%08x",*pblk);

                   pblk++;

         }

}

void MEMORY_Test(void)

{

         u8 err;

         u8 *ptr1,*ptr2;

         printf("\r\n\r\nReady...");

         Do_OSMemQuery();

         printf("\r\nmalloc1:");

         ptr1 = OSMemGet(MemoryControlBlock1_Pointer,&err);

//根据"内存控制块指针MemoryControlBlock1_Pointer",申请一个节点

         if(ptr1!=NULL) *(u32*)ptr1=0x12345678;

         Do_OSMemQuery();

         printf("\r\nmalloc2:");

         ptr2 = OSMemGet(MemoryControlBlock1_Pointer,&err);

         if(ptr2!=NULL) *(u32*)ptr2=0x87654321;

         Do_OSMemQuery();

         printf("\r\nfree1:");

         if(ptr2!=NULL)//后申请的先释放

         {

                   OSMemPut(MemoryControlBlock1_Pointer,ptr2);

//释放一个内存块,首地址为ptr2

                   Do_OSMemQuery();

                   ptr2=NULL;

         }

         printf("\r\nfree2:");

         if(ptr1!=NULL)//先申请的后释放

         {

                   OSMemPut(MemoryControlBlock1_Pointer,ptr1);

       //释放一个内存块,首地址为ptr1

                   Do_OSMemQuery();

                   ptr1=NULL;

         }

}

测试结果如下:

void Do_OSMemQuery2(void)

{

         u32 *pblk;//声明pblk"u32型一维指针"

         u8 i;

         OSMemQuery(MemoryControlBlock2_Pointer,&My_OS_MEM_DATA);

         printf("%s",RootNode_REG);printf("%p",My_OS_MEM_DATA.OSAddr);

    //打印根节点地址

         printf("%s",NextNode_REG);printf("%p",My_OS_MEM_DATA.OSFreeList);

    //打印下一节点地址

         //printf("%s",SizeOfNode_REG);printf("%u",My_OS_MEM_DATA.OSBlkSize);printf("%s",Bytes_REG);//打印节点字节数

         //printf("%s",NumberOfUsedNode_REG);printf("%u",My_OS_MEM_DATA.OSNUsed);//打印已用节点数量

         //printf("%s",NumberOfFreeNode_REG);printf("%u",My_OS_MEM_DATA.OSNFree);//打印未用节点数量

         //printf("%s",TotalNumberOfNode_REG);printf("%u",My_OS_MEM_DATA.OSNBlks);//打印节点总数量

         printf("   %u + %u = %u",My_OS_MEM_DATA.OSNFree,My_OS_MEM_DATA.OSNUsed,My_OS_MEM_DATA.OSNBlks);

         printf("%s",Mymemory_REG);

         pblk=(u32 *)&InternalRAM2_Array[0];

         for(i=0;i<InternalRAM2_TotalNumberOfBytes/4;i++)

         {

                   printf("  0x%08x",*pblk);

                   pblk++;

         }

}

void MEMORY2_Test(void)

{

         u8 err;

         u8 *ptr1,*ptr2;

         printf("\r\n\r\nReady...");

         Do_OSMemQuery2();

         printf("\r\nmalloc1:");

         ptr1 = OSMemGet(MemoryControlBlock2_Pointer,&err);

//根据"内存控制块指针MemoryControlBlock2_Pointer",申请一个节点

         if(ptr1!=NULL) *(u32*)ptr1=0x12345678;

         Do_OSMemQuery2();

         printf("\r\nmalloc2:");

         ptr2 = OSMemGet(MemoryControlBlock2_Pointer,&err);

         if(ptr2!=NULL) *(u32*)ptr2=0x87654321;

         Do_OSMemQuery2();

         printf("\r\nfree1:");

         if(ptr2!=NULL)//后申请的先释放

         {

                   OSMemPut(MemoryControlBlock2_Pointer,ptr2);

        //释放一个内存块,首地址为ptr2

                   Do_OSMemQuery2();

                   ptr2=NULL;

         }

         printf("\r\nfree2:");

         if(ptr1!=NULL)//先申请的后释放

         {

                   OSMemPut(MemoryControlBlock2_Pointer,ptr1);

       //释放一个内存块,首地址为ptr1

                   Do_OSMemQuery2();

                   ptr1=NULL;

         }

}

测试结果:

3、程序测试结果

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高效处理报表,掌握原生JS打印和导出报表为PDF的顺畅技巧!

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言篇 在日常工作中&#xff0c;报表打印和导出为PDF是经常要处理的任务之一。除了方…

管理类联考——写作——素材篇——论说文——写作素材02——志篇:毅力·坚持

管理类专业学位联考 (写作能力) 论说文素材 02——志篇&#xff1a;毅力坚持 论文说材料: 骐骥一跃&#xff0c;不能十步&#xff1b;驽马十驾&#xff0c;功在不舍。 ——《荀子劝 学》 一&#xff1a;道理论据 咬住青山不放松&#xff0c;立根原在破岩中&#xff1b;千磨…

gitLens插件简单使用(默认上传github)

1.安装 在vscode中的插件管理输入如下后下载 GitLens — Git supercharged 2.配置 点击文件--首选项--设置 点击右上角设置小图标 3.github使用 首先仓库文件一定是要git init是git所管理的 1.在代码文件夹下使用git init创建仓库 2.打开vscode的git管理 3.点击添加暂存区…

如何使用ArcGIS加载天地图

天地图是自然资源部主管&#xff0c;国家基础地理信息中心负责建设的国家地理信息公共服务平台&#xff0c;于2011年1月18日上线。 有的时候可能需要将在线的天地图加载到ArcGIS内&#xff0c;但是加载方式越来越复杂&#xff0c;很多方法都需要申请key&#xff0c;这里为大家…

C++基础(3)——类和对象(2)

前言 本文主要介绍了C中类和对象的基本知识。 4.2.5&#xff1a;深拷贝和浅拷贝 浅拷贝&#xff1a;编译器给我们提供的拷贝函数就是等号复制操作 深拷贝&#xff1a;自己手动重写一个拷贝构造函数&#xff0c;重新new 浅拷贝会出现的问题&#xff1a;如果使用编译器提供的…

AI是什么?AI工具集网站大全

AI是什么&#xff1f; AI 是人工智能的缩写&#xff0c;指的是通过计算机技术和算法来实现智能的能力。我们人类的智能是基于我们的大脑所实现的&#xff0c;而 AI 因此也常被称为机器智能。AI技术需要机器能够感知、推理和行动&#xff0c;这些都需要底层算法的支持&#xff…

2.2C++公有继承与私有继承

C公有继承 C中的公有继承是指一个类可以从另一个类继承公有成员&#xff0c;包括公有成员函数和变量。 公有继承是面向对象编程中最基本的继承方式&#xff0c;它表示父类的公有成员在子类中仍然是公有成员&#xff0c;可以被外部访问。 我写一个 Animal 的基类&#xff0c;…

哪些公司里面有高性能计算方向cuda方向岗位?

CUDA可以为高性能计算、科学计算、深度学习和人工智能、图形渲染和游戏开发、并行数据处理等领域提供了强大的并行计算能力和编程模型。它加速了计算任务的执行&#xff0c;推动了科学研究和创新的进程&#xff0c;同时也为开发者提供了更多的工具和资源&#xff0c;促进了开放…

留个档,Unity Animator state节点的Motion动态替换AnimationClip

前言 由于Unity没有提供直接替换的API&#xff0c;所以在仅限的API下进行逻辑操作。 替换的原理是差不多的&#xff0c;利用AnimatorOverrideController&#xff0c;进行运行时的覆盖。 网上搜索很多文章是利用 名字字符串作为hash的key来进行替换。不满足我自己项目中的需求…

【系统开发】尚硅谷 - 谷粒商城项目笔记(五):分布式缓存

文章目录 分布式缓存缓存使用场景redis作缓存中间件引入redis依赖配置redis堆外内存溢出 缓存失效问题缓存穿透缓存雪崩缓存击穿 Redisson分布式锁导入依赖redisson配置类可重入锁读写锁缓存一致性解决 缓存-SpringCache简介Cacheable自定义缓存配置CacheEvictCachePut原理与不…

【网络】协议的定制与Json序列化和反序列化

文章目录 应用层初识TCP协议通讯流程定制协议再谈协议网络版本计算器Protocal.hppCalServerCalClient Json的安装 应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 初识TCP协议通讯流程 建立链接和断开链接 基于TCP协议&#xff0c…

六、使用深度学习构建人脸识别模型

本章介绍机器学习中人脸识别的历史以及从零开始如何构建一个人脸识别模型,含所有训练数据,源代码,不强制要求GPU。使用 docker 来管理库依赖项,提供与平台无关的一致环境。使用 Dlib 进行预处理,使用 Tensorflow + Scikit-learn 训练能够根据图像预测身份的分类器。 1、人…

《吉林省教育学院学报》简介及投稿邮箱

《吉林省教育学院学报》简介&#xff1a; 主管单位 吉林省教育厅 主办单位 吉林省教育学院 出版周期&#xff1a;月刊 国际刊号&#xff1a;ISSN&#xff1a;1671-1580&#xff1b;国内刊号&#xff1a;CN&#xff1a;22-1296/G4&#xff1b;邮发代号&#xff1a;12-223 出…

创建线程三种方法

创建和运行线程 方法一&#xff0c;直接使用 Thread // 创建线程对象 Thread t new Thread() { public void run() {// 要执行的任务} }; // 启动线程 t.start(); 例如&#xff1a; // 构造方法的参数是给线程指定名字&#xff0c;推荐Thread t1 new Thread("t1"…

Doris的安装

Doris的安装 文章目录 Doris的安装写在前面Linux 操作系统版本需求软件需求操作系统安装要求设置系统最大打开文件句柄数时钟同步关闭交换分区&#xff08;swap&#xff09; 开发测试环境生产环境 安装下载安装包默认端口集群部署前置准备安装部署FE安装部署BE在 **FE** 中添加…

2.1C++派生

C派生概述 C中的派生允许从一个已有的类中创建一个新的类&#xff0c;该新类继承了原有类的属性和方法。 派生类可以增加新的属性和方法&#xff0c;也可以重写原有类的方法以改变其行为。 C中的派生类可以通过公有、私有和保护继承来继承基类的成员。 公有继承允许派生类访…

网络协议驱动互联网

在分布式系统中&#xff0c;数据通过各种网络协议在网络中传输。作为应用程序开发者&#xff0c;这往往在问题出现之前似乎是一个黑盒子。 在本文中&#xff0c;我们将解释常见网络协议的工作原理&#xff0c;它们在分布式系统中的应用以及我们如何解决常见问题。后续还会介绍一…

开源一键拥有你自己的ChatGPT+Midjourney网页服务,用不用是另一回事,先收藏!

功能支持 原ChatGPT-Next-Web所有功能 midjourney imgine 想象 midjourney upscale 放大 midjourney variation 变幻 midjourney describe 识图 midjourney blend 混图 midjourney 垫图 绘图进度百分比、实时图像显示 自身支持midjourney-api 参数说明 MIDJOURNEY_PROXY_URL …

组态王与多台PLC之间无线以太网通信

在实际系统中&#xff0c;同一个车间里分布多台PLC&#xff0c;通过上位机集中控制。通常所有设备距离在几十米到上百米不等。在有通讯需求的时候&#xff0c;如果布线的话&#xff0c;工程量较大耽误工期&#xff0c;这种情况下比较适合采用无线通信方式。 本方案以组态王和2…

java -D详解

官方文档对 -D 有明确的解释&#xff0c;具体看 https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html 简单解释一下 首先&#xff0c;java 命令是这么用的 其次&#xff0c;-D 是属于 [options] 这一块的。而 [options] 又分为如下几类 -D 就属于标准选…