使用外部存储

使用外部存储


每个兼容 Android 的设备都支持可用于保存文件的共享“外部存储”。 该存储可能是可移除的存储介质(例如 SD 卡)或内部(不可移除)存储。 保存到外部存储的文件是全局可读取文件,而且,在计算机上启用 USB 大容量存储以传输文件后,可由用户修改这些文件。

注意:如果用户在计算机上装载了外部存储或移除了介质,则外部存储可能变为不可用状态,并且在您保存到外部存储的文件上没有实施任何安全性。 所有应用都能读取和写入放置在外部存储上的文件,并且用户可以移除这些文件。

使用作用域目录访问

在 Android 7.0 或更高版本中,如果您需要访问外部存储上的特定目录,请使用作用域目录访问。 作用域目录访问可简化您的应用访问标准外部存储目录(例如  Pictures  目录)的方式,并提供简单的权限 UI,清楚地详细介绍应用正在请求访问的目录。 有关作用域目录访问的更多详情,请参阅 使用作用域目录访问

获取外部存储的访问权限

要读取或写入外部存储上的文件,您的应用必须获取 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 系统权限。 例如:

<manifest ...>
   
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

如果您同时需要读取和写入文件,则只需请求 WRITE_EXTERNAL_STORAGE 权限,因为此权限也隐含了读取权限要求。

:从 Android 4.4 开始,如果您仅仅读取或写入应用的私有文件,则不需要这些权限。 如需了解更多信息,请参阅下面有关保存应用私有文件的部分。

检查介质可用性

在使用外部存储执行任何工作之前,应始终调用 getExternalStorageState() 以检查介质是否可用。介质可能已装载到计算机,处于缺失、只读或其他某种状态。 例如,以下是可用于检查可用性的几种方法:

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
   
String state = Environment.getExternalStorageState();
   
if (Environment.MEDIA_MOUNTED.equals(state)) {
       
return true;
   
}
   
return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
   
String state = Environment.getExternalStorageState();
   
if (Environment.MEDIA_MOUNTED.equals(state) ||
       
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
       
return true;
   
}
   
return false;
}

getExternalStorageState() 方法将返回您可能需要检查的其他状态(例如介质是否处于共享 [连接到计算]、完全缺失、错误移除等状态)。 当您的应用需要访问介质时,您可以使用这些状态向用户通知更多信息。

保存可与其他应用共享的文件

一般而言,应该将用户可通过您的应用获取的新文件保存到设备上的“公共”位置,以便其他应用能够在其中访问这些文件,并且用户也能轻松地从该设备复制这些文件。 执行此操作时,应使用共享的公共目录之一,例如 Music/Pictures/ 和 Ringtones/ 等。

要获取表示相应的公共目录的 File,请调用 getExternalStoragePublicDirectory(),向其传递您需要的目录类型,例如 DIRECTORY_MUSICDIRECTORY_PICTURES、 DIRECTORY_RINGTONES 或其他类型。通过将您的文件保存到相应的媒体类型目录,系统的媒体扫描程序可以在系统中正确地归类您的文件(例如铃声在系统设置中显示为铃声而不是音乐)。

例如,以下方法在公共图片目录中创建了一个用于新相册的目录:

public File getAlbumStorageDir(String albumName) {
   
// Get the directory for the user's public pictures directory.
   
File file = new File(Environment.getExternalStoragePublicDirectory(
           
Environment.DIRECTORY_PICTURES), albumName);
   
if (!file.mkdirs()) {
       
Log.e(LOG_TAG, "Directory not created");
   
}
   
return file;
}

保存应用私有文件

如果您正在处理的文件不适合其他应用使用(例如仅供您的应用使用的图形纹理或音效),则应该通过调用 getExternalFilesDir() 来使用外部存储上的私有存储目录。此方法还会采用 type 参数指定子目录的类型(例如 DIRECTORY_MOVIES)。 如果您不需要特定的媒体目录,请传递 null 以接收应用私有目录的根目录。

从 Android 4.4 开始,读取或写入应用私有目录中的文件不再需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 权限。 因此,您可以通过添加 maxSdkVersion 属性来声明,只能在较低版本的 Android 中请求该权限:

<manifest ...>
   
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     
android:maxSdkVersion="18" />
    ...
</manifest>

