news 2026/4/18 9:44:27

5.1RTDM框架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
5.1RTDM框架

5.1 RTDM 框架

5.1.1. RTDM的诞生背景与核心目标

自所谓的双内核硬实时 Linux 扩展(如 RTLinux、 RTAI)引入以来,已经开发了大量驱动程序。但是存在接口碎片化,平台移植成本高的问题。尽管许多这些驱动程序针对相似的硬件,但大多数驱动程序定义了私有API,应用层代码与驱动强耦合,更换硬件需重写业务逻辑。由于不存在通用 API,同一硬件(如SJA1000 CAN卡)需为Xenomai、RTAI分别开发驱动,平台移植成本高。

Real-Time Driver ModelRTDM 实时驱动模型,旨在统一实时设备驱动程序和使用它们的应用程序的开发接口。

RTDM(Real-Time Driver Model)是Xenomai的一部分,用于支持实时驱动的开放与运行。它的目的就是让开发者在实时内核(Cobalt core)上编写驱动程序时,有一个统一的接口模型,而不是直接依赖Linux的内核驱动框架(例如字符设备、块设备和网络设备)。
这保证了驱动的实时性,避免因为Linux内核的调度或锁机制而引入不可预测的延迟。

独立的驱动模型(separate driver model)

  • RTDM 提供了一套与Linux内核驱动模型分离的API(例如 rtdm_driver, rtdm_device)
  • 驱动通过 RTDM 框架注册到 Xenomai 的实时核心中,而不是注册到 Linux 的 VFS 或设备模型。
  • 应用程序可以通过

独立的注册和调用(separate registration and invocation)

  • 注册(registration)
    驱动作者需要用 RTDM 的 APIrtdm_dev_register()来注册设备
  • 调用 (invocation)
    用户空间程序通过 RTDM 设备文件调用对应的实时驱动。RTDM syscalls (/dev/rtdm/…) 来调用这些驱动,绕过 Linux 的常规路径。

带外感知( Aware )驱动程序

Xenomai RTDM 驱动的历史和现状问题

  • 很多驱动在 Linux 内核里已经存在 (如 GPIO,CAN, 串口, SPI 等),但这些驱动没有实时保证。
  • Xenomai 社区要么 fork Linux 驱动, 然后改造成 RTDM 驱动,要么单独开放专用的 RTDM 驱动。这些驱动的维护要和 Linux 主线分离, 长期靠 Xenomai 社区或厂商单独维护。

针对设备,RTDM 实现了统一设备抽象层,定义两类标准化设备模型(协议设备/命名设备),覆盖90%实时场景。协议设备,支持面向消息的即通过调用socket()进行创建设备。命名设备,支持可用open()函数实例化,包括流式设备或控制类设备。为同类硬件(如UART、CAN)定义Device Profiles接口规范,实现应用与硬件解耦。

针对设备驱动,RTDM 实现了一个跨平台的 RTOS 服务抽象层,封装了任务调度、同步原语等共性服务,驱动仅依赖 RTDM API。RTOS 服务抽象层允许为任何实现 RTDM 的 Linux 平台开发可移植的设备驱动程序。

RTDM不仅能用于编写设备驱动,还可以用于扩展RTnet协议栈,RTIPC功能等,是对 Cobalt 内核的有效补充,它让 Cobalt 内核更加专注提供核心功能,更加简洁。

5.1.2 RTDM 层次结构

1. RTDM 的角色

应用程序一般总是通过硬件设备驱动程序,来访问硬件设备。RTDM在应用程序和驱动程序之间,充当了一个调解者的角色。

在 RTDM 和应用程序之间,RTDM 提供 high-level 上层API,遵循 POSIX 套接字和 I/O 模型,即用户层APIRTDM skin。在 Xenomai3 中,RTDM skinPOSIX skin的别名。通过xeno-config --skin=获取编译与链接参数时,选项--skin=rtdm--skin=posix严格等效。

