进击的五月,继上期《使用Air724UG制作简易贪吃蛇》教程之后,@打盹的消防车 又为大家带来基于STM32的潘多拉LuatOS移植全新教程:

为什么使用潘多拉作为教程呢?

STM32不能没有通讯,那就选IoT开发板——潘多拉显然没什么短板,很适合入门使用。当然,其他STM32也可以参照本教程来做。

文中同样涉及一些其他平台的移植思路,所以想移植LuatOS都可以看一看。

@

Luat OS架构分析

移植之前首先看一下LuatOS的总体构架:

LuatOS架构图


可以看到,LuatOS做了一套适配层去对接平台,所以移植只需要做适配层就可以了(别跑,看着很多,其实移植不用做很多,许多已经做了)。

接下来我们看一下LuatOS目录:

LuatOS文件目录


  • bsp:

    bsp文件里存放着各种已经适配了的芯片。目前有:

    - Air001

    - Air100ST(STM32F4)

    - Air302(NB-IoT)

    - Air640W(Wi-Fi)

    - Air724UG(4G Cat.1)

    - Win32

    只有这些么?当然不是。W800(Wi-Fi+bt)本人也在做,目前做了基础外设和LVGL;ESP32梦程在做,外设做完大部分,相信不久也会和大家见面;还有一些其他的,也已经计划适配了。

  • components:一些中间层,本次移植不需要。

  • docs:一些说明

  • lua:Lua虚拟机,重要

  • luat:luat层,重要

  • mind:思维导图

  • script:脚本,本次移植不需要。

  • tools:工具

可以看到,我们主要做的就是移植lua、luat两个文件夹,其中lua层为Lua虚拟机与平台无关,几乎不用改什么,通常放进去可以直接编译。

我们主要看luat:

- luat/cmsis_os2
# cmsis_os2库移植对接层,如果库支持可以直接对接
- luat/freertos
# freertos库移植对接层,如果使用freertos可以直接对接
- luat/rtt
# RT-Thread库移植对接层,如果使用RT-Thread可以直接对接
- luat/include # 头文件
- luat/module # lua库实现,几乎无需改动
- luat/packages/lua-cjson
# 平台无关的json库(自由选择软件包)考。

LuatOS移植思路


介绍Luatos构架之后,我们说一下移植思路。需要移植的核心功能有:

- lua虚拟机

- msgbus(消息队列)

- timer(定时)

- uart(打印)

- fs(文件系统)

- 外设

lua虚拟机我们直接把lua文件夹放进去编译即可;

msgbus(消息队列)、timer(定时)如果使用FreeRTOS、RT-Thread或者cmsis_os2,可以直接使用现成的,无需移植(可能不同平台需要微调);

uart(打印)和fs(文件系统)以及外设,我们需要针对自己的芯片进行对接。

Msgbus(消息队列)移植


首先看msgbus(消息队列),我们要实现luat_msgbus.h中的函数:

// 定义接口方法
void luat_msgbus_init(void);
uint32_t luat_msgbus_put(rtos_msg_t* msg, size_t timeout);
uint32_t luat_msgbus_get(rtos_msg_t* msg, size_t timeout);
uint32_t luat_msgbus_freesize(void);

可以看到我们只需要实现四个函数就可以:

luat_msgbus_init(消息队列初始化)

luat_msgbus_put(消息队发送)

luat_msgbus_get(消息获取)

luat_msgbus_freesize(消息队列剩余空闲位置)

这里我们以FreeRTOS为例:

void luat_msgbus_init(void) 

{

 if (!xQueue) { 

 #if configSUPPORT_STATIC_ALLOCATION xQueue =

 xQueueCreateStatic( QUEUE_LENGTH,

 ITEM_SIZE,ucQueueStorageArea,&xStaticQueue );

 #else

 xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);

 #endif

 }

}

uint32_t luat_msgbus_put(rtos_msg_t* msg, size_t timeout) 

{

if (xQueue == NULL)

 return 1;

return xQueueSendFromISR(xQueue, msg, NULL) == pdTRUE ? 0 : 1;

 }

uint32_t luat_msgbus_get (rtos_msg_t* msg, size_t timeout)

{

if (xQueue == NULL) 

 return 1;

return xQueueReceive(xQueue, msg, timeout) == pdTRUE ? 0 : 1; 

 }

