下载 FreeRTOS
 

出色的 RTOS & 嵌入式软件

内核
最新资讯
简化任何设备的身份验证云连接。
利用 CoAP 设计节能型云连接 IoT 解决方案。
11.0.0 版 FreeRTOS 内核简介:
FreeRTOS 路线图和代码贡献流程。
使用 FreeRTOS 实现 OPC-UA over TSN。

适用于 FreeRTOS
的 Posix/Linux 模拟器 使用 GCC
[RTOS 移植]

本页记载的 FreeRTOS 移植允许 FreeRTOS 在 Linux 上运行, 就像 FreeRTOS Windows 移植(经常被称为 FreeRTOS Windows 模拟器)始终允许 FreeRTOS 在 Windows 上运行那样。该移植由 David Vrabel 开发, 灵感来自 William Davy 在 2008 年开发的 Linux 移植。

移植层的实现使用 POSIX 线程,因此本移植也被称为 POSIX 移植。请不要将它和 FreeRTOS-Plus-POSIX 库两者完全不同。 FreeRTOS-Plus-POSIX 为原生 FreeRTOS API 提供 POSIX 线程包装器,使用 POSIX API 编写的应用程序因此可以在 FreeRTOS 上运行,而 Linux/POSIX FreeRTOS 移植则让 FreeRTOS 应用程序可以在 POSIX 操作系统上运行。

就像 Windows 移植那样,FreeRTOS Linux 移植为您提供了一个方便的环境, 您可以在其中试验 FreeRTOS 并开发 FreeRTOS 应用程序,以便之后将其移植到真正的嵌入式硬件上——但 FreeRTOS 应用程序 (使用 Linux 移植)将不会显示实时行为。

 

重要事项!适用于 FreeRTOS 的 Posix/Linux 模拟器演示的使用说明

在使用模拟器演示之前,请阅读以下所有要点。 另请参见常见问题:我的应用程序未运行,哪里出错了?

源代码组织

FreeRTOS zip 文件下载内容包含所有 FreeRTOS 端口和演示应用程序的源代码,因此它所包含的文件比使用 FreeRTOS Linux 端口构建和运行预配置演示所需的文件多很多。关于 zip 文件目录结构体信息,请参见源代码组织页面。

Posix/Linux 模拟器演示

内核演示项目

(请参阅 GitHub 上的内核演示项目。) 此项目使用 Linux (POSIX) 移植演示 FreeRTOS 内核功能。通过设置定义在 main.c 顶部的常量 mainSELECTED_APPLICATION,此项目可被配置为运行简单的 Blinky 样式演示 (Blinky_DEMO), 或更全面的样式演示 (FULL_demo) 。
  • Blinky 演示

    如果 mainSELECTED_APPLICATION 被设置为 Blinky_DEMOmain() 则将调用 main_blinky(),并在 main_blinky.c 中实现此调用。main_blinky() 会创建一个非常简单的演示,该演示包括两项任务、一个软件定时器和一个队列。其中一个任务通过队列以 200 毫秒的频率向另一个任务反复发送值 100,而定时器则每 2000 毫秒就向同一队列发送值 200。接收任务每次从队列中收到其中一个值时都会打印出一条消息。

  • 全面演示

    如果 mainSELECTED_APPLICATION 被设置为 FULL_DEMOmain() 则将调用main_full(),并在 main_full.c 中实现此调用。由 main_full() 创建的演示主要由标准演示任务组成,这些任务除了测试 RTOS 移植和演示如何使用 FreeRTOS API 外,不执行其他特定功能。

    完整演示包含一个‘检查’任务,每 10 秒(模拟时间) 执行一次,但拥有最高优先级,以确保其能获取处理时间。它的主要功能就是检查所有标准演示任务是否仍在运行。此检查任务会维护其每次执行时向控制台输出的状态字符串。如果所有标准演示任务都运行正常,没有错误,那此字符串会包含 "OK" 和当前滴答计数。如果检测到错误,那此字符串会包含一条信息,指明哪个任务报告了此错误。

网络演示项目

(请参阅 GitHub 上的网络演示项目。)
此项目演示了如何使用 FreeRTOS-Plus-TCP TCP/IP 堆栈在 Linux 上联网。它会重新使用原来为使用 FreeRTOS-Plus-TCP 和 Windows RTOS 移植而编写的 TCP 回显客户端演示。

TCP 回显演示使用 FreeRTOS-Plus-TCP TCP/IP 堆栈连接 至 端口 7 上的TCP标准 回显服务器TCP,并与其进行通信。 适用于 Linux 的 FreeRTOS-Plus-TCP 网络接口使用 libslirp 访问网络。