在 RTDM 和驱动程序之间,RTDM 提供 low-level 底层API,旨在提供一个用于构建可移植驱动程序的小型 RTOS 抽象层。

图中的Wrapper Library包装库和Hardware Abstraction LayerHAL硬件抽象层,并不在 RTDM 的范围内。它们是可以根据需要添加的可选间接层,以实现进一步的抽象。

引入Wrapper Library包装库,可以简化相同类别的设备对上层 RTDM API 的使用。

推荐使用 HAL硬件抽象层,可以重用多个驱动程序的公共代码段,例如,附加在不同低级通信适配器驱动程序上的协议堆栈。

Xenomai的双核系统的关键就是保证head(实时域,Out-of-Band)和root(非实时域, In-Band)的连续处理,驱动也是这样。

比如,之前提到的RTDM的锁体系对 Linux-Devetail 硬自旋锁的封装链条
从最底层的硬自旋锁(hard_spinlock_t)-> 管线自旋锁(pipeline_spinlock_t)-> RTDM层的rtdm_lock_t,保证在不同上下文(IRQ/任务/管线)访问共享资源时的原子性和安全性。
驱动的处理方式:

  • 双域驱动涉及
    • RTDM 驱动把核心硬件操作放在 head (OOB)
    • 控制、配置、调度等操作放在 root (非实时域)
  • 连续处理
    • IRQ 先到 head 处理,保证低延迟响应
    • 再同步到 root, 保证 Linux 内核可以看到事件
2. RTDM 堆叠

RTDM 支持并鼓励驱动程序堆叠,如下图所示,其中堆叠了 2 层 RTDM。但驱动程序开发人员仍然可以自由定义适当的其他驱动程序层。

5.1.3 RTDM 统一设备抽象层

1. 设备模型

以 LInux 的方式看待设备可区分为 3 种基本设备类型:字符模块, 块模块, 或者一个网络模块.

RTDM 支持两种不同类型的设备。它们是根据目前实时 Linux 驱动程序的特性选择的。RTDM 不包含实时块设备模型,甚至不包含文件系统模型。

  • 协议设备 :所有面向消息的设备都属于这一组,类似于 Linux 中的网络模块。

    • 协议设备使用两个标识符进行注册,即协议族和套接字类型。它们根据 POSIX 套接字模型进行寻址,即通过调用 socket()进行创建,通过 close()进行销毁。至少,它们必须提供对发送和接收消息的支持:sendmsg()和 recvmsg(),send()/sendto()和 recv()/recvfrom()在内部映射到这些函数。此外,协议设备的驱动程序可以通过 ioctl()处理发出的请求。
    • ioctl()接口还用于将剩余的套接字调用(如 bind()、connect()、listen()、accept()、shutdown()、getsockopt()、setsockopt()、getsockname()和 getpeername())传递给驱动程序。选择这种映射是为了避免在 RTDM 层中为不常使用的函数创建大量入口点。以bind()的代码实现为例,其内部调用了ioctl()接口。
    COBALT_IMPL(int, bind, (int fd, const struct sockaddr *my_addr, socklen_t addrlen)) { struct _rtdm_setsockaddr_args args = { my_addr, addrlen }; int ret; ret = do_ioctl(fd, _RTIOC_BIND, &args); if (ret != -EADV && ret != -ENOSYS) return set_errno(ret); return __STD(bind(fd, my_addr, addrlen)); }
  • 命名设备 :这些设备在实时子系统下以唯一的清晰文本名称注册,然后可以通过 open()函数实例化。类似于 Linux 中的字符设备。

    • RTDM 不维护特定的命名层次结构或文件系统。基本上,驱动程序在选择设备名称方面是自由的,但在设备配置文件中为常见类指定了常规的命名方案。
    • 命名设备可以进一步细分为:
      • 流式设备:支持流导向 I/O(read()/write())访问的设备,例如 UART 设备,SPI 设备。
      • 功能设备:仅通过 ioctl()接口提供功能的设备。包括所有不符合消息和流导向模型的设备。
