精华内容
下载资源
问答
  • Zephyr_FileSystems

    2019-05-16 14:42:10
    Zephyr RTOS 的虚拟文件系统开关允许应用程序在不同的挂载点(如:/fatfs 和 /nffs)挂载多个文件系统。挂载点数据结构包含实例化、挂载和操作文件系统所需的所有必要的信息。文件系统开关通过引入文件系统注册机制...

    1. 文件系统(File Systems)

    Zephyr RTOS 的虚拟文件系统开关允许应用程序在不同的挂载点(如:/fatfs 和 /nffs)挂载多个文件系统。挂载点数据结构包含实例化、挂载和操作文件系统所需的所有必要的信息。文件系统开关通过引入文件系统注册机制,将应用程序从直接访问一个文件系统指定的 API 或内部函数分离开。
    

    在 Zephyr 中,任何文件系统的实现或库可以通过一个文件系统注册 API 插入或拔出。

    int fs_register(enum fs_type type, struct fs_file_system_t *fs)int fs_unregister(enum fs_type type, struct fs_file_system_t *fs);
    

    Zephyr RTOS 通过使用挂载点作为磁盘卷名来支持一个文件系统的多个实例,文件系统库在格式化或挂载到一个磁盘时使用这个卷名。

    文件系统的声明如下:

    static struct fs_mount_t mp = {
        .type = FS_FATFS,                    // FS_FATFS:文件系统的类型,例如:FATFS 或 NFFS。
        .mnt_point = FATFS_MNTP,       // FATFS_MNTP:文件系统的挂载点。
        .fs_data = &fat_fs,                  // fat_fs:将被 fs_mount( ) API 使用的文件系统数据。
    };
    

    2. 已知限制(Known Limitations)

    由于库内部实现的限制,NFFS 只支持文件系统的一个实例。
    

    3. 示例(Sample)

    在 samples/subsys/fs 中提供了如何使用文件系统的示例。
    

    4. API 参考(API Reference)

    4.1. 定义(Defines)

    • FS_SEEK_SET
    • FS_SEEK_CUR
    • FS_SEEK_END

    4.2. 枚举(Enums)

    • enum fs_dir_entry_type
      值:
      FS_DIR_ENTRY_FILE = 0
      FS_DIR_ENTRY_DIR

    • enum fs_type
      值:
      FS_FATFS = 0
      FS_NFFS
      FS_TYPE_END

    4.3. 函数(Functions)

    • int fs_open(struct fs_file_t * zfp, const char * file_name)
      打开文件。
      打开一个已存在文件或创建一个新的文件,并将一个流与其关联。
      参数:

      • zfp:指向文件对象的指针。
      • file_name:要打开的文件名。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_close(struct fs_file_t * zfp)
      关闭文件。
      刷新关联的流,并关闭文件。
      参数:

      • zfp:指向文件对象的指针。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • fs_unlink(const char * path)
      删除文件链接(File unlink)。
      删除指定的文件或目录。
      参数:

      • path:要删除的文件或目录的路径。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_rename(const char * from, const char * to)
      文件或目录重命名。
      执行指定源路径的重命名和或移动到指定的目的地。源路径可以引用文件或目录。目标路径中的所有中间目录必须已经存在。如果源路径引用文件,则目标路径必须包含完整的文件名路径,而不仅仅是新的父目录。如果一个对象已经在指定的目标路径上存在,这个函数会在重命名之前将其删除链接(即:目标被损毁(the destination gets clobbered))。
      参数:

      • from:源路径。
      • to:目的路径。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • ssize_t fs_read(struct fs_file_t * zfp, void * ptr, size_t size)
      读文件。
      返回:读取的字节数。如果成功,读取的字节数将等于要求读取的字节数。如果文件中没有足够的有效字节数,将返回少于要求读取的字节数。发生错误时将返回 -ERRNO
      参数:

      • zfp:指向文件对象的指针。
      • ptr:指向数据缓存区的指针。
      • size:要求读取的字节数。
    • ssize_t fs_write(struct fs_file_t * zfp, const void * ptr, size_t size)
      写文件。
      返回:写入的字节数。如果成功,写入的字节数将等于要求写入的字节数。其它任何值都表示出错。错误时将返回 -ERRNO。在返回 -ERRNO 的情况下,由于无法启动操作,文件指针将不会被提前(will not be advanced)。如果它能够写入,但不能写入所有要求的字节数,这是因为磁盘已经满了。这种情况下,它将返回少于要求写入的字节数,但不像常规错误那样返回 -ERRNO
      参数:

      • zfp:指向文件对象的指针。
      • ptr:指向数据缓存区的指针。
      • size:要求写入的字节数。
    • int fs_seek(struct fs_file_t * zfp, off_t offset, int whence)
      偏移文件。
      将文件位置移动到文件中的新位置。偏移量是基于 whence 增加到新位置的。
      参数:

      • zfp:指向文件对象的指针。
      • offset:要将文件指针移动的相对偏移。
      • whence:计算偏移量的相对位置。
        • FS_SEEK_SET:从文件起始处开始偏移。
        • FS_SEEK_CUR:从当前位置开始偏移。
        • FS_SEEK_END:从文件结尾开始偏移。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • off_t fs_tell(struct fs_file_t * zfp)
      获取文件的当前位置。
      参数:

      • zfp:指向文件对象的指针。

      返回值:

      • position:文件当前的位置,当前修订不验证文件对象(Current position in file Current revision does not validate the file object)。
    • int fs_truncate(struct fs_file_t * zfp, off_t length)
      更改打开文件的大小。
      如果文件的新长度小于当前文件的大小,则将其截断为新的长度。如果文件的新长度大于当前文件的大小,则将其扩张为新的长度。扩张后的区域填充 0。
      Note:在扩张的情况下,如果在扩张时卷已经被填满了,则函数扩张到最大可能的长度并返回成功。调用者应检查扩张的长度与要求的长度是否一致。
      参数:

      • zfp:指向文件对象的指针。
      • length:文件的新长度。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_sync(struct fs_file_t * zfp)
      刷新打开文件的写缓存。
      该函数可以用于刷新打开文件的缓存。可以调用该函数来确保数据立刻写入到存储介质。这样做可能是为了避免在意外断电时丢失数据。**注意,关闭文件时会进行刷新缓存,因此如果文件正在关闭,则不需要调用该函数。

      参数:

      • zfp:指向文件对象的指针。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_mkdir(const char * path)
      创建目录。
      使用指定路径创建一个新目录。
      参数:

      • path:要创建新目录的路径。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_opendir(struct fs_dir_t * zdp, const char * path)
      打开目录。
      使用指定路径打开一个已存在的目录。
      参数:

      • zdp:指向目录对象的指针。
      • path:要打开新目录的路径。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_readdir(struct fs_dir_t * zdp, struct fs_dirent * entry)
      读取目录。
      读取打开目录的目录项。
      返回:在目录末尾(In end-of-dir)的情况下,将返回 0 并设置 entry->name[0] = 0
      参数:

      • zdp:指向目录对象的指针。
      • entry:指向要读取目录项的存放的结构体 zfs_dirent 的指针。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_closedir(struct fs_dir_t * zdp)
      关闭目录。
      关闭一个打开的目录。
      参数:

      • zdp:指向目录对象的指针。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_mount(struct fs_mount_t * mp)
      挂载文件系统。
      执行挂载文件系统所需的步骤,如调用文件系统指定挂载函数并将挂载点挂载到文件系统列表中。
      参数:

      • mp:指向结构体 fs_mount_t的指针。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_unmount(struct fs_mount_t * mp)
      卸载文件系统。
      执行卸载文件系统所需的步骤,如调用文件系统指定卸载函数并将挂载点从文件系统列表中移除。
      参数:

      • mp:指向结构体 fs_mount_t的指针。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_stat(const char * path, struct fs_dirent * entry)
      文件或目录的状态。
      检查指定路径的文件或目录的状态。
      参数:

      • path:文件或目录的路径。
      • entry:指向结构体 zfs_dirent 的指针,如果文件或目录存在时则填充该结构体。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_statvfs(const char * path, struct fs_statvfs * stat)
      检索文件系统卷的统计信息。
      返回文件系统卷中总的可用空间。
      参数:

      • path:挂载目录的路径。
      • stat:指向结构体 zfs_statvfs 的指针,用于接收文件系统的统计信息。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_register(enum fs_type type, struct fs_file_system_t * fs)
      注册一个文件系统。
      用虚拟文件系统注册文件系统。
      参数:

      • type:文件系统的类型(如:FS_FATFS)
      • fs:指向文件系统的指针。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。
    • int fs_unregister(enum fs_type type, struct fs_file_system_t * fs)
      注销文件系统。
      用虚拟文件系统注销文件系统。
      参数:

      • type:文件系统的类型(如:FS_FATFS)
      • fs:指向文件系统的指针。

      返回值:

      • 0:成功。
      • -ERRNO:错误代码。

    4.4. 结构体(struct)

    • struct fs_mount_t
      文件系统挂载信息结构体。#include <fs.h>
      成员:

      • nodefs_mount_list 列表的入口。
      • type:文件系统的类型。
      • mnt_point:挂载点目录名称(如:“/fatfs”)
      • fs_data:指向文件系统特定数据的指针。
      • storage_dev:指向后端存储设备的指针。
      • mountp_len:挂载点字符串的长度。
      • fs:指向挂载点文件系统接口的指针。
    • struct fs_dirent
      结构体用于接收文件或目录信息。#include <fs.h>
      用于读取目录项以获取文件或目录信息的函数中。
      成员:

      • dir_entry_type:文件或目录。
        • FS_DIR_ENTRY_FILE
        • FS_DIR_ENTRY_DIR
      • name:文件或目录的名称。
      • size:文件的大小。如果是目录则为 0。
    • struct fs_statvfs
      结构体用于接收卷的统计信息。
      用于检索卷中总的可用空间信息。
      成员:

      • f_bsize:最优传递块大小。
      • f_frsize:分配单位大小。
      • f_blocks:FS 以 f_frsize 为单位的大小。
      • f_bfree:空闲块的个数。
    • struct fs_file_system_t
      文件系统接口结构体。 #include <fs.h>
      成员:

      • open:打开一个已存在的文件或创建一个新的文件。
      • read:读取数据项的数字节。
      • write:写入数字节到数据项。
      • lseek:将文件位置移动到文件中的新位置。
      • tell:检索在文件中的当前位置。
      • truncate:截短文件到新长度。
      • sync:刷新打开文件的缓存。
      • close:刷新相关流并关闭文件。
      • opendir:通过指定路径打开已存在的目录。
      • readdir:读取打开目录的目录项。
      • closedir:关闭一个打开的目录。
      • mount:挂载一个文件系统。
      • unmount:卸载一个文件系统。
      • unlik:删除指定的文件或目录。
      • rename:文件或目录重命名。
      • mkdir:使用指定路径创建一个新的目录。
      • stat:通过指定路径检查文件或目录的状态。
      • statvfs:返回文件系统卷中总的可用空间。
    展开全文
  • zephyr操作系统 Some time ago we announced PVS-Studio's new feature that enabled it to integrate into PlatformIO. Naturally, our team kept in touch with the PlatformIO team while working on that ...

    zephyr操作系统

    PVS-Studio and Zephyr

    Some time ago we announced PVS-Studio's new feature that enabled it to integrate into PlatformIO. Naturally, our team kept in touch with the PlatformIO team while working on that feature, and they suggested that we check the real-time operating system Zephyr to see if we could find any interesting bugs in its code. We thought it was a good idea, and so here's this article about the check results.

    不久前,我们宣布了PVS-Studio的新功能,使其能够集成到PlatformIO中。 自然,我们的团队在使用该功能时与PlatformIO团队保持联系,他们建议我们检查实时操作系统Zephyr,看看是否可以在其代码中找到任何有趣的错误。 我们认为这是个好主意,因此本文是有关检查结果的文章。

    PlatformIO (PlatformIO)

    Before proceeding with the main topic of this article, I'd like to mention PlatformIO to the developers of embedded systems — it can make their life a bit easier. PlatformIO is a cross-platform tool for microcontroller programming. The core of PlatformIO is a command-line tool, however it is recommended to use it as a plugin for Visual Studio Code. It supports a large number of modern microchips, and boards based on them. It can automatically download suitable build systems. The site has a large collection of libraries for managing plug-in electronic components. There is support for several static code analyzers, including PVS-Studio.

    在继续本文的主要主题之前,我想向嵌入式系统开发人员提及PlatformIO-它可以使他们的生活更轻松一些。 PlatformIO是用于微控制器编程的跨平台工具。 PlatformIO的核心是命令行工具,但是建议将其用作Visual Studio Code的插件。 它支持大量的现代微芯片和基于它们的板。 它可以自动下载合适的构建系统。 该站点具有大量用于管理插入式电子组件的库。 支持多种静态代码分析器,包括PVS-Studio。

    PVS工作室 (PVS-Studio)

    PVS-Studio is not much known in the world of embedded systems yet, so here's a brief overview of our tool in case you haven't heard of it. Our regular readers may skip over to the next section.

    PVS-Studio在嵌入式系统领域尚不为人所知,因此,如果您没有听说过它,那么这里简要介绍一下我们的工具。 我们的普通读者可以跳到下一部分。

    PVS-Studio is a static code analyzer that can detect bugs and potential vulnerabilities in the code of programs written in C, C++, C#, and Java. As for C and C++, the following compilers are supported: PVS-Studio是静态代码分析器,可以检测用C,C ++,C#和Java编写的程序的代码中的错误和潜在漏洞。 对于C和C ++,支持以下编译器:
    • Windows. Visual Studio 2010-2019 C, C++, C++/CLI, C++/CX (WinRT)

      视窗。 Visual Studio 2010-2019年C,C ++,C ++ / CLI,C ++ / CX(WinRT)
    • Windows. IAR Embedded Workbench, C/C++ Compiler for ARM C, C++

      视窗。 IAR嵌入式工作台,用于ARM C,C ++的C / C ++编译器
    • Windows. QNX Momentics, QCC C, C++

      视窗。 QNX Momentics,QCC C,C ++
    • Windows/Linux. Keil µVision, DS-MDK, ARM Compiler 5/6 C, C++

      Windows / Linux。 Keil µVision,DS-MDK,ARM编译器5/6 C,C ++
    • Windows/Linux. Texas Instruments Code Composer Studio, ARM Code Generation Tools C, C++

      Windows / Linux。 德州仪器Code Composer Studio,ARM代码生成工具C,C ++
    • Windows/Linux/macOS. GNU Arm Embedded Toolchain, Arm Embedded GCC compiler, C, C++

      Windows / Linux / macOS。 GNU Arm嵌入式工具链,Arm嵌入式GCC编译器,C,C ++
    • Windows/Linux/macOS. Clang C, C++

      Windows / Linux / macOS。 Clang C,C ++
    • Linux/macOS. GCC C, C++

      Linux / macOS。 GCC C,C ++
    • Windows. MinGW C, C++

      视窗。 MinGW C,C ++

    The analyzer uses its own warning classification system, but you can also have warnings issued according to the coding standards CWE, SEI CERT, and MISRA.

    分析仪使用自己的警告分类系统,但是您也可以根据编码标准CWESEI CERTMISRA发出警告。

    PVS-Studio can be quickly adopted and put to regular use even in a large legacy project. This is achieved thanks to a special mechanism of mass warning suppression. It makes the analyzer treat all existing warnings as technical debt and hide them, thus allowing you to focus on the warnings produced only on newly written or modified code. This enables the team to start using the analyzer in their everyday work, getting back every now and then to address the technical debt and gradually eliminate it.

    即使在大型遗留项目中,也可以快速采用PVS-Studio并使其正常使用。 这要归功于特殊的群众警告抑制机制。 它使分析仪将所有现有警告视为技术欠债,并将其隐藏起来,从而使您可以将精力集中在仅在新编写或修改的代码上产生的警告。 这使团队可以开始在日常工作中使用分析仪,时不时找回解决技术问题的方法,并逐步消除它。

    PVS-Studio allows many other use scenarios. For instance, you can run it as a plugin for SonarQube. It can also integrate with such systems as Travis CI, CircleCI, GitLab CI/CD, and so on. A detailed description of PVS-Studio is outside the scope of this article, so please refer to the following article, which offers many useful links and answers to many questions: "Why You Should Choose the PVS-Studio Static Analyzer to Integrate into Your Development Process".

    PVS-Studio允许许多其他使用场景。 例如,您可以将其作为SonarQube的插件运行。 它还可以与Travis CI,CircleCI,GitLab CI / CD等系统集成。 PVS-Studio的详细描述不在本文的讨论范围之内,因此,请参考以下文章,该文章提供了许多有用的链接和许多问题的答案:“ 为什么要选择PVS-Studio静态分析仪来集成到您的开发中处理 “。

    和风 (Zephyr)

    While working on PVS-Studio's integration into PlatformIO, we kept in touch with the PlatformIO team, and they suggested that we check Zephyr, a project from the embedded software world. We liked the idea, and that's how this article appeared.

    在致力于将PVS-Studio集成到PlatformIO中的过程中 ,我们与PlatformIO团队保持联系,他们建议我们检查来自嵌入式软件世界的项目Zephyr。 我们喜欢这个主意,这就是这篇文章的样子。

    Zephyr is a small real-time operating system for connected, resource-constrained and embedded devices (with an emphasis on microcontrollers) supporting multiple architectures and released under the Apache License 2.0. Supported platforms: ARM (Cortex-M0, Cortex-M3, Cortex-M4, Cortex-M23, Cortex-M33, Cortex-R4, Cortex-R5, Cortex-A53), x86, x86-64, ARC, RISC-V, Nios II, Xtensa. Zephyr是一个小型实时操作系统,用于支持多种架构的连接的,资源受限的嵌入式设备(重点是微控制器),并根据Apache License 2.0发布。 支持的平台:ARM(Cortex-M0,Cortex-M3,Cortex-M4,Cortex-M23,Cortex-M33,Cortex-R4,Cortex-R5,Cortex-A53),x86,x86-64,ARC,RISC-V, Nios II,Xtensa。

    Here are some of its distinguishing features:

    以下是其一些特色:

    • Single address space. Combines application-specific code with a custom kernel to create a monolithic image that gets loaded and executed on a system's hardware.

      单个地址空间。 将特定于应用程序的代码与自定义内核结合在一起,以创建一个整体映像,该映像将在系统的硬件上加载并执行。
    • Highly configurable / Modular for flexibility. Allows an application to incorporate only the capabilities it needs as it needs them, and to specify their quantity and size.

      高度可配置的/模块化的灵活性。 允许应用程序根据需要仅合并所需的功能,并指定其数量和大小。
    • Compile-time resource definition. Reduces code size and increases performance for resource-limited systems.

      编译时资源定义。 减少代码大小并提高资源受限系统的性能。
    • Minimal error checking. The same as the previous one, with complete debugging information provided during testing if needed.

      最小的错误检查。 与上一个相同,并在测试过程中根据需要提供了完整的调试信息。
    • A number of services for development: multi-threading, interrupt, inter-thread synchronization, memory allocation, power management, and many other services.

      许多开发服务:多线程,中断,线程间同步,内存分配,电源管理和许多其他服务。

    One of the curious things about Zephyr is that Synopsys is involved into its development. In 2014, Synopsys bought Coverity, the creator of a static analyzer of the same name.

    关于Zephyr的一件奇怪的事情是Synopsys参与了其开发。 2014年,Synopsys收购了Coverity,这是同名静态分析仪的创建者。

    Hence it's only natural that Zephyr is being checked with Coverity from the very beginning. This tool is a leader among analyzers, which helped guarantee the high quality of Zephyr's source code.

    因此,从一开始就对Zephyr进行Coverity检查是很自然的。 该工具是分析仪中的佼佼者,有助于保证Zephyr源代码的高质量。

    和风代码的质量 (The quality of Zephyr's code)

    If you ask me, Zephyr is a high-quality project. These are the reasons why I think so:

    如果您问我,Zephyr是一个高质量的项目。 这些是我这么认为的原因:

    • PVS-Studio produced 122 general-purpose warnings of the High level and 367 warnings of the Medium level. That's not much, considering the total number of C/C++ files checked – 560. The kernel is checked through samples checking. The total estimate I got for the project was 7810 C/C++ files and 10075 header files, which means the check covered only part of the project. But then again, I didn't aim at checking the entire code base, and the number of warnings I got still corresponds to a low warning density.

      PVS-Studio生成了122个高级通用警报和367个中级警报。 考虑到已检查的C / C ++文件总数– 560,这不是很多。通过示例检查来检查内核。 我对该项目的总估算是7810个C / C ++文件和10075个头文件,这意味着检查仅涵盖了项目的一部分。 但是话又说回来,我的目的不是检查整个代码库,而我得到的警告数量仍然对应于低警告密度。
    • Many of the warnings turned out to be false positives or «semi-false positives». What I mean by the latter is explained below.

      许多警告被证明是误报或“半误报”。 我对后者的解释如下。
    • I used the SourceMonitor utility to scan Zephyr's source code, and according to its statistics, comments make 48% of the code. That's quite a bit, and, as my practice proves, it means the developers really care about their code's quality and readability.

      我使用SourceMonitor实用程序扫描Zephyr的源代码,根据其统计数据,注释占了代码的48%。 这相当多,而且正如我的实践所证明的,这意味着开发人员真的很在乎其代码的质量和可读性。

    • The project is checked with the Coverity static analyzer. This must explain why PVS-Studio – while having found some bugs – still hasn't performed as impressively as it sometimes does when analyzing other projects.

      使用Coverity静态分析器检查项目。 这必须可以解释为什么PVS-Studio尽管发现了一些错误,但是在分析其他项目时却没有有时表现得那么出色。

    Taking all this into account, I conclude that the project's authors care about their code's quality and reliability. Now let's look at some of the warnings issued by the PVS-Studio analyzer (version 7.06).

    考虑到所有这些因素,我得出的结论是,该项目的作者在乎其代码的质量和可靠性。 现在,让我们看一下PVS-Studio分析仪(版本7.06)发出的一些警告。

    «半假»警告 («Semi-false» warnings)

    Since the project's code deals with low-level functionality, it's written in a specific way and uses a lot of conditional compilation (#ifdef). This leads to a large number of warnings that don't point at genuine bugs yet can't be viewed as outright false. This can be best explained with examples.

    由于该项目的代码处理低级功能,因此它是以特定方式编写的,并使用了很多条件编译(#ifdef)。 这导致大量警告不指向真正的错误,但不能被视为完全错误。 最好用示例来解释。

    “半假”肯定:示例1 («Semi-false» positives: example 1)

    static struct char_framebuffer char_fb;
    
    int cfb_framebuffer_invert(struct device *dev)
    {
      struct char_framebuffer *fb = &char_fb;
    
      if (!fb || !fb->buf) {
        return -1;
      }
    
      fb->inverted = !fb->inverted;
    
      return 0;
    }

    PVS-Studio diagnostic message: V560 A part of conditional expression is always false: !fb. cfb.c 188

    PVS-Studio诊断消息: V560条件表达式的一部分始终为false:!fb。 cfb.c 188

    Obtaining the address of a static variable always yields a non-null pointer, so the fb pointer is never equal to zero and, therefore, the check is not needed.

    获取静态变量的地址始终会生成非空指针,因此fb指针永远不会等于零,因此不需要进行检查。

    Yet it's obviously not a bug but simply a redundant, harmless check. Besides, the compiler will optimize it away when building a Release version, so this check wouldn't even cause any slowdown.

    但这显然不是错误,而只是多余的,无害的检查。 此外,编译器将在构建发行版时对其进行优化,因此此检查甚至不会导致任何速度下降。

    That's what I call «semi-false» positives. Technically, the analyzer is totally correct in pointing out that redundant check, and it's better to remove it. On the other hand, minor issues like that are too petty and plain even to mention here.

    这就是我所说的“半假”肯定。 从技术上讲,分析仪指出冗余检查是完全正确的,最好将其删除。 另一方面,诸如此类的小问题太小而太简单了,甚至在这里也没有提及。

    “半假”肯定:示例2 («Semi-false» positives: example 2)

    int hex2char(u8_t x, char *c)
    {
      if (x <= 9) {
        *c = x + '0';
      } else if (x >= 10 && x <= 15) {
        *c = x - 10 + 'a';
      } else {
        return -EINVAL;
      }
      return 0;
    }

    PVS-Studio diagnostic message: V560 A part of conditional expression is always true: x >= 10. hex.c 31

    PVS-Studio诊断消息: V560条件表达式的一部分始终为true:x> =10。hex.c 31

    Again, the analyzer is technically correct by pointing out an always-true conditional subexpression. If the x variable is not less than or equal to 9, then it naturally will be always greater than or equal to 10. So the code can be simplified:

    同样,分析器通过指出始终为真的条件子表达式在技术上是正确的。 如果x变量不小于或等于9,则它自然总是大于或等于10。因此可以简化代码:

    } else if (x <= 15) {

    Again, the redundant check is not a true, harmful bug but just a decoration.

    同样,多余的检查不是真正的有害错误,而只是装饰。

    “半假”肯定:示例3,更复杂的情况 ( «Semi-false» positives: example 3, more complicated case)

    First let's look at the possible implementations of the CHECKIF macro:

    首先,让我们看一下CHECKIF宏的可能实现:

    #if defined(CONFIG_ASSERT_ON_ERRORS)
    #define CHECKIF(expr) \
      __ASSERT_NO_MSG(!(expr));   \
      if (0)
    #elif defined(CONFIG_NO_RUNTIME_CHECKS)
    #define CHECKIF(...) \
      if (0)
    #else
    #define CHECKIF(expr) \
      if (expr)
    #endif

    Depending on the compilation mode, the check will be either executed or skipped. In our case, when analyzing the project with PVS-Studio, the following implementation was selected:

    根据编译模式,将执行或跳过检查。 在我们的案例中,当使用PVS-Studio分析项目时,选择了以下实现:

    #define CHECKIF(expr) \
      if (expr)

    Let's see where we get from here.

    让我们看看从这里到哪里。

    int k_queue_append_list(struct k_queue *queue, void *head, void *tail)
    {
      CHECKIF(head == NULL || tail == NULL) {
        return -EINVAL;
      }
    
      k_spinlock_key_t key = k_spin_lock(&queue->lock);
      struct k_thread *thread = NULL;
      if (head != NULL) {
        thread = z_unpend_first_thread(&queue->wait_q);
      }
      ....
    }

    PVS-Studio diagnostic message: V547 [CWE-571] Expression 'head != NULL' is always true. queue.c 244

    PVS-Studio诊断消息: V547 [CWE-571]表达式'head!= NULL'始终为true。 队列c 244

    The analyzer believes the (head != NULL) check is always true. That's correct. If the head pointer is equal to NULL, the check at the beginning of the function will have it exit sooner:

    分析仪认为(head!= NULL)检查始终为真。 没错 如果指针等于NULL,则函数开始处的检查将使它更快退出:

    CHECKIF(head == NULL || tail == NULL) {
      return -EINVAL;
    }

    As a reminder, this is what the macro expands into in this implementation:

    提醒一下,这是宏在此实现中的扩展:

    if (head == NULL || tail == NULL) {
      return -EINVAL;
    }

    So again, PVS-Studio is technically right and its warning is to the point. But you can't just remove the check because it's still needed. Should the other scenario be selected, the macro would expand as follows:

    同样,PVS-Studio在技术上是正确的,其警告已到了重点。 但是您不能只删除支票,因为它仍然需要。 如果选择了其他情况,则宏将如下扩展:

    if (0) {
      return -EINVAL;
    }

    Now you want the redundant check. Sure, the analyzer wouldn't produce the warning in that case, but it does in this one, where we deal with the Debug version.

    现在,您需要冗余检查。 当然,在这种情况下,分析器不会发出警告,但是在这种情况下,我们会处理Debug版本。

    I hope it's clear now where «semi-false» positives come from. They aren't a problem, though. PVS-Studio provides a handful of false warning suppression mechanisms, which are described in detail in the documentation.

    我希望现在弄清楚“半假”肯定词来自何处。 不过,这不是问题。 PVS-Studio提供了一些错误警告抑制机制,在文档中对其进行了详细说明。

    相关警告 (Relevant warnings)

    Were there any interesting warnings then? Yes, there were, and we are going to take a look at some bugs of different types. But I'd like to make two statements first:

    那时有没有有趣的警告? 是的,有,我们将研究一些不同类型的错误。 但我想先发表两个声明:

    1. Static analysis is not about one-time checks like this. The correct use strategy is to regularly run the analyzer on the project, which is actually exactly how Coverity is used in the development of Zephyr. And that's how adopting PVS-Studio or any other analyzer allows you to detect even more bugs and thus make them cheaper to fix at earlier stages.

      静态分析不是像这样的一次性检查。 正确的使用策略是定期在项目上运行分析仪,这实际上正是Zephyr开发中使用Coverity的方式。 这就是采用PVS-Studio或任何其他分析器的方式,它使您可以检测更多的错误,从而使在早期进行修复时更便宜。
    2. While writing this article, I didn't aim at finding as many bugs as possible and I could have well missed many of the bugs or erroneously discarded them as «semi-false» positives. If Zephyr's authors are reading this, I recommend that you check the project and study the analysis report on your own. Since the project is open-source and available on GitHub, you can use a free PVS-Studio licensing option.

      在撰写本文时,我的目的并不是要找到尽可能多的错误,而我很可能会错过许多错误,或者将它们错误地误认为是“半假”。 如果Zephyr的作者正在阅读本文,建议您检查项目并自己研究分析报告。 由于该项目是开源的,并且在GitHub上可用,因此您可以使用免费的PVS-Studio许可选项。

    片段1,错字 (Fragment 1, typo)

    static void gen_prov_ack(struct prov_rx *rx, struct net_buf_simple *buf)
    {
      ....
      if (link.tx.cb && link.tx.cb) {
        link.tx.cb(0, link.tx.cb_data);
      }
      ....
    }

    PVS-Studio diagnostic message: V501 [CWE-571] There are identical sub-expressions to the left and to the right of the '&&' operator: link.tx.cb && link.tx.cb pb_adv.c 377

    PVS-Studio诊断消息: V501 [CWE-571]'&&'运算符的左侧和右侧具有相同的子表达式:link.tx.cb && link.tx.cb pb_adv.c 377

    The link.tx.cb variable is checked twice. This must be a typo, with link.tx.cb_data being the second variable to be checked instead.

    link.tx.cb变量被检查两次。 这必须是一个错字,其中link.tx.cb_data是要检查的第二个变量。

    片段2,缓冲区溢出 (Fragment 2, buffer overflow)

    Let's take a look at the net_hostname_get function, which will be used further.

    让我们看一下net_hostname_get函数,将进一步使用它。

    #if defined(CONFIG_NET_HOSTNAME_ENABLE)
    const char *net_hostname_get(void);
    #else
    static inline const char *net_hostname_get(void)
    {
      return "zephyr";
    }
    #endif

    In my case, the #else branch implementation was selected at the preprocessing stage, i.e. the preprocessed file will contain the following implementation of the function:

    在我的情况下,在预处理阶段选择了#else分支实现,即,预处理文件将包含以下函数实现:

    static inline const char *net_hostname_get(void)
    {
      return "zephyr";
    }

    The function returns a pointer to an array of 7 bytes (including the terminating null character at the end of the string).

    该函数返回一个指向7个字节的数组的指针(包括在字符串末尾的终止空字符)。

    Now, here's the code where the overflow occurs.

    现在,这里是发生溢出的代码。

    static int do_net_init(void)
    {
      ....
      (void)memcpy(hostname, net_hostname_get(), MAX_HOSTNAME_LEN);
      ....
    }

    PVS-Studio diagnostic message: V512 [CWE-119] A call of the 'memcpy' function will lead to the 'net_hostname_get()' buffer becoming out of range. log_backend_net.c 114

    PVS-Studio诊断消息: V512 [CWE-119]调用'memcpy'函数将导致'net_hostname_get()'缓冲区超出范围。 log_backend_net.c 114

    After the preprocessing, MAX_HOSTNAME_LEN expands as follows:

    预处理后, MAX_HOSTNAME_LEN扩展如下:

    (void)memcpy(hostname, net_hostname_get(),
        sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx"));

    Therefore, when copying the data, the program will end up accessing memory beyond the string literal's bounds. How exactly it's going to affect the execution is hard to tell because this is undefined behavior.

    因此,复制数据时,程序最终将访问超出字符串常量范围的内存。 由于这是未定义的行为,因此很难确定它将如何影响执行。

    片段3,潜在的缓冲区溢出 (Fragment 3, potential buffer overflow)

    int do_write_op_json(struct lwm2m_message *msg)
    {
      u8_t value[TOKEN_BUF_LEN];
      u8_t base_name[MAX_RESOURCE_LEN];
      u8_t full_name[MAX_RESOURCE_LEN];
      ....
      /* combine base_name + name */
      snprintf(full_name, TOKEN_BUF_LEN, "%s%s", base_name, value);
      ....
    }

    PVS-Studio diagnostic message: V512 [CWE-119] A call of the 'snprintf' function will lead to overflow of the buffer 'full_name'. lwm2m_rw_json.c 826

    PVS-Studio诊断消息: V512 [CWE-119]调用'snprintf'函数将导致缓冲区'full_name'溢出。 lwm2m_rw_json.c 826

    Substituting the macros' values leads us to the following:

    替换宏的值会使我们得出以下结论:

    u8_t value[64];
    u8_t base_name[20];
    u8_t full_name[20];
    ....
    snprintf(full_name, 64, "%s%s", base_name, value);

    Only 20 bytes are allocated for the full_name buffer, which is where the string is formed, while the parts that the string is formed from are stored in two buffers 20 and 64 bytes long. In addition, the constant 64 passed to the snprintf function and intended to prevent the overflow is obviously a bit too large!

    仅20个字节分配给full_name缓冲区,这是字符串的形成位置,而字符串的组成部分则存储在两个20和64字节长的缓冲区中。 另外,传递给snprintf函数的用于防止溢出的常数64显然太大了!

    This code won't necessarily end up with a buffer overflow. Perhaps the developers always get away with it because the substrings are always too short. But overall, this code has no protection against an overflow, which is a classic security weakness CWE-119.

    这段代码不一定会因缓冲区溢出而结束。 也许开发人员总是不喜欢它,因为子字符串总是太短。 但是总的来说,此代码无法防止溢出,这是经典的安全漏洞CWE-119

    片段4,表达始终为真 (Fragment 4, expression is always true)

    static int keys_set(const char *name, size_t len_rd, settings_read_cb read_cb,
                        void *cb_arg)
    {
      ....
      size_t len;
      ....
      len = read_cb(cb_arg, val, sizeof(val));
      if (len < 0) {
        BT_ERR("Failed to read value (err %zu)", len);
        return -EINVAL;
      }
      ....
    }

    PVS-Studio diagnostic message: V547 [CWE-570] Expression 'len < 0' is always false. Unsigned type value is never < 0. keys.c 312

    PVS-Studio诊断消息: V547 [CWE-570]表达式'len <0'始终为false。 无符号类型值永远不会小于0。keys.c 312

    The len variable is unsigned, which means it can't be less than 0. Therefore, the error state isn't handled in any way. Elsewhere, the value returned by the read_cb function is stored in a variable of type int or ssize_t. For example:

    len变量是无符号的,这意味着它不能小于0。因此,不会以任何方式处理错误状态。 在其他地方, read_cb函数返回的值存储在intssize_t类型的变量中。 例如:

    static inline int mesh_x_set(....)
    {
     ssize_t len;
     len = read_cb(cb_arg, out, read_len);
     if (len < 0) {
     ....
    }

    注意。 (Note.)

    Actually, the

    其实,

    read_cb function doesn't look fine at all. Look at its declaration: read_cb函数看起来一点也不好。 看一下它的声明:
    static u8_t read_cb(const struct bt_gatt_attr *attr, void *user_data)

    The type u8_t is actually unsigned char.

    u8_t类型实际上是无符号字符。

    The function always returns only positive values of type unsigned char. If we store such a value into a signed variable of type int or ssize_t, it will still be a positive value. It means error state checks don't work in other cases either. But I didn't dig too deep into this issue.

    该函数始终仅返回unsigned char类型的正值。 如果我们将这样的值存储到类型为intssize_t的带符号变量中,它将仍然是正值。 这意味着错误状态检查在其他情况下也不起作用。 但是我并没有深入探讨这个问题。

    片段5,有些奇怪 (Fragment 5, something weird)

    static char *mntpt_prepare(char *mntpt)
    {
      char *cpy_mntpt;
    
      cpy_mntpt = k_malloc(strlen(mntpt) + 1);
      if (cpy_mntpt) {
        ((u8_t *)mntpt)[strlen(mntpt)] = '\0';
        memcpy(cpy_mntpt, mntpt, strlen(mntpt));
      }
      return cpy_mntpt;
    }

    PVS-Studio diagnostic message: V575 [CWE-628] The 'memcpy' function doesn't copy the whole string. Use 'strcpy / strcpy_s' function to preserve terminal null. shell.c 427

    PVS-Studio诊断消息: V575 [CWE-628]'memcpy'函数不会复制整个字符串。 使用'strcpy / strcpy_s'函数保留终端null。 shell.c 427

    The developer was trying to make a function similar to strdup but failed.

    开发人员试图制作类似于strdup的功能,但失败了。

    The warning says the memcpy function copies a string but fails to copy the terminating null character, which is a very strange behavior.

    警告说memcpy函数复制字符串,但是无法复制终止的空字符,这是非常奇怪的行为。

    You may think the copying of the terminating null takes place in the following line:

    您可能认为终止空值的复制发生在以下行中:

    ((u8_t *)mntpt)[strlen(mntpt)] = '\0';

    But that's wrong! It's a typo that causes the terminating null to get copied into itself! Note that the target array is mntpt, not cpy_mntpt. As a result, the mntpt_prepare function returns a non-terminated string.

    但这是错误的! 这是一个错字,会导致终止null被复制到自身中! 请注意,目标数组是mntpt ,而不是cpy_mntpt 。 结果, mntpt_prepare函数返回一个非终止的字符串。

    This is what should be written instead:

    这是应该写的:

    ((u8_t *)cpy_mntpt)[strlen(mntpt)] = '\0';

    But I still can't see the reason for such a complicated implementation! This code can be reduced to the following:

    但是我仍然看不到实现如此复杂的原因! 此代码可以简化为以下代码:

    static char *mntpt_prepare(char *mntpt)
    {
      char *cpy_mntpt;
    
      cpy_mntpt = k_malloc(strlen(mntpt) + 1);
      if (cpy_mntpt) {
        strcpy(cpy_mntpt, mntpt);
      }
      return cpy_mntpt;
    }

    片段6,检查前指针取消引用 (Fragment 6, pointer dereferencing before check)

    int bt_mesh_model_publish(struct bt_mesh_model *model)
    {
      ....
      struct bt_mesh_model_pub *pub = model->pub;
      ....
      struct bt_mesh_msg_ctx ctx = {
        .send_rel = pub->send_rel,
      };
      ....
      if (!pub) {
        return -ENOTSUP;
      }
      ....
    }

    PVS-Studio diagnostic message: V595 [CWE-476] The 'pub' pointer was utilized before it was verified against nullptr. Check lines: 708, 719. access.c 708

    PVS-Studio诊断消息: V595 [CWE-476]在针对nullptr对其进行验证之前,已使用了'pub'指针。 检查行:708、719。access.c 708

    This is a very common bug pattern. The pointer is first dereferenced to initialize a struct member:

    这是一个非常常见的错误模式。 首先将指针取消引用以初始化struct成员:

    .send_rel = pub->send_rel,

    And only then is it checked for null.

    然后才检查是否为空。

    片段7-9,指针在检查前取消引用 (Fragments 7-9, pointer dereferencing before check)

    int net_tcp_accept(struct net_context *context, net_tcp_accept_cb_t cb,
                       void *user_data)
    {
      ....
      struct tcp *conn = context->tcp;
      ....
      conn->accept_cb = cb;
    
      if (!conn || conn->state != TCP_LISTEN) {
        return -EINVAL;
      }
      ....
    }

    PVS-Studio diagnostic message: V595 [CWE-476] The 'conn' pointer was utilized before it was verified against nullptr. Check lines: 1071, 1073. tcp2.c 1071

    PVS-Studio诊断消息: V595 [CWE-476]在针对nullptr对其进行验证之前,已使用了'conn'指针。 检查行:1071,1073。tcp2.c 1071

    This case is the same as the previous one. No comments needed.

    这种情况与前一种情况相同。 无需评论。

    Two more errors like that:

    这样的另外两个错误:

    • V595 [CWE-476] The 'context->tcp' pointer was utilized before it was verified against nullptr. Check lines: 1512, 1518. tcp.c 1512

      V595 [CWE-476]在针对nullptr进行验证之前,已使用“ context-> tcp”指针。 检查行:1512、1518。tcp.c 1512
    • V595 [CWE-476] The 'fsm' pointer was utilized before it was verified against nullptr. Check lines: 365, 382. fsm.c 365

      V595 [CWE-476]在针对nullptr对其进行验证之前,已使用了'fsm'指针。 检查行:365、382。fsm.c 365

    片段10,检查不正确 (Fragment 10, incorrect check)

    static int x509_get_subject_alt_name( unsigned char **p,
                                          const unsigned char *end,
                                          mbedtls_x509_sequence *subject_alt_name)
    {
      ....
        while( *p < end )
        {
            if( ( end - *p ) < 1 )
                return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
                        MBEDTLS_ERR_ASN1_OUT_OF_DATA );
        ....
      }
      ....
    }

    PVS-Studio diagnostic message: V547 [CWE-570] Expression '(end — * p) < 1' is always false. x509_crt.c 635

    PVS-Studio诊断消息: V547 [CWE-570]表达式'( end- * p)<1'始终为false。 x509_crt.c 635

    Look closely at these conditions:

    仔细观察以下情况:

    • *p < end

      * p <结束
    • (end — *p) < 1

      (结尾-* p)<1

    They are mutually opposite.

    他们是相互对立的。

    If (*p < end), then (end — *p) will always yield the value 1 or larger. Something is wrong with this code, but I have no idea how it should be fixed.

    如果(* p <end),则(end-* p)将始终产生1或更大的值。 该代码有问题,但是我不知道应该如何解决。

    片段11,无法访问的代码 (Fragment 11, unreachable code)

    uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp)
    {
        if(!disp) disp = lv_disp_get_default();
        if(!disp) {
            LV_LOG_WARN("lv_disp_get_inactive_time: no display registered");
            return 0;
        }
    
        if(disp) return lv_tick_elaps(disp->last_activity_time);
    
        lv_disp_t * d;
        uint32_t t = UINT32_MAX;
        d          = lv_disp_get_next(NULL);
        while(d) {
            t = LV_MATH_MIN(t, lv_tick_elaps(d->last_activity_time));
            d = lv_disp_get_next(d);
        }
    
        return t;
    }

    PVS-Studio diagnostic message: V547 [CWE-571] Expression 'disp' is always true. lv_disp.c 148

    PVS-Studio诊断消息: V547 [CWE-571]表达式“ disp”始终为真。 lv_disp.c 148

    The function returns if disp is a null pointer. This is followed by an opposite check – whether the disp pointer is non-null (which is always true) – and the function returns all the same.

    如果disp是空指针,则函数返回。 随后进行相反的检查– disp指针是否为非null(始终为true)–函数将返回相同的结果。

    Because of this logic, part of the code in the function's body will never get control.

    由于这种逻辑,函数主体中的部分代码将永远无法获得控制。

    片段12,奇怪的返回值 (Fragment 12, strange return value)

    static size_t put_end_tlv(struct lwm2m_output_context *out, u16_t mark_pos,
            u8_t *writer_flags, u8_t writer_flag,
            int tlv_type, int tlv_id)
    {
      struct tlv_out_formatter_data *fd;
      struct oma_tlv tlv;
      u32_t len = 0U;
    
      fd = engine_get_out_user_data(out);
      if (!fd) {
        return 0;
      }
    
      *writer_flags &= ~writer_flag;
    
      len = out->out_cpkt->offset - mark_pos;
    
      /* use stored location */
      fd->mark_pos = mark_pos;
    
      /* set instance length */
      tlv_setup(&tlv, tlv_type, tlv_id, len);
      len = oma_tlv_put(&tlv, out, NULL, true) - tlv.length;
      return 0;
    }

    PVS-Studio diagnostic message: V1001 The 'len' variable is assigned but is not used by the end of the function. lwm2m_rw_oma_tlv.c 338

    PVS-Studio诊断消息: V1001分配了“ len”变量,但在功能结束时未使用该变量。 lwm2m_rw_oma_tlv.c 338

    The function has two return statements both of which return 0. It's strange for a function to return 0 in any case. And it's strange that the len variable is never used after it has been assigned a value. I strongly suspect that the programmer actually meant to write the following code:

    该函数有两个return语句,它们都返回0。在任何情况下函数都返回0很奇怪。 奇怪的是,在给len变量赋了值之后就再也没有使用它了。 我强烈怀疑程序员实际上打算编写以下代码:

    len = oma_tlv_put(&tlv, out, NULL, true) - tlv.length;
      return len;
    }

    片段13-16,同步错误 (Fragments 13-16, synchronization error)

    static int nvs_startup(struct nvs_fs *fs)
    {
      ....
      k_mutex_lock(&fs->nvs_lock, K_FOREVER);
      ....
      if (fs->ate_wra == fs->data_wra && last_ate.len) {
        return -ESPIPE;
      }
      ....
    end:
      k_mutex_unlock(&fs->nvs_lock);
      return rc;
    }

    PVS-Studio diagnostic message: V1020 The function exited without calling the 'k_mutex_unlock' function. Check lines: 620, 549. nvs.c 620

    PVS-Studio诊断消息: V1020该函数已退出,而未调用'k_mutex_unlock'函数。 检查线:620、549。nvs.c 620

    The function may return without unlocking the mutex. As far as I understand, the following should be written instead:

    该功能可能会在未解锁互斥锁的情况下返回。 据我了解,应该改写以下内容:

    static int nvs_startup(struct nvs_fs *fs)
    {
      ....
      k_mutex_lock(&fs->nvs_lock, K_FOREVER);
      ....
      if (fs->ate_wra == fs->data_wra && last_ate.len) {
        rc = -ESPIPE;
        goto end;
      }
      ....
    end:
      k_mutex_unlock(&fs->nvs_lock);
      return rc;
    }

    Three more bugs of this type:

    此类型的其他三个错误:

    • V1020 The function exited without calling the 'k_mutex_unlock' function. Check lines: 574, 549. nvs.c 574

      V1020该函数退出而未调用'k_mutex_unlock'函数。 检查行:574,549。nvs.c 574
    • V1020 The function exited without calling the 'k_mutex_unlock' function. Check lines: 908, 890. net_context.c 908

      V1020该函数退出而未调用'k_mutex_unlock'函数。 检查行:908、890。net_context.c 908
    • V1020 The function exited without calling the 'k_mutex_unlock' function. Check lines: 1194, 1189. shell.c 1194

      V1020该函数退出而未调用'k_mutex_unlock'函数。 检查行:1194、1189。shell.c 1194

    结论 (Conclusion)

    I hope you enjoyed reading this article. Be sure to check our blog for more project checks and other interesting articles.

    希望您喜欢阅读本文。 请务必查看我们的博客以获取更多项目检查和其他有趣的文章。

    Use static analyzers to eliminate tons of bugs and potential vulnerabilities at the earlier coding stage. Early bug detection is especially crucial to embedded systems, where updates are expensive and time-consuming.

    在早期的编码阶段,使用静态分析器可以消除大量的错误和潜在的漏洞。 早期错误检测对于嵌入式系统而言尤其重要,因为嵌入式系统的更新既昂贵又耗时。

    I also encourage you to go and check your own projects with PVS-Studio. For detailed information about how to do that see the article "How to quickly check out interesting warnings given by the PVS-Studio analyzer for C and C++ code?".

    我也鼓励您去PVS-Studio检查自己的项目。 有关如何执行此操作的详细信息,请参阅文章“ 如何快速检查PVS-Studio分析器针对C和C ++代码给出的有趣警告? ”。

    翻译自: https://habr.com/en/company/pvs-studio/blog/495284/

    zephyr操作系统

    展开全文
  • zephyr操作系统:第一节

    千次阅读 2016-08-23 21:50:01
    Zephyr Project是一个小巧且易扩展的实时操作系统(real-time operating system,RTOS)。它可以应用于多种“资源受限”的系统,并且支持多种架构。 开发者能够通过裁剪系统得到最佳解决方案。

    1. Zephyr OS简介

            Zephyr Project是一个小巧且易扩展的实时操作系统(real-time operating system,RTOS)。它可以应用于多种“资源受限”的系统,并且支持多种架构。

            开发者能够通过裁剪系统得到最佳解决方案。作为一个真正的开源项目,Zephyr社区会持续更新Zephyr项目来支持更多新硬件,开发者工具,传感器以及设备驱动等等。

            在Zephyr操作系统上,先进的安全性,设备管理能力,连通栈和文件系统能够很容易实现。

    2. Zephyr OS特点

    2.1 安全性

            随着越来越多的设备在物联网(Internet of Things)中互相连通,即便是最小的设备也需要具备一定级别的安全性来避免恶意攻击。

            Zephyr项目设计中已经将安全性考虑在内了。该项目在所有的开发阶段使用了深度安全生命周期,这些开发活动包括:安全性验证、渗透测试、频繁代码review、静态代码分析、威胁建模和review,通过这些方法来避免代码中的后门。实际上,Zephyr内核静态编译成单个二进制可执行文件,没有可加载内核。这种做法就将攻击代码的可能性限定在了编译阶段。

            该项目已经被多位安全性验证方面的专家进行仔细审查,在这一过程中,专家们使用了工业级安全工具来识别攻击模型。确保安全性集成是该项目的最优先考虑的事情。

            该项目管理一个安全性工作组,委任一名安全维护者来进行安全性方面的管理工作。这个管理机构成员负责保证代码开发的各个方面的安全性。

    2.2 开放源码

            Zephyr项目支持Apache2.0开源认证。项目可以用于商业、非商业解决方案。Linux基金会作为联合主办单位,该项目采用轻量级的管理以及开源结构。Linux基金会积极致力于从整个工业领域寻找那些对于Zephyr项目感兴趣的新成员,这些成员包括:工商业生产商、半导体厂商、开发工具、加速器、启动以及专业生产者。

    2.3 连通性

            Zephyr项目提供用于低功率、小内存的设备的优化的连通协议。该项目支持蓝牙、蓝牙LE、WiFi、802.15.4以及其他的标准协议,比如6Lowpan、COAProject、IPv4、IPv6以及NFC。后续项目还会继续增强这些连通的功能。

    2.4 模块化

            Zephyr项目给开发者提供了丰富的用于受限设备的软件特性。Zephyr内核以及相关软件模块可以运行在8kB到512kB的内存设备上。除此之外,Zephyr能够以完整的RTOS系统运行或者按照开发者自己的设备裁剪相关的模块。Zephyr项目将支持集成第三方组件,包括第三方库、外部模块以及应用开发。

    3. Zephyr OS运行机制

    3.1 Fiber

            Fibers是被协同调度的。

            Fibers在调用可阻塞API之后运行。可阻塞API的功能:

            > 标记成不可运行;

            > 然后,下一最高优先级Fiber被运行。

            典型的应用:Fibers被用于设备驱动以及性能要求高的工作

    3.2 Task

            一个task被调度时,在没有fibers,该task可以运行。

            tasks是基于抢占的。

            最高优先级task首先运行。

            Tasks常被用于数据处理。

    3.3 ISR

            ISRs能够中断fibers和tasks。

            默认情况下,内嵌的ISRs是打开的。

            ISRs能够标记fibers和tasks为运行态。


    Zephyr官网链接:点击打开链接https://www.zephyrproject.org/

    展开全文
  • Zephyr-系统配置(Kconfig)

    2021-11-15 17:41:56
    1 摘要 Zephyr 内核和子系统可以在构建时进行... 和linux内核配置一样,最终配置会写入.config文件,保持至zephyr/bulid路径下。 PS:当然,用户也可以直接通过zephyr/.config来修改配置symbol,这样会存在风险。上...

    目录

    1 摘要

    2 交互式配置

    2.1 menucofnig

    2.2 guiconfig

    3 Kconfig结构

    3.1 SubKconfig

    3.2 .deconfig

    3.3 Initial Configuration

    3.4 Kconfig预处理函数

    4 总结


    1 摘要

            Zephyr 内核和子系统可以在构建时进行配置,以适应特定的应用和平台需求。配置通过 Kconfig 处理,这是 Linux 内核使用的相同配置系统。目标是无需更改任何源代码下支持各种配置。

    2 交互式配置

    • 支持两种交互式配置方式:menuconfig和guiconfig。
    • 和linux内核配置一样,最终配置会写入.config文件,保存至zephyr/bulid路径下。

    PS:当然,用户也可以直接通过zephyr/.config来修改配置symbol,这样会存在风险。上述两个系统提供的交互式配置方式会很好的处理各symbol之间的依赖关系。直接修改.config往往会忽视这种场景,不建议使用

    2.1 menucofnig

    • 执行该命令后可使用menuconfig进行symbol配置
    #-b <board>:指定硬件平台
    #-t menuconfig:使用memuconfig
    #<app root>:应用工程CMakeList.txt所在路径 
    west build -b <board> -t menuconfig <app root>
    • 完成设置后保存退出,相关配置会保持至zephyr/build/zephyr/.config文件中

    2.2 guiconfig

    • 执行该命令后可使用guiconfig进行symbol配置
    #-b <board>:指定硬件平台
    #-t guiconfig:使用guiconfig
    #<app root>:应用工程CMakeList.txt所在路径 
    west build -b <board> -t guiconfig <app root>

    3 Kconfig结构

    • 顶层文件在zephyr/kconfig,直接调用Kconfig.zephyr。该文件一方面会包含各级子目录下的subkconfig文件,形成交互界面的各级菜单和symbol;另一方面包含各种.deconfig文件,具体作用下面会分析到。
    • 同时每个board会有一个初始配置Inital Configuration,它是由三个源头合并而成的。
    • Kconfig支持获取dts信息,如何使用下面会分析
    • 最后通过scripts/kconfig下的pyhton脚本生成.config和autoconf.h文件

    3.1 SubKconfig

    和linux内核配置系统一样,组成各级子菜单和symbol

    3.2 .deconfig

    官方文档说明该文件是用于修改“invisible Kconfig symbols”。按我的理解,就是不建议用户修改的特定配置。比如某个硬件板,必须特定的配置会由原厂固定写在.deconfig文件中,并且都是invisible Kconfig symbols,不会出现在交互式配置界面上。

    3.3 Initial Configuration

    • boards///_defconfig。该文件是由开发人员做为板级基本配置提交到内核中,也支持用户通过menuconfig交互界面修改。
    • 应用配置pri.conf。作用于该工程,可让用户在不修改内核的情况下,添加自己项目所需的配置信息。

            PS:pri.conf对symbol的配置优先级是最高的

    • CMake中带CONFIG_ 前缀的entries

    3.4 Kconfig预处理函数

    • Kconfiglib支持用Python编写的自定义Kconfig预处理器函数,主要应用是用于获取设备树信息。
    • 支持如下函数
    $(dt_chosen_reg_addr_int,<property in /chosen>[,<index>,<unit>])
    $(dt_chosen_reg_addr_hex,<property in /chosen>[,<index>,<unit>])
    $(dt_chosen_reg_size_int,<property in /chosen>[,<index>,<unit>])
    $(dt_chosen_reg_size_hex,<property in /chosen>[,<index>,<unit>])
    $(dt_node_reg_addr_int,<node path>[,<index>,<unit>])
    $(dt_node_reg_addr_hex,<node path>[,<index>,<unit>])
    $(dt_node_reg_size_int,<node path>[,<index>,<unit>])
    $(dt_node_reg_size_hex,<node path>[,<index>,<unit>])
    $(dt_compat_enabled,<compatible string>)
    $(dt_chosen_enabled,<property in /chosen>)
    $(dt_node_has_bool_prop,<node path>,<prop>)
    $(dt_node_has_prop,<node path>,<prop>)
    • 以官方例子说明如何使用
    //某设备树的部分信息如下
    {
         soc {
                 #address-cells = <1>;
                 #size-cells = <1>;
    
                 spi0: spi@10014000 {
                         compatible = "sifive,spi0";
                         reg = <0x10014000 0x1000 0x20010000 0x3c0900>;
                         reg-names = "control", "mem";
                         ...
                 };
    };
    
    //用户在kconfig如下配置,
    config FLASH_BASE_ADDRESS
        //获取/soc/spi@1001400子节点,序号1(第二个)block中reg_addr,并以16进制输出
         default $(dt_node_reg_addr_hex,/soc/spi@1001400,1)
         
    //经过预处理后等同于
    config FLASH_BASE_ADDRESS
         default 0x20010000

    3.5 .config & autoconf.h

    • .config:是在交互式配置退出时保存配置数据至该文件,路径在bulid/zephyr/下

    • autoconf.h:已经过预处理器转换为头文件,生成路径在bulid/zephyr/include/generated/下

    4 总结

            整体上和linux的配置系统一致,由顶层Kconfig文件和Initial Configuration入手,找到线头再慢慢深入了解。

            对于交互式界面配置也可以从zephyr\scripts\kconfig目录下的guiconfig.py和menuconfig.py入手,获取更详细的信息。

            另外zephyr还支持用户Kconfig预处理函数,是通过scripts/kconfig/kconfigfunctions.py​来实现。

    展开全文
  • 在如下目录下面执行:west build -b nrf5340dk_nrf5340_cpuapp -p 进行编译 west flash 进行代码下载
  • 对于zephyr这个实时嵌入式操作系统,可以先在x86架构上搭建运行环境,学习相关知识。之后,根据实际需求,再将操作系统移植到不同架构的MCU上。 一、环境搭建 准备工作:安装ubuntu操作系统 虚拟机软件版本:...
  • Zephyr 物联网操作系统环境搭建

    千次阅读 2017-04-26 13:55:50
    Zephyr项目构建演示 一、环境搭建 1、系统环境 系统环境 Linux version 4.2.0 (root@localhost) (gcc version 5.2.0 (GCC) ) #9 SMP Fri Feb 5 19:34:29 CST 2016 2、sdk安装 下载sdk wget ...
  • Zephyr OS 基础篇: 系统简介

    千次阅读 2016-07-28 22:25:20
    Zephyr 是一个用于物联网的开源操作系统,受到 Linux 基金会支持(参考 这里 ),在今年二月份刚发布 1.0 版本,目前开发到 1.4 版本。Zephyr 的目标非常远大,即一统物联网操作系统Zephyr 目前还处于初期...
  • Zephyr:V2.3.99 SDK:0.11.4 Date:2020-08-20 Zephyr 可以在 Windows/MAC/Linux 上开发,本文只介绍 Linux(Ubuntu) 上的环境搭建 。 2.1基于 Linux 的编译环境搭建 2.1.1安装必要工具 1.更新软件源 $sudo apt-...
  • 初识Zephyr3.1 简介3.2 快速入门3.3 编译运行第一个示例 1.前言 19年 底实验室协助某公司完成磁控胶囊胃镜胶囊机器人的开发工作,我们主要负责图像压缩算法的设计与实现。其中算法运行载体为Nordic公司的nrf52840...
  • 原文链接 官方网站 开发环境 主机: Ubuntu v18.04 Zephyr:V2.3.99 ...启动图形配置界面首先,需要初始化默认工程,生成默认的配置文件.config。先随便选择一个应用。  使用west: #west build -p
  • Zephyr是基于Cmake开发的,编译系统是一个以应用程序为中心的系统,它需要一个应用程序来初始化需要编译的内核源码树。应用程序的编译会驱使应用程序和内核的配置、编译过程的发生,并将它们最终编译成一个单一的二...
  • Zephyr OS 基础篇:系统简介

    千次阅读 2016-07-28 20:42:44
    简单介绍 Zephyr OS 的概念、特征、目录结构以及学习资料。
  • 本文主要说明Zephyr用户模式-简介中简介的系统调用访问和系统调用堆栈,和ARM相关的技术基础请参考Zephyr用户模式-技术基础用户模式下,代码被限制使用特权指令和访问内存。由于系统调用工作在特权模式下,并且系统...
  • Zephyr系统移植调试小结

    千次阅读 2017-02-28 17:05:08
    本文主要是针对本人基于ATMEL SAMV71-xult板卡一直zephyr系统过程进行小结,不足之处望大家多多指点!!
  • 测试路径:zephyr\samples\hello_world 1. nrf5340dk_nrf5340_cpuapp.overlay文件的修改 / { buttons { compatible = "gpio-keys"; /*p0.23*/ M_BUTTON1: m_button1 { gpios = <&gpio0 ...
  • zephyr学习笔记汇总

    千次阅读 2018-03-27 19:45:46
    原本 contiki 在本能手心中是排名第一的物联网系统,直到 zephyr 的出现。 2016年2月推出的这个开源项目,在 Linux 基金会的扶持下,短短时间内已经支持了几十款的嵌入式硬件,厂家支持力度大,社区活跃,展现了...
  • zephyr\samples\hello_world 需要修改的文件 1. overlay文件 / { buttons { compatible = "gpio-keys"; /*p0.23*/ M_BUTTON1: m_button1 { gpios = <&gpio0 23 (GPIO_PULL_UP|GPIO_...
  • Zephyr应用笔记:mcuboot引导zephyr镜像

    千次阅读 2018-03-08 09:41:54
    博客的宗旨:看过这篇文章的人都能够顺利开发zephyr的boot。boot采用mcuboot(version 1.1.0),Github地址:https://github.com/runtimeco/mcubootzephyr(version 1.10.0),Github地址:...
  • Zephyr下是没有文件系统的,因为Zephyr主打低功耗,如果在低功耗环境上在搭载文件系统会使得代码变得沉重与复杂化,如果我们一般想要包含一些文件的话Zephyr的Cmake提供了一组开发环境包,其中generate_inc_file_...
  • 1:zephyr内核支持很多种驱动,但是在zephyr应用中所支持的驱动,则是在zephyr应用编译时通过CONFIG配置来选择的。以此来达到控制内核及驱动裁剪的功效。 2:重要的数据结构 struct device //zephyr内核对象,用于...
  • Zephyr 编译环境搭建(Linux + Window)

    千次阅读 2018-11-24 10:48:33
    Zephyr 系统介绍 Zephyr™ 项目是一个采用 Apache 2.0 协议许可,Linux基金会托管的协作项目。为所有资源受限设备,构建了针对低功耗、小型内存微处理器设备而进行优化的物联网嵌入式小型、可扩展的实时操作系统...
  • Zephyr评估

    2019-10-18 10:34:35
    Zephyr进行快速的学习之后,得到了下面的5条总体认识。 Zephyr的内核模式是宏内核,不是微内核;设备驱动都被集成在内核中;内核编译采用Kconfig脚本配置,资源配置也通过它在编译时指定 Zephyr支持的平台:arm、...
  • 这一节是翻译自 MCUboot 网站上 MCUboot with Zephyr 。 MCUboot 最初是 Mynewt 的引导程序。此后,它也成为了 Zephyr 的引导程序。Zephyr 的应用程序在编译方式存在较大大的差异,这里将会记录这些不同之处。 ...
  • cortex-m3 的异常向量表所在的源文件是 arch/arm/core/cortex-m/vector_table.s,整个文件的源码如下(忽略部分不相关的东西): <pre>GDATA(_main_stack) SECTION_SUBSEC_FUNC(exc_vector_table,_...
  • Zephyr OS 开发极速入门

    万次阅读 2018-02-07 20:51:10
    Zephyr是由 Linux`基金会托管的开源协作项目,目标是构建一个针对资源受限设备的小型、可裁剪的实时操作系统(RTOS)。Zephyr`项目非常适合构建简单的传感器网络、可穿戴设备以及小型物联网无线网关。系统采用模块化...
  • zephyr 系统--内存片Memory Slab使用方法

    千次阅读 2017-09-02 13:01:41
    内存片(memory slab) 是一个内核对象,它允许从指定的内存区域上动态地分配内存块(memory block)。同一个内存片上面的所有内存块的尺寸是固定(相同)的,这样做的好处是可以高效地分配和释放,避免了内存碎片问题。...
  • 板级移植zephyr的所支持的针对不同板子的层级创建app创建目录创建和拷贝文件修改文件修改CMakeLists.txt修改main.c使用自己定义的板子进行编译 zephyr的所支持的针对不同板子的层级 #mermaid-svg-aoNNhRrmwugVZdW8 ....
  • Zephyr-基于Windows系统的软件开发

    千次阅读 2018-12-12 20:41:05
    ** Zephyr之前基于Linux的开发的,前不久1.11版本的宣布支持window开发。由于虚拟机占内存,博主本次想用基于Windows进行开发。当然也是基于cmd命令的。** 首先是防火墙我选择是关闭的。 用管理员身份运行...
  • 实验例程路径:zephyr\samples\hello_world 需要修改的文件 1.创建overlay文件 &adc { status = "okay"; }; 2.修改prj.conf文件 CONFIG_LOG=y CONFIG_USE_SEGGER_RTT=y CONFIG_UART_CONSOLE=n #打开...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,164
精华内容 465
关键字:

zephyr文件系统