配置用于演示的 TCP/IP 堆栈:

  • 请按照 软件设置 #1、软件设置 #2 和软件设置 #4 部分 的说明进行操作, 具体内容可在描述在 Windows 主机上使用 FreeRTOS-Plus-TCP 的页面上找到(无需参考软件设置 #3 部分的内容)。

  • configECHO_SERVER_ADDR0configECHO_SERVER_ADDR3 常量设置为 FreeRTOSConfig.h 中的回显服务器地址。

防火墙经常拦截 TCP 端口 7(标准回显端口)。若碰到这种情况, 将 FreeRTOS 应用程序和回显服务器使用的端口号改成高且有效的 数字(例如 50000)即可。FreeRTOS 应用程序使用的端口号由 echoECHO_PORT 常量设置, 该常量就在 TCPEchoClient_SingleTasks.c 中。 如果您在 Linux 中创建一个使用 nc 命令的 TCP 回显服务器,那请使用 -l switch 设置端口号:

$ sudo nc -l 7

网络故障排除

如果回显服务器与 FreeRTOS 演示在同一台计算机上运行,那可能无法发送 ARP 响应,从而导致演示无法连接到回显服务器。若出现此问题,请在另一台计算机上运行回显服务器,不要在执行 RTOS 演示的那台计算机上运行它。

构建 Posix/Linux 模拟器演示

  • 前提条件(下列输出显示了测试期间使用的版本):

    1. gcc
      $ gcc --version
      gcc (GCC) 9.2.0
    2. make
      $ make --version
      GNU Make 3.81
      Copyright (C) 2006  Free Software Foundation, Inc.
    3. libpcap(用于网络支持)
      $ version: libpcap-devel-1.5.3-11.x86_64
      要在 ubuntu 上安装,请运行
      $ sudo apt-get install libpcap-dev
      要在基于 rpm 的系统上安装,请运行
      $ sudo yum install libpcap-devel
      $ sudo dnf install libpcap-devel
      要在 MacOS 上安装,请运行
      $ brew install libpcap
      或者也可通过源代码安装,请按照 INSTAL.md 中的说明进行操作
    4. libslirp(用于网络支持)

      $ version: libslirp-dev_4.6.1-1build1_amd64
      要在 ubuntu 上安装,请运行
      $ sudo apt-get install libglib2.0-dev libslirp-dev
      要在基于 rpm 的系统上安装,请运行
      $ sudo dnf install libslirp-devel
      要在 MacOS 上安装,请运行
      $ brew install libslirp
  • 构建源代码:

    1. 导航到 内核演示源码网络演示源码中的演示目录
       $ cd FreeRTOS/Demo/Posix_GCC/
       $ cd FreeRTOS-Plus/Demo/FreeRTOS_Plus_TCP_Echo_Posix
    2. 要构建源代码,请运行:
      $ make
    3. 要清理源代码,请运行:
      $ make clean
  • 运行演示(请查看上文有关如何在可用演示之间进行选择的说明):

    1. 导航到新创建的 "build" 目录
      $ cd build
    2. 运行内核演示(Blinky 演示或 全面演示)
      $ ./posix_demo
    3. 运行联网演示 在另一台计算机上运行回显服务器
      $ sudo nc -l 7
      在您自己的计算机上运行
      $ sudo ./posix_tcp_demo
     

GDB 调试提示

本节假设您已安装并熟悉 gdb。您可以在此处找到 gdb 文档。

端口层使用两个过程信号: SIGUSR1SIGALRM。如果 pthread 没有等待信号,那么 GDB 会在收到信号时暂停进程。必须告诉 GDB 忽略(而不是打印)信号 SIGUSR1,因为每个线程都是异步接收信号的。在 GDB 中,输入:  

$ handle SIGUSR1 nostop noprint pass
以确保调试不会被信号中断。请参见:
$ man signal
了解更多信息。

或者,在主目录中创建一个名为 .gdbinit 的文件,并放入下列两行指令:

handle SIGUSR1 nostop noignore noprint
handle SIGALRM nostop noignore noprint
当您将这两行指令添加至 .gdbinit 文件后,它就会告诉 GDB 不要因收到这些信号而中断。

用于系统嘀嗒的定时器共有三种:ITIMER_REALITIMER_VIRTUALITIMER_PROF。默认的系统嘀嗒定时器是 ITIMER_VALUTIAL,因为它仅在进程 在用户空间执行时计时,因此一旦出现断点,它就停止计时。ITIMER_PROF 相当于 ITIMER_VERTICAL,但它还包括执行系统调用的时间。即使进程根本没有执行,ITIMER_REAL 也会继续 进行计时,因此它表示实时计时。ITIMER_REAL 是唯一 可用的选项,因为其他定时器不计时,除非进程真的在运行。基于此,如果 nanosleep 在 IDLE 钩子任务中被调用,那非实时定时器报告的时间几乎 不会增加。