2. 设备规范(Device Profiles)

struct rtdm_devicestruct rtdm_driver结构体,是对设备和驱动的抽象,统一的结构体,可用简化设备驱动程序的实现并提高其可移植性。

struct rtdm_devicestruct rtdm_driver结构体成员的定义和取值,与设备自身特点息息相关。针对不同的硬件设备类型,RTDM 为结构体成员变量和成员函数预先定义了取值规范,及衍生出来的必要的数据结构和常量,统称为设备配置规范(Device Profiles)。

  • 设备特性

    • device_flags,设备类型
    /** 如果设置,则应用程序只能请求该设备的一个实例。 */ #define RTDM_EXCLUSIVE 0x0001 /** * 使用在 rtdm_device 描述中提供的固定 minor 进行注册。 * 如果此标志不存在,则 RTDM 核心会根据注册顺序为由驱动程序管理的设备分配 minor 号。 */ #define RTDM_FIXED_MINOR 0x0002 /** 如果设置,则设备通过命名名称进行访问。 */ #define RTDM_NAMED_DEVICE 0x0010 /** 如果设置,则设备通过协议 ID 和套接字类型组合进行访问。 */ #define RTDM_PROTOCOL_DEVICE 0x0020 /** 选择设备类型的掩码。 */ #define RTDM_DEVICE_TYPE_MASK 0x00F0 /** 标志表示 RTDM 的安全变体(尚未支持) */ #define RTDM_SECURE_DEVICE 0x80000000
    • profile_info

      • 宏定义RTDM_PROFILE_INFO,用于初始化profile_info信息。
      • __name: 类名称,自定义。
      • __id: 类主标识号,用于唯一标识该类。
        #define RTDM_CLASS_PARPORT 1 #define RTDM_CLASS_SERIAL 2 #define RTDM_CLASS_CAN 3 #define RTDM_CLASS_NETWORK 4 #define RTDM_CLASS_RTMAC 5 #define RTDM_CLASS_TESTING 6 #define RTDM_CLASS_RTIPC 7 #define RTDM_CLASS_COBALT 8 #define RTDM_CLASS_UDD 9 #define RTDM_CLASS_MEMORY 10 #define RTDM_CLASS_GPIO 11 #define RTDM_CLASS_SPI 12 #define RTDM_CLASS_PWM 13 #define RTDM_CLASS_MISC 223 #define RTDM_CLASS_EXPERIMENTAL 224 #define RTDM_CLASS_MAX 255
      • __subid: 类次标识号,用于进一步细分该类,一般由各驱动自行定义。
      • __version: 配置版本号,用于表示该类配置的版本,一般由各驱动自行定义。
    • protocol_family 协议族

      • PF_CAN
      • PF_INET
      • PF_PACKET
      • PF_RTIPC
    • socket_type 套接字类型

      • SOCK_DGRAM
      • SOCK_RAW
      • SOCK_STREAM
  • 支持的操作struct rtdm_fd_ops中定义了所有可能的操作, 驱动程序按设备需求,按需选择实现哪些操作。由于在实时上下文中与非实时上下文相比,必须使用不同的同步机制和资源分配策略,因此明确区分服务调用上下文至关重要。为了让驱动程序决定如何处理不同的上下文,可以为每个入口类型安装单独的处理程序。如果处理程序是上下文无关的,则也可以为两个入口点注册相同的处理程序。

    • open
    • socket
    • close
    • ioctl_rt
    • ioctl_nrt
    • read_rt
    • read_nrt
    • write_rt
    • write_nrt
    • recvmsg_rt
    • recvmsg_nrt
    • sendmsg_rt
    • sendmsg_nrt
    • select
    • mmap
    • get_unmapped_area
  • 类型和常量:为描述设备而引入的结构,IOCTL使用的选项或其他数据类型,以及在RTDM上下文中使用的任何常量。

    • 设备必须提供的 IOCTL 选项。
      • 通用的IOCTL选项,定义在include/rtdm/uapi/rtdm.h中,包括_RTIOC_GETSOCKOPT,_RTIOC_SETSOCKOPT,_RTIOC_BIND, _RTIOC_CONNECT等。
      • 串口相关的IOCTL选项,定义在include/rtdm/uapi/serial.h,包括RTSER_RTIOC_GET_CONFIG,RTSER_RTIOC_SET_CONFIG,RTSER_RTIOC_GET_CONTROL等。
      • CAN相关的IOCTL选项,定义在include/rtdm/uapi/can.h,包括RTCAN_RTIOC_TAKE_TIMESTAMP,RTCAN_RTIOC_RCV_TIMEOUT,RTCAN_RTIOC_SND_TIMEOUT等。
      • GPIO相关的IOCTL选项,定义在include/rtdm/uapi/gpio.h,包括GPIO_RTIOC_DIR_OUT,GPIO_RTIOC_DIR_IN,GPIO_RTIOC_IRQEN等。
      • 未其它设备及驱动定义的IOCTL选项,不再一一列举。
    • 其它数据类型和宏定义等,一般定义在include/rtdm/uapi/rtdm.hinclude/rtdm/uapi/serial.hinclude/rtdm/uapi/can.h等头文件中。