:当用户卸载您的应用时,此目录及其内容将被删除。此外,系统媒体扫描程序不会读取这些目录中的文件,因此不能从 MediaStore 内容提供程序访问这些文件。 同样,不应将这些目录用于最终属于用户的媒体,例如使用您的应用拍摄或编辑的照片或用户使用您的应用购买的音乐等 — 这些文件应保存在公共目录中

有时,已分配某个内部存储器分区用作外部存储的设备可能还提供了 SD 卡槽。在使用运行 Android 4.3 和更低版本的这类设备时,getExternalFilesDir() 方法将仅提供内部分区的访问权限,而您的应用无法读取或写入 SD 卡。不过,从 Android 4.4 开始,可通过调用getExternalFilesDirs() 来同时访问两个位置,该方法将会返回包含各个位置条目的 File 数组。 数组中的第一个条目被视为外部主存储;除非该位置已满或不可用,否则应该使用该位置。 如果您希望在支持 Android 4.3 和更低版本的同时访问两个可能的位置,请使用支持库中的静态方法 ContextCompat.getExternalFilesDirs()。 在 Android 4.3 和更低版本中,此方法也会返回一个 File 数组,但其中始终仅包含一个条目。

注意 尽管 MediaStore 内容提供程序不能访问 getExternalFilesDir() 和 getExternalFilesDirs() 所提供的目录,但其他具有 READ_EXTERNAL_STORAGE 权限的应用仍可访问外部存储上的所有文件,包括上述文件。 如果您需要完全限制您的文件的访问权限,则应该转而将您的文件写入到内部存储

保存缓存文件

要打开表示应该将缓存文件保存到的外部存储目录的 File,请调用 getExternalCacheDir()。 如果用户卸载您的应用,这些文件也会被自动删除。

与前述 ContextCompat.getExternalFilesDirs() 相似,您也可以通过调用 ContextCompat.getExternalCacheDirs() 来访问辅助外部存储(如果可用)上的缓存目录。

1

提示:为节省文件空间并保持应用性能,您应该在应用的整个生命周期内仔细管理您的缓存文件并移除其中不再需要的文件,这一点非常重要。/p>

引用自--Android开发者文档

