Rtems 源码阅读
RTEMS(Real‑Time Executive for Multiprocessor Systems)是一款始于 1988 年、1993 年正式发布的开源实时操作系统,专为多处理器嵌入式环境设计,支持 POSIX 和 BSD 套接字等开放标准 API,并可运行于 ARM、PowerPC、SPARC、MIPS、RISC‑V 等 18 种处理器架构及近 200 个 BSP(Board Support Package)上。它以库形式发布,应用程序与内核静态链接为单一映像,采用单地址空间、无用户/内核隔离设计,从而简化资源管理并确保确定性响应。2025 年 1 月 22 日发布的 6.1 版本全面将构建系统由 GNU Autotools 切换到基于 Python 的 Waf,大幅提升了构建速度并优化了依赖管理,同时引入了改进的调度算法和增强的 SMP 支持。
本文章用于记录阅读 Rtems 内核源码的笔记,尝试理解其中的逻辑。Rtems 内核的版本是 6.1,在线代码网站见 https://rtems.davidingplus.cn/lxr/source/。
文件系统
系统调用
open()
open() 函数的源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| int open(const char *path, int oflag, ...) { int rv = 0; va_list ap; mode_t mode = 0; rtems_libio_t *iop = NULL;
va_start(ap, oflag); mode = va_arg(ap, mode_t);
iop = rtems_libio_allocate(); if (iop != NULL) { rv = do_open(iop, path, oflag, mode); } else { errno = ENFILE; rv = -1; }
va_end(ap);
return rv; }
|
struct rtems_libio_t
rtems_libio_t 结构体定义如下。该结构体用于表示一个文件描述符的内部状态,Rtems 中每打开一个文件都会关联一个该结构体的实例,通常简称为 iop(I/O pointer)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| typedef struct rtems_libio_tt rtems_libio_t;
struct rtems_libio_tt { Atomic_Uint flags;
off_t offset;
rtems_filesystem_location_info_t pathinfo;
uint32_t data0;
void *data1; };
|
struct rtems_filesystem_location_info_t
rtems_filesystem_location_info_t 结构体定义如下。它表示一个路径位置,用于描述文件系统中某个具体节点(如文件或目录)的位置及其访问方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| typedef struct rtems_filesystem_location_info_tt { rtems_chain_node mt_entry_node;
void *node_access;
void *node_access_2;
const rtems_filesystem_file_handlers_r *handlers;
rtems_filesystem_mount_table_entry_t *mt_entry;
} rtems_filesystem_location_info_t;
|
struct rtems_filesystem_file_handlers_r
比较重要的成员是 const rtems_filesystem_file_handlers_r *handlers
,该结构类似于 Linux 内核中的 file_operations,定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| struct _rtems_filesystem_file_handlers_r { rtems_filesystem_open_t open_h; rtems_filesystem_close_t close_h; rtems_filesystem_read_t read_h; rtems_filesystem_write_t write_h; rtems_filesystem_ioctl_t ioctl_h; rtems_filesystem_lseek_t lseek_h; rtems_filesystem_fstat_t fstat_h; rtems_filesystem_ftruncate_t ftruncate_h; rtems_filesystem_fsync_t fsync_h; rtems_filesystem_fdatasync_t fdatasync_h; rtems_filesystem_fcntl_t fcntl_h; rtems_filesystem_poll_t poll_h; rtems_filesystem_kqfilter_t kqfilter_h; rtems_filesystem_readv_t readv_h; rtems_filesystem_writev_t writev_h; rtems_filesystem_mmap_t mmap_h; };
|
struct rtems_filesystem_mount_table_entry_tt
另一个成员是 struct rtems_filesystem_mount_table_entry_tt。这个结构体的作用是为每一个已挂载的文件系统提供一个集中式的描述,包含了文件系统的根节点信息、挂载点、类型、设备、访问控制状态等关键信息。对每个文件系统,Rtems 会维护一个这样的挂载表链表。在挂载和卸载文件系统时,Rtems 会对这个结构体进行相应的初始化、操作或释放。文件系统的挂载、查找路径、访问权限、卸载等都依赖于这个结构体中记录的信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| typedef struct rtems_filesystem_mount_table_entry_tt rtems_filesystem_mount_table_entry_t;
struct rtems_filesystem_mount_table_entry_tt { rtems_chain_node mt_node;
void *fs_info;
const rtems_filesystem_operations_table *ops;
const void *immutable_fs_info;
rtems_chain_control location_chain;
rtems_filesystem_global_location_t *mt_point_node;
rtems_filesystem_global_location_t *mt_fs_root;
bool mounted;
bool writeable;
bool no_regular_file_mknod;
const rtems_filesystem_limits_and_options_t *pathconf_limits_and_options;
const char *target;
const char *type;
char *dev;
rtems_id unmount_task; };
|
在 RTEMS 中,文件系统的操作由 rtems_filesystem_operations_table 结构体统一管理,它定义了路径解析、节点创建、删除、克隆等核心操作函数,作用上相当于 Linux 中的 inode_operations。每个挂载的文件系统通过 rtems_filesystem_mount_table_entry_t 表示,类似于 Linux 的 super_block,其中包含了指向操作表 ops 的指针。当用户发起如 open、read、write 等文件访问请求时,系统首先通过 eval_path_h 函数解析路径并定位到目标节点,然后使用该节点中挂载的 rtems_filesystem_file_handlers_r(类似 Linux 的 file_operations)来完成具体操作。整个设计将挂载管理、路径解析和文件操作职责分离,形成清晰的模块边界,同时借鉴了 Linux 文件系统架构的思想。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| struct _rtems_filesystem_operations_table { rtems_filesystem_mt_entry_lock_t lock_h;
rtems_filesystem_mt_entry_unlock_t unlock_h;
rtems_filesystem_eval_path_t eval_path_h;
rtems_filesystem_link_t link_h;
rtems_filesystem_are_nodes_equal_t are_nodes_equal_h;
rtems_filesystem_mknod_t mknod_h;
rtems_filesystem_rmnod_t rmnod_h;
rtems_filesystem_fchmod_t fchmod_h;
rtems_filesystem_chown_t chown_h;
rtems_filesystem_clonenode_t clonenod_h;
rtems_filesystem_freenode_t freenod_h;
rtems_filesystem_mount_t mount_h;
rtems_filesystem_unmount_t unmount_h;
rtems_filesystem_fsunmount_me_t fsunmount_me_h;
rtems_filesystem_utimens_t utimens_h;
rtems_filesystem_symlink_t symlink_h;
rtems_filesystem_readlink_t readlink_h;
rtems_filesystem_rename_t rename_h;
rtems_filesystem_statvfs_t statvfs_h; };
|
rtems_libio_allocate()
open 函数中分配文件描述符结构使用的函数是 rtems_libio_allocate(),定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| rtems_libio_t *rtems_libio_allocate(void) { rtems_libio_t *iop;
rtems_libio_lock();
iop = rtems_libio_iop_free_head;
if (iop != NULL) { void *next;
next = iop->data1;
rtems_libio_iop_free_head = next;
if (next == NULL) { rtems_libio_iop_free_tail = &rtems_libio_iop_free_head; } }
rtems_libio_unlock();
return iop; }
|
rtems_libio_iop_free_head
rtems_libio_iop_free_head 是一个全局变量,用于维护 Rtems 文件描述符(rtems_libio_t)的空闲链表头指针。
在初始化阶段,Rtems 会预分配一定数量的 rtems_libio_t 结构,并通过 data1 字段将它们串成一个单向链表。rtems_libio_iop_free_head 指向第一个可用节点。每次 rtems_libio_allocate() 被调用时,从头部取出一个节点,并更新链表。如果分配后链表为空,rtems_libio_iop_free_tail 会被指向 &rtems_libio_iop_free_head,表示空了。释放节点时会调用一个对应的 rtems_libio_free(iop),将节点重新挂回链表尾部。
那其实对于 rtems_libio_t 链表而言,在预分配的时候就需要将 rtems_filesystem_location_info_tt 中关于文件系统全局的 rtems_filesystem_file_handlers_r 和 rtems_filesystem_mount_table_entry_tt 信息写入,这样才能保证系统调用的时候能够成功调用底层函数。
初始化阶段的函数逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| static void rtems_libio_init(void) { uint32_t i; rtems_libio_t *iop;
if (rtems_libio_number_iops > 0) { iop = rtems_libio_iop_free_head = &rtems_libio_iops[0];
for (i = 0; (i + 1) < rtems_libio_number_iops; i++, iop++) iop->data1 = iop + 1;
iop->data1 = NULL;
rtems_libio_iop_free_tail = &iop->data1; } }
|
rtems_libio_iops 是 Rtems 预先分配的 I/O 控制块数组,配置了 CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 以后,会预先创建出这个数组。
问题来了:有了这个数组,为什么还要一个额外的 free list 链表来管理呢?
1 2
| #if CONFIGURE_MAXIMUM_FILE_DESCRIPTORS > 0 rtems_libio_t rtems_libio_iops[ CONFIGURE_MAXIMUM_FILE_DESCRIPTORS ];
|
do_open()
open() 函数中分配好文件描述符结构以后,最终会到达 do_open() 函数的位置进行处理。
函数开始时,从 iop 获取文件描述符 fd,并根据 oflag 解析读写权限、创建、独占、截断和目录打开等标志。然后确定是否跟随符号链接,组合路径解析所需的权限标志 eval_flags。
接着初始化路径解析上下文,解析路径并获取当前文件系统位置。若支持创建普通文件且路径未结束,则调用创建文件的函数。随后判断是否以目录方式打开,并检查写权限和目录类型的合法性,防止写目录或以目录方式打开非目录。
路径信息保存到 iop->pathinfo,清理路径解析上下文后,设置文件控制标志,调用底层驱动的 open 函数打开文件。若成功且指定截断,则调用 ftruncate 截断文件内容,截断失败时关闭文件。
最后,若操作全部成功,设置文件打开标志并返回文件描述符;失败时释放资源并返回错误。整个过程确保了路径解析、权限检查和文件打开的正确性和安全性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| static int do_open( rtems_libio_t *iop, const char *path, int oflag, mode_t mode ) { int rv = 0;
int fd = rtems_libio_iop_to_descriptor(iop);
int rwflag = oflag + 1;
bool read_access = (rwflag & _FREAD) == _FREAD;
bool write_access = (rwflag & _FWRITE) == _FWRITE;
bool make = (oflag & O_CREAT) == O_CREAT;
bool exclusive = (oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
bool truncate = (oflag & O_TRUNC) == O_TRUNC;
bool open_dir;
#ifdef O_NOFOLLOW int follow = (oflag & O_NOFOLLOW) == O_NOFOLLOW ? 0 : RTEMS_FS_FOLLOW_LINK; #else int follow = RTEMS_FS_FOLLOW_LINK; #endif
int eval_flags = follow | (read_access ? RTEMS_FS_PERMS_READ : 0) | (write_access ? RTEMS_FS_PERMS_WRITE : 0) | (make ? RTEMS_FS_MAKE : 0) | (exclusive ? RTEMS_FS_EXCLUSIVE : 0);
rtems_filesystem_eval_path_context_t ctx;
const rtems_filesystem_location_info_t *currentloc;
bool create_reg_file;
rtems_filesystem_eval_path_start(&ctx, path, eval_flags);
currentloc = rtems_filesystem_eval_path_get_currentloc(&ctx);
create_reg_file = !currentloc->mt_entry->no_regular_file_mknod;
if (create_reg_file && rtems_filesystem_eval_path_has_token(&ctx)) { create_regular_file(&ctx, mode); }
#ifdef O_DIRECTORY open_dir = (oflag & O_DIRECTORY) == O_DIRECTORY; #else open_dir = false; #endif
if (write_access || open_dir) { mode_t type = rtems_filesystem_location_type(currentloc);
if (create_reg_file && write_access && S_ISDIR(type)) { rtems_filesystem_eval_path_error(&ctx, EISDIR); }
if (open_dir && !S_ISDIR(type)) { rtems_filesystem_eval_path_error(&ctx, ENOTDIR); } }
rtems_filesystem_eval_path_extract_currentloc(&ctx, &iop->pathinfo);
rtems_filesystem_eval_path_cleanup(&ctx);
rtems_libio_iop_flags_set(iop, rtems_libio_fcntl_flags(oflag));
rv = (*iop->pathinfo.handlers->open_h)(iop, path, oflag, mode);
if (rv == 0) {
if (truncate) { if (write_access) { rv = (*iop->pathinfo.handlers->ftruncate_h)(iop, 0); } else { rv = -1; errno = EINVAL; }
if (rv != 0) { (*iop->pathinfo.handlers->close_h)(iop); } }
if (rv == 0) { rtems_libio_iop_flags_set(iop, LIBIO_FLAGS_OPEN);
rv = fd; } else { rv = -1; } }
if (rv < 0) { rtems_libio_free(iop); }
return rv; }
|
路径解析过程
do_open() 涉及到的路径解析代码片段如下:
1 2 3 4 5
| rtems_filesystem_eval_path_start(&ctx, path, eval_flags);
currentloc = rtems_filesystem_eval_path_get_currentloc(&ctx);
|
rtems_filesystem_eval_path_start() 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| rtems_filesystem_location_info_t * rtems_filesystem_eval_path_start( rtems_filesystem_eval_path_context_t *ctx, const char *path, int eval_flags) { return rtems_filesystem_eval_path_start_with_root_and_current( ctx, path, strlen(path), eval_flags, &rtems_filesystem_root, &rtems_filesystem_current ); }
rtems_filesystem_location_info_t * rtems_filesystem_eval_path_start_with_root_and_current( rtems_filesystem_eval_path_context_t *ctx, const char *path, size_t pathlen, int eval_flags, rtems_filesystem_global_location_t *const *global_root_ptr, rtems_filesystem_global_location_t *const *global_current_ptr) { memset(ctx, 0, sizeof(*ctx));
ctx->path = path; ctx->pathlen = pathlen;
ctx->flags = eval_flags;
set_startloc(ctx, global_root_ptr, global_current_ptr);
rtems_filesystem_instance_lock(&ctx->startloc->location);
rtems_filesystem_location_clone( &ctx->currentloc, &ctx->startloc->location );
rtems_filesystem_eval_path_continue(ctx);
return &ctx->currentloc; }
|
可以看出最后进入了 rtems_filesystem_eval_path_continue() 函数,嵌套太深了。目前看不懂整个路径的解析过程。
路径解析完毕后,do_open() 函数中执行以下函数用于维护状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| currentloc = rtems_filesystem_eval_path_get_currentloc(&ctx);
create_reg_file = !currentloc->mt_entry->no_regular_file_mknod;
......
rtems_filesystem_eval_path_extract_currentloc(&ctx, &iop->pathinfo);
rtems_filesystem_eval_path_cleanup(&ctx);
|
底层文件系统的 open 函数
拿到所有信息以后,do_open() 函数中调用底层文件系统的 open() 函数真正打开文件:
1 2
| rv = (*iop->pathinfo.handlers->open_h)(iop, path, oflag, mode);
|
close()
close() 函数的源码如下。在前面更改完状态标志位后,还是会进入到底层文件系统的 close_h 函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| int close(int fd) { rtems_libio_t *iop; unsigned int flags; int rc;
if ((uint32_t)fd >= rtems_libio_number_iops) { rtems_set_errno_and_return_minus_one(EBADF); }
iop = rtems_libio_iop(fd);
flags = rtems_libio_iop_flags(iop);
while (true) { unsigned int desired; bool success;
if ((flags & LIBIO_FLAGS_OPEN) == 0) { rtems_set_errno_and_return_minus_one(EBADF); }
flags &= LIBIO_FLAGS_REFERENCE_INC - 1U;
desired = flags & ~LIBIO_FLAGS_OPEN;
success = _Atomic_Compare_exchange_uint( &iop->flags, &flags, desired, ATOMIC_ORDER_ACQ_REL, ATOMIC_ORDER_RELAXED );
if (success) { break; }
if ((flags & ~(LIBIO_FLAGS_REFERENCE_INC - 1U)) != 0) { rtems_set_errno_and_return_minus_one(EBUSY); } }
rc = (*iop->pathinfo.handlers->close_h)(iop);
rtems_libio_free(iop);
return rc; }
|
read()
read() 函数的源码如下。可以看出除了做了一些检查以外,直接调用了底层文件系统的 read_h() 函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| ssize_t read( int fd, void *buffer, size_t count ) { rtems_libio_t *iop; ssize_t n;
rtems_libio_check_buffer(buffer);
rtems_libio_check_count(count);
LIBIO_GET_IOP_WITH_ACCESS(fd, iop, LIBIO_FLAGS_READ, EBADF);
n = (*iop->pathinfo.handlers->read_h)(iop, buffer, count);
rtems_libio_iop_drop(iop);
return n; }
|
write()
write() 函数的源码如下。大致逻辑同样同 read 函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| ssize_t write( int fd, const void *buffer, size_t count ) { rtems_libio_t *iop; ssize_t n;
rtems_libio_check_buffer(buffer);
rtems_libio_check_count(count);
LIBIO_GET_IOP_WITH_ACCESS(fd, iop, LIBIO_FLAGS_WRITE, EBADF);
n = (*iop->pathinfo.handlers->write_h)(iop, buffer, count);
rtems_libio_iop_drop(iop);
return n; }
|
文件系统启动流程
rtems_filesystem_initialize()
该函数用于初始化 Rtems 的根文件系统,通常是 IMFS。官方文档 提到,其他文件系统可以被挂载,但它们只能挂载到基础文件系统中的某个目录挂载点。对于我们想注册的自定义文件系统,有两种手段,一种是在根文件系统挂载好以后,找到某个目录手动挂载新文件系统,另一种是直接修改 rtems_filesystem_root_configuration 根文件系统的配置,使用我们自己的文件系统,这样 Rtems 在启动的时候就会默认跑我们自己的文件系统。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| void rtems_filesystem_initialize(void) { int rv = 0;
const rtems_filesystem_mount_configuration *root_config = &rtems_filesystem_root_configuration;
rv = mount( root_config->source, root_config->target, root_config->filesystemtype, root_config->options, root_config->data );
if (rv != 0) rtems_fatal_error_occurred(0xABCD0002);
rv = mkdir("/dev", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (rv != 0) rtems_fatal_error_occurred(0xABCD0003);
}
|
struct rtems_filesystem_mount_configuration
挂载根文件系统的挂载配置信息的结构体是 struct rtems_filesystem_mount_configuration。该结构体是 Rtems 中用于挂载文件系统时传递参数的配置结构。它的作用是将挂载一个文件系统所需的各种信息(如设备源、挂载点、文件系统类型等)集中在一起,作为参数传给 mount() 函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| typedef struct { const char *source;
const char *target;
const char *filesystemtype;
rtems_filesystem_options_t options;
const void *data; } rtems_filesystem_mount_configuration;
|
在 rtems_filesystem_initialize() 中,根文件系统的配置是预定义好的全局变量 rtems_filesystem_root_configuration。
1 2 3 4 5 6 7
| const rtems_filesystem_mount_configuration rtems_filesystem_root_configuration = { NULL, NULL, "/", RTEMS_FILESYSTEM_READ_WRITE, &IMFS_root_mount_data, };
|
rtems_filesystem_register()
rtems_filesystem_register() 用于在 Rtems 操作系统中注册一个新的文件系统类型。它接收文件系统的类型名称和对应的挂载函数指针,动态分配内存创建一个文件系统节点,将类型名称和挂载函数保存到该节点中,并检查该类型是否已被注册。如果未注册,则将该节点添加到全局文件系统链表完成注册;如果已注册,则释放内存并返回错误。通过这个注册机制,系统能够识别和管理多种文件系统类型,并在需要时调用对应的挂载函数进行挂载操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| int rtems_filesystem_register( const char *type, rtems_filesystem_fsmount_me_t mount_h) { rtems_chain_control *chain = &filesystem_chain;
size_t type_size = strlen(type) + 1;
size_t fsn_size = sizeof(filesystem_node) + type_size;
filesystem_node *fsn = malloc(fsn_size);
char *type_storage = (char *)fsn + sizeof(*fsn);
if (fsn == NULL) rtems_set_errno_and_return_minus_one(ENOMEM);
memcpy(type_storage, type, type_size);
fsn->entry.type = type_storage;
fsn->entry.mount_h = mount_h;
rtems_libio_lock();
if (rtems_filesystem_get_mount_handler(type) == NULL) { rtems_chain_initialize_node(&fsn->node);
rtems_chain_append_unprotected(chain, &fsn->node); } else { rtems_libio_unlock();
free(fsn);
rtems_set_errno_and_return_minus_one(EINVAL); }
rtems_libio_unlock();
return 0; }
|
struct filesystem_node
struct filesystem_node 结构体的作用是将一个文件系统的描述信息封装为链表中的一个节点,使得多个文件系统表项可以通过链表的形式组织和管理。它结合了 Rtems 的链表节点结构 rtems_chain_node 与文件系统表项 rtems_filesystem_table_t,方便在系统中动态维护、查找和操作支持的文件系统。该结构体通常用于构建一个文件系统注册表,实现对多个文件系统的统一管理和遍历。
1 2 3 4 5 6 7 8 9
| typedef struct { rtems_chain_node node;
rtems_filesystem_table_t entry; } filesystem_node;
|
struct rtems_filesystem_table_t
struct rtems_filesystem_table_t 结构体的作用是描述一个可挂载的文件系统类型,包括文件系统的类型名称和对应的挂载函数。它为 RTEMS 提供了一种统一的方式来表示和管理不同类型的文件系统,使系统能够在运行时根据类型名称选择合适的挂载函数进行文件系统初始化和挂载操作。这种设计有助于扩展文件系统支持,并实现灵活的文件系统管理机制。
在 rtems_filesystem_register() 函数中,成员 type 和 mount_h 通过函数参数传入,并在函数内赋值。
1 2 3 4 5 6 7 8 9
| typedef struct rtems_filesystem_table_t { const char *type;
rtems_filesystem_fsmount_me_t mount_h; } rtems_filesystem_table_t;
|
rtems_filesystem_fsmount_me_t
rtems_filesystem_fsmount_me_t 是一个函数指针,定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
typedef int (*rtems_filesystem_fsmount_me_t)( rtems_filesystem_mount_table_entry_t *mt_entry, const void *data );
|
struct rtems_filesystem_mount_table_entry_t 结构体的作用是为每一个已挂载的文件系统提供一个集中式的描述,包含了文件系统的根节点信息、挂载点、类型、设备、访问控制状态等关键信息。Rtems 会维护一个这样的挂载表链表,每个表项都是这个结构体的一个实例。在挂载和卸载文件系统时,Rtems 会对这个结构体进行相应的初始化、操作或释放。文件系统的挂载、查找路径、访问权限、卸载等都依赖于这个结构体中记录的信息。
关于 struct rtems_filesystem_mount_table_entry_tt 和 struct _rtems_filesystem_operations_table,前面在 struct rtems_libio_t 的时候提到过,不再赘述。
rtems_fsmount()
rtems_fsmount() 函数的作用是批量挂载多个文件系统,它按照用户提供的挂载表(fstab)顺序,依次创建每个挂载点目录并调用 mount() 执行挂载操作。该函数支持错误报告和失败控制机制,允许用户根据挂载结果决定是否继续处理后续项。通过这种方式,rtems_fsmount 提供了一种统一、高效的接口,适用于系统启动时自动挂载多个文件系统,简化了挂载流程并增强了可配置性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
int rtems_fsmount( const rtems_fstab_entry *fstab_ptr, size_t fstab_count, size_t *fail_idx ) { int rc = 0; int tmp_rc; size_t fstab_idx = 0; bool terminate = false;
while (!terminate && (fstab_idx < fstab_count)) { tmp_rc = 0;
if (tmp_rc == 0) { tmp_rc = rtems_mkdir(fstab_ptr->target, S_IRWXU | S_IRWXG | S_IRWXO); if (tmp_rc != 0) { if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNTPNT_CRTERR)) { fprintf(stdout, "fsmount: creation of mount point \"%s\" failed: %s\n", fstab_ptr->target, strerror(errno)); } if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNTPNT_CRTERR)) { terminate = true; rc = tmp_rc; } } }
if (tmp_rc == 0) { tmp_rc = mount(fstab_ptr->source, fstab_ptr->target, fstab_ptr->type, fstab_ptr->options, NULL); if (tmp_rc != 0) { if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNT_FAILED)) { fprintf(stdout, "fsmount: mounting of \"%s\" to \"%s\" failed: %s\n", fstab_ptr->source, fstab_ptr->target, strerror(errno)); } if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNT_FAILED)) { terminate = true; rc = tmp_rc; } } else { if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNT_OK)) { fprintf(stdout, "fsmount: mounting of \"%s\" to \"%s\" succeeded\n", fstab_ptr->source, fstab_ptr->target); } if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNT_OK)) { terminate = true; } } }
if (!terminate) { fstab_ptr++; fstab_idx++; } }
if (fail_idx != NULL) { *fail_idx = fstab_idx; }
return rc; }
|
struct rtems_fstab_entry
struct rtems_fstab_entry 用于描述单个文件系统的挂载配置信息,包括挂载源设备或路径、挂载点目录、文件系统类型以及挂载时的选项和行为控制标志。它为系统批量挂载文件系统时提供了统一的数据格式,使挂载操作可以根据该结构体中的信息依次执行,支持对挂载过程中的错误报告和中止条件进行灵活管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
typedef struct { const char *source;
const char *target;
const char *type;
rtems_filesystem_options_t options;
uint16_t report_reasons;
uint16_t abort_reasons; } rtems_fstab_entry;
|
mount()
mount() 函数用于根据指定的源路径、目标挂载点、文件系统类型和挂载选项,完成文件系统的挂载操作。它首先根据文件系统类型获取对应的挂载处理函数,然后创建一个挂载表项来保存挂载信息,调用具体文件系统的挂载函数进行挂载,并将挂载点注册到系统的文件系统层次中。如果挂载或注册失败,会进行相应的资源释放和错误处理。函数最终返回挂载结果,成功返回 0,失败返回 -1 并设置相应的错误码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
int mount( const char *source, const char *target, const char *filesystemtype, rtems_filesystem_options_t options, const void *data) { int rv = 0;
if ( options == RTEMS_FILESYSTEM_READ_ONLY || options == RTEMS_FILESYSTEM_READ_WRITE) { rtems_filesystem_fsmount_me_t fsmount_me_h = rtems_filesystem_get_mount_handler(filesystemtype);
if (fsmount_me_h != NULL) { size_t target_length = 0; rtems_filesystem_mount_table_entry_t *mt_entry = alloc_mount_table_entry( source, target, filesystemtype, &target_length);
if (mt_entry != NULL) { mt_entry->writeable = options == RTEMS_FILESYSTEM_READ_WRITE;
rv = (*fsmount_me_h)(mt_entry, data); if (rv == 0) { if (target != NULL) { rv = register_subordinate_file_system(mt_entry, target); } else { rv = register_root_file_system(mt_entry); }
if (rv != 0) { (*mt_entry->ops->fsunmount_me_h)(mt_entry); } }
if (rv != 0) { free(mt_entry); } } else { errno = ENOMEM; rv = -1; } } else { errno = EINVAL; rv = -1; } } else { errno = EINVAL; rv = -1; }
return rv; }
|
参考文档
- RTEMS Filesystem Design Guide (6.1). — RTEMS Filesystem Design Guide 6.1 (22nd January 2025) documentation
- rtems文件系统部分 - 《ext4文件系统移植》 - 极客文档