uint32_t luat_msgbus_freesize(void)

{

if (xQueue == NULL) 

 return 1; 

 return 1;

 }


可以看到,我们做的就是将LuatOS的消息队列对接到RTOS。

Timer(定时器)移植


接下来移植timer(定时器),我们要实现luat_timer.h中的函数:

int luat\_timer\_start(luat\_timer\_t\* timer);

int luat\_timer\_stop(luat\_timer\_t\* timer);

luat\_timer\_t\* luat\_timer\_get(size\_t timer\_id);

int luat\_timer\_mdelay(size\_t ms);

也很简单,我们只有需要实现:

luat_timer_start(定时器开启)

luat_timer_stop(定时器停止)

luat_timer_get(定时器获取)

luat_timer_mdelay(延迟)

同样我们以FreeRTOS为例:

int luat_timer_start(luat_timer_t* timer)

{

TimerHandle\_t os\_timer;

int timerIndex;

timerIndex = nextTimerSlot();

if (timerIndex < 0可以) 

{ 

 return 1; // too many timer!!

} 

os\_timer = xTimerCreate("luat\_timer", timer->timeout / portTICK\_RATE\_MS, timer->repeat, timer, luat\_timer\_callback);

if (!os\_timer) 

{ 

  return -1; 

} 

timers\[timerIndex\] = timer; 

timer->os\_timer = os\_timer;

int re = xTimerStart(os\_timer, 0);

if (re != pdPASS) 

{ 

 xTimerDelete(os\_timer, 0); 

 timers\[timerIndex\] = 0; 

 } 

 return re == pdPASS ? 0 : -1;

 }

int luat_timer_stop(luat_timer_t* timer) 

{ 

  if (!timer) 

 return 1; 

 for (size_t i = 0; i < FREERTOS\_TIMER\_COUNT; i++)          { 

  if (timers\[i\] == timer) 

 { 

 timers\[i\] = NULL; 

  break;

 } 

 } 

 xTimerStop((TimerHandle\_t)timer->os\_timer, 10); 

 xTimerDelete((TimerHandle\_t)timer->os\_timer, 10); 

 return 0; 

 }; 

 luat_timer_t\* luat_timer_get(size_t timer\_id) 

 {

 for (size_t i = 0; i < FREERTOS\_TIMER\_COUNT; i++){ if (timers\[i\] && timers\[i\]->id == timer\_id) 

 { 

 return timers\[i\]; 

 } 

}

return NULL; 

}

int luat_timer_mdelay(size_t ms) 

{ 

  if (ms > 0) 

 { 

 vTaskDelay(ms / portTICK\_RATE\_MS); 

 } 

return 0; 

}

可以看到,和消息队列一样,只要将LuatOS的定时函数对接RTOS的定时函数就OK啦,很简单是不是。

Uart(打印)移植


接下来uart(打印),我们需要实现luat_uart.h,针对使用的板子实现以下几个串口基本的函数即可:

int l\_uart\_handler(lua\_State \*L, void\* ptr);

int luat\_uart\_setup(luat\_uart\_t\* uart);

int luat\_uart\_write(int uartid, void\* data, size\_t length);

int luat\_uart\_read(int uartid, void\* buffer, size\_t length);

int luat\_uart\_close(int uartid);

int luat\_uart\_exist(int uartid);

int luat\_setup\_cb(int uartid, int received, int sent);

文件系统移植


剩下一个文件系统,如果我们的板子支持posix风格,那么恭喜,可以直接对接,否则我们需要实现luat_fs.h。