3. 设备注册和调用

通过将struct rtdm_device *dev设备描述传递给 rtdm_dev_register()来注册 RTDM 设备。

  1. 注册命名设备
static struct rtdm_driver foo_driver = { .profile_info = RTDM_PROFILE_INFO(foo, RTDM_CLASS_EXPERIMENTAL, RTDM_SUBCLASS_FOO, 42), .device_flags = RTDM_NAMED_DEVICE|RTDM_EXCLUSIVE, .device_count = 2, .context_size = sizeof(struct foo_context), .ops = { .open = foo_open, .ioctl_rt = foo_ioctl_rt, .ioctl_nrt = foo_ioctl_nrt, .close = foo_close, }, }; static struct rtdm_device foo_devices[2] = { [ 0 ... 1 ] = { .driver = &foo_driver, .label = "foo%d", }, }; MODULE_VERSION("1.0.0"); MODULE_DESCRIPTION("Ultra-void IV board driver"); MODULE_AUTHOR'"Whoever"); foo_devices[0].device_data = &some_driver_data0; ret = rtdm_dev_register(&foo_devices[0]); ... foo_devices[1].device_data = &some_driver_data1; ret = rtdm_dev_register(&foo_devices[1]);

这段代码演示了如何基于基于RTDM(Real-Time Device Model)框架,定义和注册一个命名设备的实时设备驱动程序。

首先定义了一个名为foo_driver的静态结构体,用于描述驱动程序的基本信息和操作接口。具体来说:

  • profile_info字段包含了驱动程序的配置信息,如驱动名称、类别、子类别和版本号。
  • device_flags字段设置了设备的标志,这里表明设备是命名设备并且是独占的。
  • device_count字段指定了将由该驱动程序控制的设备数量,在这里为2。
  • context_size字段定义了设备上下文的大小,即每个设备的内存需求,这里使用sizeof(struct foo_context)来获取foo_context结构体的大小。
  • ops字段是一个指向结构体的指针,该结构体定义了设备的操作函数,如打开(foo_open)、实时IO控制(foo_ioctl_rt)、非实时IO控制(foo_ioctl_nrt)和关闭(foo_close)。

接下来,定义了一个包含两个设备的静态数组foo_devices,每个设备都关联到上面定义的foo_driver驱动程序,并且其标签被设置为foo%d,这里的%d是一个占位符,实际使用时会被设备的具体编号(0或1)替换。

最后,将两个设备的数据指针分别指向不同的驱动数据结构some_driver_data0some_driver_data1,然后通过rtdm_dev_register函数注册这两个设备。

应用程序可以通过以下两种方式打开设备。