移植层设计说明

简单的 FreeRTOS 模拟器实现只需封装平台本地线程,所有切换任务 上下文调用都将调用 OS 挂起并恢复线程 API。此模拟器使用 Posix 条件变量和信号来 控制底层 Posix 线程的执行。信号可以异步传递到线程,因此它们 可以中断目标线程的执行,而挂起线程则需等待条件变量才能恢复。

在设计多线程进程时,我们通常会使用多个线程来支持并发执行, 并在 IO 任务上实现一定程度的非阻塞。此模拟器不使用线程实现并发执行, 仅用它存储执行的上下文。信号、互斥锁和条件变量都被用来同步上下文 切换,但是否要改变上下文最终是由 FreeRTOS 调度器决定的。

创建新任务时,会创建一个 pthread 以作为执行该任务的上下文。pthread 会立即 将自身挂起,并将执行返回给创建者。当 pthread 被挂起时,它就会等待调用 pthread_cond_wait;该调用在收到恢复信号 pthread_cond_signal 前,一直处于阻塞状态。

FreeRTOS 任务可以通过两种方式进行切换:一种是通过调用 taskYIELD() 进行协同式切换,另一种则是作为 RTOS 系统嘀嗒的一部分进行抢先式切换。在此模拟器中,任务上下文通过恢复下一个任务上下文(由 FreeRTOS 调度器决定)和暂停当前上下文(两者之间有个简短的握手)进行切换。

RTOS 系统滴答通过使用 ITIMER 生成,并且信号会被传递至(仅传递至)当前正在执行的 pthread。RTP 系统嘀嗒信号处理程序会递增嘀嗒计数并选择下一个 RTOS 任务上下文。它会恢复线程, 并向自己发送一个挂起信号。只有当 TOS 系统嘀嗒信号处理程序退出时才会处理挂起,因为信号也要 排队。

已知问题

pthread_createpthread_exit/cancel 是系统密集型调用,可以快速饱和 处理时间。

如果您调用了被阻塞 (printf) 的系统函数和库函数,则可能会导致整个进程停止。若确实有必要 进行系统调用,则必须屏蔽该线程上的所有信号,然后在执行完系统调用后重新恢复信号接收。 您还可以创建额外的线程来模拟中断,但也需屏蔽这些线程上的信号, 这样它们就接收不到信号,从而将由 FreeRTOS 调度器安排执行,并成为 FreeRTOS 常规任务的一部分。

为防止进程窃取主机操作系统的所有空闲执行时间,请使用 nano_sleep()。它 在实现过程中不使用任何信号,但会立即从休眠/挂起进程中中止,以服务信号。 因此,使用它的最佳方式就是设置一个比 FreeRTOS 执行时间切片更长的睡眠时间,并从 空闲任务中调用它,这样进程就会处于挂起状态,直至下一个嘀嗒。

常见问题和解决方案

创建帖子

问题
使用 pthread_create 创建外部线程(例如:模拟中断)可能会影响 FreeRTOS 调度任务的方式,因为底层 RTOS 移植需要接收某些信号后才能操作。虽然这样创建出来的线程可以接收信号,但会导致调度器从其预期目标中劫持线程以服务于特定的 FreeRTOS 任务。这可能导致系统崩溃甚至处于死锁状态。

解决方案
使用 pthread_create 创建线程时,应通过向线程正文添加以下内容将信号阻塞在该线程上:

sigset_t set;
sigfillset( &set );

pthread_sigmask( SIG_SETMASK, &set, NULL );

或者,在创建线程之前阻塞信号,会出现一个更好但不可移植的解决方案:

void * args;
sigset_t set;
pthread_t tid;
pthread_attr_t attr;

sigfillset( &set );
pthread_attr_init( &attr );
pthread_attr_setsigmask_np( &attr, &set );

pthread_create( &tid, &attr, start_routine, args );

创建新任务

xTaskCreate() 创建的 RTOS 任务会将其创建的堆栈传递到 port.c 中,使得 pthread 库可以一起使用此堆栈和 pthread_attr_setstack。可创建的最小堆栈大小存在限制。该限制与平台有关,且相当于 PTHREAD_STACK_MIN。因此,pthread_create() 会创建自己的堆栈,而忽略 FreeRTOS 传递的堆栈。若出现这种情况,请不要担心,它不会对运行时系统产生不好影响,但某些使用 FreeRTOS 调试工具来显示堆栈信息或其他 FreeRTOS 变量的用户可能会遇到一些不一致问题。

作为一种变通方法,在 Posix 端口上运行时,如果所需的堆栈小于 PTHREAD_STACK_MIN,则始终传递大小为 PTHREAD_STACK_MIN 的堆栈。

 
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.