int luat_fs_init(void);
int luat_fs_mkfs(luat_fs_conf_t *conf);
int luat_fs_mount(luat_fs_conf_t *conf);
int luat_fs_umount(luat_fs_conf_t *conf);
int luat_fs_info(const char* path, luat_fs_info_t *conf);
FILE* luat_fs_fopen(const char *filename, const char *mode);
int luat_fs_getc(FILE* stream);
int luat_fs_fseek(FILE* stream, long int offset, int origin);
int luat_fs_ftell(FILE* stream);
int luat_fs_fclose(FILE* stream);
int luat_fs_feof(FILE* stream);
int luat_fs_ferror(FILE *stream);
size_t luat_fs_fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t luat_fs_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
int luat_fs_remove(const char *filename);
int luat_fs_rename(const char *old_filename, const char *new_filename);
size_t luat_fs_fsize(const char *filename);
int luat_fs_fexist(const char *filename);
int luat_fs_mkdir(char const* _DirName);
int luat_fs_rmdir(char const* _DirName);
#ifdef LUAT_USE_FS_VFS
struct luat_vfs_file_opts
{
FILE* (*fopen)(void* fsdata, const char *filename, const char *mode); i
nt (*getc)(void* fsdata, FILE* stream);
int (*fseek)(void* fsdata, FILE* stream, long int offset, int origin);
int (*ftell)(void* fsdata, FILE* stream);
int (*fclose)(void* fsdata, FILE* stream);
int (*feof)(void* fsdata, FILE* stream);
int (*ferror)(void* fsdata, FILE *stream);
size_t (*fread)(void* fsdata, void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t (*fwrite)(void* fsdata, const void *ptr, size_t size, size_t nmemb, FILE *stream);
};
struct luat_vfs_filesystem_opts
{
int (*remove)(void* fsdata, const char *filename); int (*rename)(void* fsdata, const char *old_filename, const char *new_filename);
size_t (*fsize)(void* fsdata, const char *filename); int (*fexist)(void* fsdata, const char *filename);
int (*mkfs)(void* fsdata, luat_fs_conf_t *conf);
int (*mount)(void** fsdata, luat_fs_conf_t *conf);
int (*umount)(void* fsdata, luat_fs_conf_t *conf); int (*info)(void* fsdata, const char* path, luat_fs_info_t *conf);
int (*mkdir)(void* fsdata, char const* _DirName); int (*rmdir)(void* fsdata, char const* _DirName);
};
struct luat_vfs_filesystem
{
char name[16];
struct luat_vfs_filesystem_opts opts;
struct luat_vfs_file_opts fopts;
};
typedef struct luat_vfs_mount
{
struct luat_vfs_filesystem *fs;
void *userdata; char prefix[16];
int ok;
}
luat_vfs_mount_t;
typedef struct luat_vfs_fd
{
FILE* fd;
luat_vfs_mount_t *fsMount;
}
luat_vfs_fd_t;
typedef struct luat_vfs
{
struct luat_vfs_filesystem* fsList[LUAT_VFS_FILESYSTEM_MAX];
luat_vfs_mount_t mounted[LUAT_VFS_FILESYSTEM_MOUNT_MAX];
luat_vfs_fd_t fds[LUAT_VFS_FILESYSTEM_FD_MAX+1];
}
luat_vfs_t;
int luat_vfs_init(void* params);
int luat_vfs_reg(const struct luat_vfs_filesystem* fs);
FILE* luat_vfs_add_fd(FILE* fd, luat_vfs_mount_t * mount);
int luat_vfs_rm_fd(FILE* fd);
#endif

这部分需要针对各自的平台实现对接,各位需要针对自己的去实现。

随后我们需要创建一个luat_base_xxx.c去管理我们移植的库以及自己的板卡信息,这里我们以Air302为例:

Air302移植示例


static const luaL\_Reg loadedlibs\[\] = { 

 {"\_G", luaopen\_base}, // \_G 

 {LUA\_LOADLIBNAME, luaopen\_package}, // require 

 {LUA\_COLIBNAME, luaopen\_coroutine}, // coroutine协程库 

 {LUA\_TABLIBNAME, luaopen\_table},    // table库,操作table类型的数据结构 

 {LUA\_IOLIBNAME, luaopen\_io},    // io库,操作文件 

 {LUA\_OSLIBNAME, luaopen\_os},    // os库,已精简 

 {LUA\_STRLIBNAME, luaopen\_string},   // string库,字符串操作 

 {LUA\_MATHLIBNAME, luaopen\_math},    // math 数值计算 

 {LUA\_DBLIBNAME, luaopen\_debug},   // debug库,已精简

// 往下是LuatOS定制的库, 如需精简请仔细测试

//------------------------------------------------------------

// 核心支撑库, 不可禁用!! 

 {"rtos",    luaopen\_rtos},    // rtos底层库, 核心功能是队列和定时器 

 {"log",     luaopen\_log},    // 日志库 

 {"timer",   luaopen\_timer},    // 延时库

//-------------------------------------------------------------

// 设备驱动类, 可按实际情况删减. 即使最精简的固件, 也强烈建议保留uart库 

 {"uart",    luaopen\_uart},   // 串口操作 

 {"gpio",    luaopen\_gpio},   // GPIO脚的操作 

 {"i2c",     luaopen\_i2c},    // I2C操作 

 {"spi",     luaopen\_spi},    // SPI操作 

 {"adc",     luaopen\_adc},    // ADC模块 

 {"pwm",     luaopen\_pwm},   // PWM模块

//-------------------------------------------------------------

// 工具库, 按需选用 

 {"json",    luaopen\_cjson},    // json的序列化和反序列化 

 {"pack",    luaopen\_pack},    // pack.pack/pack.unpack 

 {"mqttcore",luaopen\_mqttcore},    // MQTT 协议封装 

 {"libcoap", luaopen\_libcoap},   // 处理COAP消息 

 {"libgnss", luaopen\_libgnss},    // 处理GNSS定位数据 

 {"fs",   luaopen\_fs},    // 文件系统库,在io库之外再提供一些方法 

 {"sensor",  luaopen\_sensor},    // 传感器库,支持DS18B20 

 {"disp",  luaopen\_disp},    // OLED显示模块,支持SSD1306 

 {"u8g2", luaopen\_u8g2},     // u8g2 

 {"crypto",luaopen\_crypto},   // 加密和hash模块

 // {"eink",  luaopen\_eink}, 

 // 电子墨水屏,试验阶段 

//{"iconv", luaopen\_iconv}, 

// 编码转换,暂不可用

//----------------------------------------------------------------

// 联网及NBIOT特有的库 

 {"socket",  luaopen\_socket},     // 套接字操作 

 {"lpmem",   luaopen\_lpmem},      // 低功耗时仍工作的内存块 

 {"nbiot",   luaopen\_nbiot},   // NBIOT专属模块 

 {"pm",      luaopen\_pm},    // 低功耗模式 

 {"http",  luaopen\_http},  // http库 

 {"ctiot", luaopen\_ctiot},   // ctiot库,中国电信ctwing平台 

 {NULL, NULL}

 };

// 按不同的rtconfig加载不同的库函数

void luat\_openlibs(lua\_State \*L) { 

// 加载系统库 

const luaL\_Reg \*lib;

/\* "require" functions from 'loadedlibs' and set results to global table \*/ 

for (lib = loadedlibs; lib->func; lib++) { luaL\_requiref(L, lib->name, lib->func, 1); 

 lua\_pop(L, 1);  /\* remove lib \*/ 

 }

 }

const char\* luat\_os\_bsp(void) { 

return "ec616"; 

}

我们可以将未实现的或者不想编译的注释掉,修改bsp名等,随后在我们的主程序中启用lua虚拟机。


#include "bget.h "#include "luat\_base.h" void app\_main(void) { bpool(ptr, size); // lua vm需要一块内存用于内部分配, 给出首地址及大小 luat_main();// luat_main是LuatOS的主入口, 该方法通常不会返回 }

接下来我们就是编译,根据报错修改、调试。

这样LuatOS基础移植就实现了,随后就是外设的适配。和之前一样,查看对应的.h文件,去对接需要实现的函数,可以参考已经实现的做移植。

可以看到——LuatOS移植的依赖并不多,甚至没有RTOS也可以实现移植。

潘多拉移植示例


移植顺序按照wendal在LuatOS上的bsp移植顺序,依次为:编译环境的集成、核心功能的适配、外设的适配以及网络接口的适配。

编译环境的集成


首先我们需要一个潘多拉的rtt工程,clone rtt的最新仓库,进去潘多拉的bsp使用scons --dist命令提取一个工程。

\- lua                    # Lua虚拟机 

\- luat/module       # lua库实现

放进去编译,确保编译没问题。

核心功能的适配


我们使用的RTT,这部分移植已经做好了,只需要把RTT目录放进去,首次移植编译我们只加入一些核心基础的就可以,不需要加入RTT目录中全部代码。

可以看到,核心的移植已经都做好了。编译之前需要配置一下RTT:

RTT基础配置操作


• menuconfig进入开启文件系统

• 开启nor flash(我们使用了板载的nor flash)

• 修改主线程heap

• 开启libc库

• 开启ymodem为了后面下载脚本

外设开启QSPI FLASH驱动

开启timer等其他驱动(按自己实际需要)

软件包开启FAL

软件包开启littlefs

随后,将luat_rtt_base.c中未使用的库注释掉:

编译看看,会报错:

我们的bsp已经做了FAL配置,所以进入FAL软件包,把sample去掉:

然后我们初始化文件系统,新建一个luat_fs_init.c:

新建luat_fs_init.c文件


#include "luat_base.h"

#include "luat_malloc.h"

#include "luat_msgbus.h"

#include "luat_timer.h"

#include "luat_gpio.h"

#include "rtthread.h"

#include <rtdevice.h>

/* 添加 fal 头文件 */

#include <fal.h>

/* 添加文件系统头文件 */

#include <dfs_fs.h>

#define DBG_TAG "port.fs"

#define DBG_LVL       DBG_LOG

#include <rtdbg.h>

#include "drv_flash.h"

#include "lfs.h"  

/* 定义要使用的分区名字 */

#define FS_PARTITION_NAME             "filesystem"

static uint8_t fs_ok = 0;

extern char luadb_inline[];

int luat_fs_init(void){ 

 if (fs_ok) return 0;

 fs_ok = 1;

struct rt_device *mtd_dev= RT_NULL; 

 /* 初始化 fal */
fal_init(); /* 生成 mtd 设备 */
mtd_dev = fal_mtd_nor_device_create(FS_PARTITION_NAME); if (!mtd_dev)
{
LOG_E("Can't create a mtd device on '%s' partition.", FS_PARTITION_NAME);
} else
{ /* 挂载 littlefs */
if (dfs_mount(FS_PARTITION_NAME, "/", "lfs", 0, 0) == 0)
{
LOG_I("Filesystem initialized!");
} else
{ /* 格式化文件系统 */
dfs_mkfs("lfs", FS_PARTITION\_NAME); /* 挂载 littlefs */
if (dfs_mount("filesystem", "/", "lfs", 0, 0) == 0)
{
LOG_I("Filesystem initialized!");
} else
{ LOG_E("Failed to initialize filesystem!");
}
}
} // 尝试挂载luadb区域
mkdir("/lua", 0); return 0;}
INIT_ENV_EXPORT(luat_fs_init);

修改main文件


#include <rtthread.h>
#include <rtdevice.h>
#include <board.h> #define DBG_ENABLE#define DBG_SECTION_NAME "main"
#define DBG_LEVEL DBG_LOG
#define DBG_COLOR #include <rtdbg.h>
#include "luat_base.h"
int main(void)
{
rt_thread_mdelay(100); // 故意延后100ms luat_log_set_uart_port(1);
luat_main();
while (1)
{ rt_thread_delay(10000000); }}

此时编译测试正常,下载测试:

可以看到虚拟机正常跑起来了,因为没找到main.lua所以重启。

我们把sys.lua和main.lua,通过ymodem下载进去重启:

脚本运行成功,至此LuatOS基础移植成功。

外设的适配


基于RTT的大部分外设已经适配了,直接添加我们之前删除的RTT目录下的文件编译测试即可。

网络接口的适配


基于RTT的网络接口也已经适配了,直接添加我们之前删除的RTT目录下的文件编译测试即可。

本期移植教程就讲到这里了.

源码下载

上海合宙通信模块 - 合宙Luat,让万物互联更简单

来了!STM32移植LuatOS,潘多拉示例全新教程的更多相关文章