<think>嗯,用户问的是如何在FreeRTOS中使用外部存储来扩展栈空间。我需要先回忆一下FreeRTOS的内存管理机制。FreeRTOS的任务栈通常是分配在内部RAM中的,但有时候内部RAM不够用,特别是资源受限的嵌入式系统。这时候可能需要用外部存储,比如外部SRAM或者SDRAM。 首先,得确认用户使用的FreeRTOS版本是否支持自定义内存分配。因为默认情况下,任务栈是使用pvPortMalloc分配的,这个函数在heap_x.c文件中定义。用户可能需要修改内存分配函数,让栈空间从外部存储分配。 接下来要考虑的是外部存储的硬件连接和驱动。比如,外部存储是否已经正确初始化,是否有读写函数,是否需要通过FSMC或者其它总线接口来访问。这部分需要用户自己实现驱动,确保外部存储的可靠性,因为栈空间的操作频繁,延迟或错误会导致系统崩溃。 然后是修改FreeRTOS的配置。用户可能需要自定义pvPortMalloc和vPortFree函数,使其指向外部存储的分配和释放。或者,在创建任务时,直接传递外部存储的缓冲区作为栈空间。这可以通过xTaskCreateStatic函数来实现,该函数允许用户指定栈的地址,这样用户就可以将栈分配到外部内存中。 不过,使用外部存储可能会带来一些问题。比如,访问速度比内部RAM慢,这可能导致任务切换延迟增加。此外,外部存储的稳定性也需要考虑,比如数据线干扰可能引发内存错误,进而导致系统不稳定。因此,需要权衡利弊,是否真的有必要将栈放在外部。 另外,还需要注意内存对齐的问题。不同的处理器架构对内存对齐有不同要求,外部存储的缓冲区必须满足这些对齐条件,否则可能导致硬件异常或性能下降。 还有一点是堆栈溢出检测。FreeRTOS提供了堆栈溢出检测机制,如果栈空间在外部存储,需要确保这些检测机制仍然有效,或者根据实际情况调整溢出检测的阈值。 最后,可能还需要调整链接脚本,将特定的内存区域分配到外部存储,但这通常涉及到更底层的配置,需要根据具体的编译器和处理器架构来处理。 总结一下,步骤大致是:确认硬件支持,编写或调整外部存储驱动,修改FreeRTOS的内存分配或任务创建方式,处理潜在的性能和稳定性问题,并进行充分的测试。用户需要根据具体情况进行调整,并注意可能出现的各种问题。</think>在FreeRTOS中通过外部存储扩展任务栈空间的实现需要结合硬件特性和系统配置,以下是分步骤说明: 1. **原理分析** - FreeRTOS默认使用内部RAM分配任务栈 - 当处理复杂任务时可能出现栈溢出(可通过`configCHECK_FOR_STACK_OVERFLOW`检测) - 扩展方案:将任务栈分配到外部存储器(如SRAM、PSRAM、SDRAM) 2. **硬件准备** ```c // 示例:STM32的FSMC初始化代码片段 void FSMC_SRAM_Init(void) { /* FSMC时钟使能 */ RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE); /* 配置FSMC时序参数 */ FSMC_NORSRAMTimingInitTypeDef timing; timing.FSMC_AddressSetupTime = 2; timing.FSMC_AddressHoldTime = 1; timing.FSMC_DataSetupTime = 5; /* ...其他参数配置... */ } ``` 3. **内存管理修改** - 方案1:自定义内存分配函数 ```c void *ExternalMalloc(size_t size) { // 调用外部存储控制器接口分配内存 return ExtSRAM_Alloc(size); } void ExternalFree(void *ptr) { ExtSRAM_Free(ptr); } // 重定义FreeRTOS内存分配宏 #define pvPortMalloc ExternalMalloc #define vPortFree ExternalFree ``` - 方案2:静态分配(推荐) ```c StackType_t xTaskStack[1024] __attribute__((section(".external_ram"))); // 链接到外部存储区 xTaskCreateStatic(vTaskFunction, "Task", 1024, NULL, tskIDLE_PRIORITY, xTaskStack, NULL); ``` 4. **链接脚本调整(以GCC为例)** ```ld MEMORY { INTERNAL_RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K EXTERNAL_RAM (xrw) : ORIGIN = 0x60000000, LENGTH = 1M } SECTIONS { .external_stack (NOLOAD) : { . = ALIGN(8); _sexternal_stack = .; . = . + _external_stack_size; _eexternal_stack = .; } > EXTERNAL_RAM } ``` 5. **注意事项** - **访问速度**:外部存储通常比内部RAM慢3-5倍,需测试任务切换时间是否可接受 - **数据一致性**:启用MPU保护(若存在)或定期刷新缓存(针对SDRAM) - **错误处理**:建议添加ECC校验或CRC检查机制 - **实时性影响**:通过`uxTaskGetStackHighWaterMark()`监控栈使用量 6. **性能优化建议** - 关键任务(如中断服务例程)保留在内部RAM - 使用`xTaskCreateRestricted()`设置内存访问权限 - 启用`configSTACK_DEPTH_TYPE`设置为uint32_t以支持大容量栈 实践案例:在STM32H743平台上使用SDRAM扩展任务栈 ```c // 定义任务控制块和栈 StaticTask_t xTaskTCB; StackType_t uxTaskStack[ configMINIMAL_STACK_SIZE * 4 ] __attribute__((section(".sdram_section"))); void vTask(void *pvParameters) { for(;;) { // 任务代码 } } int main(void) { SDRAM_Init(); // 初始化SDRAM控制器 xTaskCreateStatic(vTask, "ExtTask", sizeof(uxTaskStack)/sizeof(StackType_t), NULL, tskIDLE_PRIORITY + 1, uxTaskStack, &xTaskTCB); vTaskStartScheduler(); } ``` 测试验证步骤: 1. 使用`uxTaskGetStackHighWaterMark()`监控栈使用 2. 通过内存写入测试模式验证存储可靠性 3. 压力测试:连续创建/删除任务验证内存管理稳定性 4. 测量任务切换时间变化(使用逻辑分析仪捕获) 常见问题解决方法: 1. **HardFault异常**:检查内存对齐(需8字节对齐) 2. **数据损坏**:确认外部存储时序参数正确性 3. **性能下降**:优化关键路径代码到内部RAM执行 4. **分配失败**:检查外部存储初始化是否成功 通过合理配置,外部存储可有效扩展FreeRTOS栈空间,但需要权衡访问速度、可靠性和系统实时性要求。建议先进行小规模验证再实际部署。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值