推荐的使用方式,Xenomai 应用程序使用实际的设备节点路径来打开RTDM设备。例如:

fd=open("/dev/rtdm/devname",...);

这种方法不仅符合最新的命名规范,还能避免潜在的兼容性问题和警告信息。

在Xenomai 2.x中, RTDM 设备节点使用的是旧的命名规则,到了 Xenomai 3,RTDM 框架改造后,设备节点的路径和命名方式也有了变化。这意味着,老应用程序如果直接移植到Xenomai 3上,可能会找不到原有的设备节点路径,导致open()失败。

XENO_OPT_RTDM_COMPAT_DEVNODE是一个布尔类型的配置选项,位于drivers菜单下。其默认值为y,表示启用。该选项允许应用程序在打开RTDM设备时使用旧的命名方案,这样就保证了向后兼容,减少应用移植工作量。

  • fd = open("devname", ...);
  • fd = open("/dev/devname", ...);

当应用程序使用上述旧命名方案打开RTDM设备时,如果内核配置中启用XENO_OPT_DEBUG_LEGACY,Xenomai 会在内核日志中发出警告信息。

  1. 注册协议设备
static struct rtdm_driver foo_driver = { .profile_info = RTDM_PROFILE_INFO(foo, RTDM_CLASS_EXPERIMENTAL, RTDM_SUBCLASS_FOO, 1), .device_flags = RTDM_PROTOCOL_DEVICE, .device_count = 1, .context_size = sizeof(struct foo_context), .protocol_family = PF_FOO, .socket_type = SOCK_DGRAM, .ops = { .socket = foo_socket, .close = foo_close, .recvmsg_rt = foo_recvmsg, .sendmsg_rt = foo_sendmsg, .ioctl_rt = foo_ioctl, .ioctl_nrt = foo_ioctl, .read_rt = foo_read, .write_rt = foo_write, .select = foo_select, }, }; static struct rtdm_device foo_device = { .driver = &foo_driver, .label = "foo", .device_data = &some_driver_data, }; ret = rtdm_dev_register(&foo_device); ... MODULE_VERSION("1.0.0"); MODULE_DESCRIPTION("Unexpected protocol driver"); MODULE_AUTHOR'"Whoever");

这段代码演示了如何基于基于RTDM(Real-Time Device Model)框架,定义和注册一个协议设备的实时设备驱动程序。

首先定义了一个名为foo_driver的静态结构体,用于描述驱动程序的基本信息和操作接口。具体来说:

  • profile_info: 描述了驱动程序的基本信息,包括驱动名称(foo)、类别(实验性的RTDM_CLASS_EXPERIMENTAL)、子类别(RTDM_SUBCLASS_FOO)和版本号(1)。
  • device_flags: 设备标志,表示设备是一个协议设备(RTDM_PROTOCOL_DEVICE)。
  • device_count: 指定该驱动程序控制的设备数量为1。
  • context_size: 设备上下文的大小,使用sizeof(struct foo_context)来确定。
  • protocol_family: 协议族,这里为PF_FOO,表示使用自定义的协议族。
  • socket_type: 套接字类型,这里是SOCK_DGRAM,表示数据报套接字。
  • ops: 定义了设备的操作函数,包括创建套接字(foo_socket)、关闭设备(foo_close)、实时接收消息(foo_recvmsg)、实时发送消息(foo_sendmsg)、实时IO控制(foo_ioctl_rt)、非实时IO控制(foo_ioctl_nrt)、实时读取(foo_read)、实时写入(foo_write)和选择(foo_select)。

接下来,定义了一个foo_device设备,关联到foo_driver驱动程序,并且其标签被设置为foo。其中,device_data代表设备数据指针,指向some_driver_data结构体。

最后,调用rtdm_dev_register(&foo_device)将设备注册到RTDM框架中。

在应用程序中,可直接调用socket相关接口使用 RTDM 设备。例如,初始化一个 socket 实例:

txsock = socket(PF_CAN, SOCK_RAW, 0)