  1. STM32移植RT-Thread后的串口在调试助手上出现:(mq != RT_NULL) assert failed at rt_mq_recv:2085和串口只发送数据不能接收数据问题

    STM32移植RT-Thread后的串口在调试助手上出现:(mq != RT_NULL) assert failed at rt_mq_recv:2085的问题讨论:http://www.rt-thr ...

  2. 图论(Tarjan缩点):BZOJ 1194: [HNOI2006]潘多拉的盒子

    1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 344  Solved: 181[Submit][Stat ...

  3. Hello China操作系统STM32移植指南(一)

    Hello China操作系统移植指南 首先说明一下,为了适应更多的文化背景,对Hello China操作系统的名字做了修改,修改为"Hello X",或者连接在一起,写为&quo ...

  4. BZOJ 1194: [HNOI2006]潘多拉的盒子( BFS + tarjan + dp )

    O(S²)枚举2个诅咒机, 然后O(n²)BFS去判断. 构成一个有向图, tarjan缩点, 然后就是求DAG的最长路.. ------------------------------------- ...

  5. 阿里安全潘多拉实验室首先完美越狱苹果iOS 11.2

    苹果官方对iOS 11的评价是"为iPhone带来巨大进步,让iPad实现里程碑式飞跃."但为了不断修复Bug,苹果于12月2日推出最新的iOS 11.2,修复了Google安全人 ...

  6. 独家探寻阿里安全潘多拉实验室,完美越狱苹果iOS11.2.1

    知道如何从攻击的视角去发现漏洞,才能建立更安全的体系,促进了整个生态的良性发展.以阿里安全潘多拉实验室为例,在对移动系统安全研究的过程中,把研究过程中发现的问题上报给厂商,促进系统安全性的提升. 小编 ...

  7. 【BZOJ-1194】潘多拉的盒子 拓扑排序 + DP

    1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 456  Solved: 215[Submit][Stat ...

  8. 【转】hurry_liu 大神STM32移植contiki入门之一:系统介绍和开发环境搭建

    前言: 由于项目的原因,需要在LPC1788(STM32 cortex-M3)上面跑contiki. 之前没有涉及到contiki,不知其为何物.不过这个不是难事,做IT的,每每遇到新事物,都不会处理 ...

  9. [BZOJ1194][HNOI2006][强连通分量Tarjan+dfs]潘多拉的盒子

    [BZOJ1194][HNOI2006]潘多拉的盒子 Input 第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50).文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语 ...

随机推荐

  1. CCNA 第一章 网络互联

    1: 网络互联基础 互联网络定义:使用路由器将多个网络连接起来,并配置IP或者IPV6协议的逻辑网络编址方案,便组成了互联网络. 导致LAN(局域网)拥塞的常见原因: (1):广播域或者冲突域中的主机 ...

  2. 找大于等于一个数的最小的2^n

    最近看hashmap源码时,发现给定初始capacity计算threshold的过程很巧妙. 1 static final int tableSizeFor(int cap) { 2 int n = ...

  3. SAP ABAP ALV 颜色设置(两个ALV函数例子) 列 行 单元格

    @[TOC](设置ALV颜色)# 前言淦! 要求花花绿绿的ALV ,那就淦他! 需要的参数和对应颜色放在最后.稍微改改就能用. 介绍两个常用的ALV函数实现1.REUSE_ALV_GRID_DISPL ...

  4. Redis6.x学习笔记(四)复制

    复制概述 Redis支持复制的功能,以实现当一台服务器的数据更新后,自动将新的数据异步同步到其它数据库. Redis复制实现中,把数据库分为主数据库master和从数据库slave,主数据库可以进行读 ...

  5. 基础知识:DFRduino UNO R3最全资料详解

    一.概述篇:1. 什么是DFRduino UNO R3?DFRduino UNO R3是一块基与开放原始代码的Simple i/o平台,並且具有使用类似java,C语言的开发环境.让您可以快速使用Ar ...

  6. [bug] sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1054, "Unknown column 'recevie_name' in 'field list'")

    Python Flask 开发购物网站,提交订单时报错 根据提示,检查代码,发现是字段名拼写错误导致,数据库对应的字段是receive_name,误写成了recevie_name 另外要注意,灰色字和 ...

  7. nosql数据库之Redis集群

    Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施(installation). Redis 集群不支持那些需要同时处理多个键的 Redis 命令, 因为执行这些命令需要在多个 ...

  8. Linux进阶之RAID磁盘阵列、系统启动及dd命令

    本节内容 1.      磁盘阵列 RAID0: 条带卷 2+ 100% 读写速度快,不容错 RAID1: 镜像卷 2   50% 读写速度慢,容错 RAID5: 奇偶校验条带卷 3 读写速度快,容错 ...

  9. 如何设置 web 项目打开的默认页面

    引言 我们在创建 Web 项目启动 Tomcat 会自动打开一个默认 index.jsp 页面,这个页面是创建 Web 项目时就自动生成的.那么,如何设置 web 项目打开的这个的默认页面,改为自己的 ...

  10. xml 解析之 JDOM解析

    JDOM 是一种使用 XML 的独特 Java 工具包,用于快速开发 XML 应用程序.JDOM 是一个开源项目,它基于树形结构,利用纯 Java 的技术对 XML 文档实现解析.生成.序列化及多种操 ...