具体实例,可以参考demo/posix/cobalt/can-rtt.c

5.1.4 RTDM RTOS 服务抽象层

为了提高驱动程序的可移植性,RTDM 提供了一个与底层系统无关的通用 API,涵盖基本的 RTOS 服务。该 API 旨在仅提供典型实时驱动程序所需的最小服务集。这有助于保持 RTDM 层的小巧性,并且还提高了其在其他实时 Linux 变体上的可移植性。以下服务组可用:

  • 驱动内联服务:提供驱动之间可相互调用的接口。
  • 时钟服务:提供接口用于获取系统时钟或单调时钟,都是以 64 位值表示,单位为纳秒。
  • 任务服务:此组函数允许驱动程序创建自己的实时任务,挂起用户和驱动程序任务的执行,或操作它们的特性(优先级和周期性)。
  • 定时器服务:提供定时器服务。
  • 同步服务:RTDM 提供各种基本同步服务。首先,自旋锁可用于保护小的临界路径,无论它们是否位于中断处理程序中或在非实时上下文中运行。经典的互斥锁和信号量也可用于同步实时任务。同时支持事件机制,可作为信号量的替代方案。
  • 中断管理服务:对于大多数硬件驱动程序,中断是必不可少的服务。可以使用 RTDM 注册实时中断线的处理程序,并且可以明确启用和禁用这些中断线。
  • 非实时信号服务:为了将事件从实时域传播到非实时域,可以从 RTDM 层请求特殊的信号服务。从任何上下文触发此类信号都是安全的。一旦没有更多时间关键任务待处理,注册的处理程序将在非实时上下文中执行。
  • 实用工具服务:此组服务包括实时内存分配、对用户空间内存区域的安全访问、实时安全的内核控制台输出,以及检查当前上下文是否为实时任务等待。

在后续的章节,会详细介绍上述各种服务。

5.1.5 在 Xenomai3 中集成的RTDM驱动

xenomai-v3.2.4/kernel/drivers/目录下列出了 Xenomai3 支持的驱动程序,以下是这些子目录及其包含的驱动程序的简要介绍:

  • autotune,包含自动调优驱动程序,用于优化实时系统的性能和配置。
  • serial,包含串行通信驱动程序,支持 UART(通用异步收发传输器)等串行接口设备。
  • can,包含 CAN(Controller Area Network)总线驱动程序,用于支持 CAN 网络接口设备。
  • net,包含网络驱动程序,支持以太网控制器等网络接口设备,用于实时网络通信。
  • analogy,包含模拟输入和输出驱动程序,支持 ADC(模数转换器)和 DAC(数模转换器)等模拟接口设备。
  • ipc,包含进程间通信(IPC)驱动程序,支持实时环境下的消息传递、共享内存等通信机制。
  • udd,包含用户设备驱动(UDD)驱动程序,允许用户空间程序通过 RTDM 接口与设备进行交互。
  • gpio,包含通用输入输出(GPIO)驱动程序,支持硬件上的输入输出引脚。
  • gpiopwm,包含基于 GPIO 的 PWM(脉宽调制)驱动程序,利用 GPIO 引脚实现 PWM 功能。
  • spi,包含 SPI(串行外设接口)总线驱动程序,用于支持 SPI 接口设备。

分别重点介绍串行设备和RTnet,它们分别是命名设备和协议设备的典型代表。

1. 串行设备

通过 read/write 提供对串行设备的访问,是一种命名设备。定义了 IOCTL 以操作输出状态线、获取输入线、等待设备事件,以及配置串行设备的线路特性、超时和事件。

已经支持的串行设备驱动,包括:

  • 16550A UART
  • MPC52xx UART
  • IMX UART

16550A UART驱动为例:

static struct rtdm_driver uart16550A_driver = { .profile_info = RTDM_PROFILE_INFO(uart16550A, RTDM_CLASS_SERIAL, RTDM_SUBCLASS_16550A, RTSER_PROFILE_VER), .device_flags = RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE, .device_count = MAX_DEVICES, .context_size = sizeof(struct rt_16550_context), .ops = { .open = rt_16550_open, .close = rt_16550_close, .ioctl_rt = rt_16550_ioctl, .ioctl_nrt = rt_16550_ioctl, .read_rt = rt_16550_read, .write_rt = rt_16550_write, }, }; int __init rt_16550_init(void) { struct rtdm_device *dev; ...snips... dev = kmalloc(sizeof(struct rtdm_device) + RTDM_MAX_DEVNAME_LEN, GFP_KERNEL); err = -ENOMEM; if (!dev) goto cleanup_out; dev->driver = &uart16550A_driver; dev->label = "rtser%d"; ...snips... err = rtdm_dev_register(dev); ...snips... }

上述代码定义了一个名为 uart16550A_driver 的 RTDM 驱动程序,用于支持 16550A UART 串行通信设备。该驱动程序的配置信息通过 RTDM_PROFILE_INFO 宏初始化,设置了类名称、主标识号、次标识号和版本号。驱动的设备标志包括 RTDM_NAMED_DEVICE 和 RTDM_EXCLUSIVE,表示设备可以通过名称访问且为独占设备。驱动的最大设备数量为 MAX_DEVICES,上下文大小为 sizeof(struct rt_16550_context)。

驱动程序的操作函数包括 open、close、ioctl_rt、ioctl_nrt、read_rt 和 write_rt,分别用于设备的打开、关闭、控制、读取和写入操作。

在 rt_16550_init 函数中,初始化过程中动态分配了一个 rtdm_device 结构体,并将其与 uart16550A_driver 关联。设备的标签设置为 “rtser%d”,然后通过 rtdm_dev_register 函数注册设备。如果设备注册失败,会进行相应的错误处理和清理操作。

2. RTnet 实时网络

RTnet 实时网络分为 2 大部分:实时网络协议堆栈 和 实时网络设备。

实时网络协议堆栈,定义在kernel/drivers/net/stack/目录,由不同的 RTDM 设备组成,及相关的数据结构和IOCTL定义等组成。

当前支持的协议栈包括:

  • 协议设备:实时 UDP
  • 协议设备:实时 TCP
  • 协议设备:实时 Packet

以 UDP 实时协议栈kernel/drivers/net/stack/ipv4/udp为例:

static struct rtdm_driver udp_driver = { .profile_info = RTDM_PROFILE_INFO(udp, RTDM_CLASS_NETWORK, RTDM_SUBCLASS_RTNET, RTNET_RTDM_VER), .device_flags = RTDM_PROTOCOL_DEVICE, .device_count = 1, .context_size = sizeof(struct rtsocket), .protocol_family = PF_INET, .socket_type = SOCK_DGRAM, /* default is UDP */ .ops = { .socket = rt_inet_socket, .close = rt_udp_close, .ioctl_rt = rt_udp_ioctl, .ioctl_nrt = rt_udp_ioctl, .recvmsg_rt = rt_udp_recvmsg, .sendmsg_rt = rt_udp_sendmsg, .select = rt_socket_select_bind, }, }; static struct rtdm_device udp_device = { .driver = &udp_driver, .label = "udp", }; static int __init rt_udp_init(void) { ...snips... err = rtdm_dev_register(&udp_device); ...snips... }

上述代码首先定义了一个名为udp_driver的 RTDM(Real-Time Device Model)驱动程序,用于支持实时环境下的 UDP 网络通信。该驱动程序的配置信息通过RTDM_PROFILE_INFO宏初始化,设置了类名称为udp,主标识号为RTDM_CLASS_NETWORK,次标识号为RTDM_SUBCLASS_RTNET,版本号为RTNET_RTDM_VER

驱动的设备标志为 RTDM_PROTOCOL_DEVICE,表示设备通过协议 ID 和套接字类型进行访问。设备数量为 1,上下文大小为 sizeof(struct rtsocket)。

驱动程序的操作函数包括:

  • socket: 使用 rt_inet_socket 创建套接字。
  • close: 使用 rt_udp_close 关闭套接字。
  • ioctl_rt 和 ioctl_nrt: 使用 rt_udp_ioctl 进行实时和非实时的控制操作。
  • recvmsg_rt: 使用 rt_udp_recvmsg 接收数据报。
  • sendmsg_rt: 使用 rt_udp_sendmsg 发送数据报。
  • select: 使用 rt_socket_select_bind 进行选择操作。

此外,还定义了一个 rtdm_device 结构体 udp_device,并将其与 udp_driver 关联,设备标签设置为 “udp”。

在 rt_udp_init 函数中,初始化过程中注册了 udp_device,使其在实时环境中可用。

实时网络设备,定义在kernel/drivers/net/drivers/目录。用struct rtnet_device结构体来定义实时网络设备,由rt_register_rtnetdev向实时网络协议堆栈注册网络接口,并调用 RTDM RTOS抽象层 完成相关驱动工作。

当前支持的实时网络设备如下:

  • Intel e1000,e1000e,igb,eepro100等
  • Freescale fec,mpc52xx_fec,mpc8260_fcc_enet,mpc8xx_enet,mpc8xx_fec等
  • RTL r8169,8139too等
  • Cadence MACB/GEM等
  • 其它网口如loopback,tulip,natsemi,via-rhine,at91_ether等等。

5.1.6 RTDM 驱动的设计哲学

5.1.6.1 RTDM 的设计定位:实时优先,而非硬件发现

RTDM 的设计目标是为实时驱动程序提供一个统一的接口模型,而不是一个通用的硬件抽象层(HAL)。RTDM 关注的是驱动程序的实时性和可移植性,而不是硬件发现和管理。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 8:32:03

如何测试实时协作编辑功能:在线文档的测试实践指南

随着远程办公和团队协作需求的不断增长,实时协作编辑功能(如 Google Docs、腾讯文档、飞书文档等)已成为现代办公软件的重要组成部分。这类功能允许多个用户同时编辑同一份文档,并实时同步内容变更,极大地提高了协同效…

作者头像 李华
网站建设 2026/4/17 13:04:03

如何测试一个内容推荐系统的“探索与利用”平衡?

理解测试视角下的“探索与利用”‌ 在推荐系统领域,“探索”指系统尝试向用户推荐其可能感兴趣但历史数据较少支持的内容,旨在发现用户潜在兴趣、更新用户画像、打破信息过滤泡。而“利用”则指系统基于用户已知的明确偏好,推荐高置信度的相…

作者头像 李华
网站建设 2026/4/18 8:18:22

测试一个区块链智能合约的完整流程

智能合约测试的必要性与挑战 在区块链技术日益融入金融、供应链、身份认证等核心领域的今天,智能合约作为承载自动执行业务逻辑的“链上代码”,其安全性与可靠性至关重要。一次微小的代码漏洞,就可能导致数百万甚至上亿美元资产的永久损失或…

作者头像 李华
网站建设 2026/4/18 5:37:20

艾体宝洞察 | Redis vs ElastiCache:哪个更具成本效益?

ElastiCache 最大的成本驱动因素很容易被忽视:您永远无法将节点的全部内存用作可用的键空间。根据 AWS 的官方文档,默认情况下有 25% 的内存被预留用于备份和复制等操作,这部分内存是不可用的。因此,客户实际可用的容量要小于实例…

作者头像 李华
网站建设 2026/4/18 8:48:06

产教融合新路径:无人机基础认知 AI+虚仿 实训室破解人才培养困境

随着“新双高”计划深入推进,职业教育迎来提质培优的关键时期。无人机产业作为低空经济核心组成部分,正面临人才供给与产业需求脱节的现实困境。在产教融合政策引领下,我们创新推出无人机基础认知“AI虚仿”创新实训室,通过虚拟仿…

作者头像 李华