嵌入式与实时应用中的Linux编程接口详解
1. 实时消息发送返回值
在实时消息发送操作中,
rt_send_*
函数的返回值具有重要意义:
-
0
:表示消息已成功发送,无需回复。
- 非零值:表示任务期待一个回复。同时,不建议使用
rt_isrpc
,因为
rt_return
足够智能,能够自行判断是否需要回复。
2. rtai_fifos 模块
rtai_fifos
模块提供了一种点对点的顺序机制,用于内核空间实时任务与用户空间进程之间的通信。以下是该模块的主要函数:
| 函数名 | 功能 | 返回值 |
|---|---|---|
int rtf_create (unsigned int fifo, int size)
|
创建一个初始大小为
size
的实时 FIFO(RT - FIFO),并分配标识符
fifo
|
成功:
size
;-ENODEV:
fifo
大于或等于
RTF_NO
;-ENOMEM:无法为 RT - FIFO 分配
size
字节的内存
|
int rtf_destroy (unsigned int fifo)
|
关闭之前使用
rtf_create()
或
rtf_open_sized()
创建/重新打开的实时 FIFO
|
成功:仍打开此 FIFO 的实时任务数量,0 表示 FIFO 真正关闭;-ENODEV:
fifo
大于或等于
RTF_NO
;-EINVAL:
fifo
不是有效的打开 FIFO
|
int rtf_reset (unsigned int fifo)
|
移除已发布但尚未从
fifo
中移除的任何数据
|
成功:0;-ENODEV:
fifo
大于或等于
RTF_NO
;-EINVAL:
fifo
不是有效的打开 FIFO
|
int rtf_resize (unsigned int fifo, int size)
|
修改之前使用
rtf_create()
创建的实时 FIFO 的大小为
size
,并丢弃当前 FIFO 中的任何数据
|
成功:
size
;-ENODEV:
fifo
大于或等于
RTF_NO
;-EINVAL:
fifo
不是有效的打开 FIFO;-ENOMEM:无法为 RT - FIFO 分配
size
字节的内存
|
int rtf_put (unsigned int fifo, void *buf, int count)
|
将数据块写入之前使用
rtf_create
创建的实时 FIFO
|
成功:写入的字节数;-ENODEV:
fifo
大于或等于
RTF_NO
;-EINVAL:
fifo
不是有效的打开 FIFO
|
int rtf_get (unsigned int fifo, void *buf, int count)
|
从之前使用
rtf_create
创建的实时 FIFO 中读取数据块
|
成功:读取的字节数;-ENODEV:
fifo
大于或等于
RTF_NO
;-EINVAL:
fifo
不是有效的打开 FIFO
|
int rtf_create_handler (unsigned int fifo, int (*handler)(unsigned int fifo));
int rtf_create_handler (unsigned int fifo, X_FIFO_HANDLER(handler));
| 安装一个处理程序,当数据写入或从实时 FIFO 读取时执行 |
成功:0;-EINVAL:
fifo
不是有效的打开 FIFO
|
以下是
rtf_create
函数的使用示例:
#include <rtai_fifos.h>
int main() {
unsigned int fifo = 1;
int size = 1024;
int result = rtf_create(fifo, size);
if (result == size) {
// 创建成功
} else {
// 处理错误
}
return 0;
}
3. rtai_shm 模块
rtai_shm
模块支持内核空间 RTAI 任务与用户空间进程之间的共享内存区域。
3.1 内核空间 API
-
void *rtai_kmalloc (unsigned long name, int size):分配一个名为name、大小为size字节的共享内存区域。首次调用为给定名称分配空间,后续调用(无论是内核空间还是用户空间)只需附加到该区域。- 成功:返回分配空间的地址。
- 0:无法分配请求的空间。
-
void rtai_kfree (void *adr):释放由name标识的共享内存区域。实际上,在最后一个附加到该区域的进程/任务调用rtai_kfree之前,该区域只是被取消映射。
3.2 用户空间 API
-
void *rtai_malloc (unsigned long name, int size):分配一个名为name、大小为size字节的共享内存区域。 -
void *rtai_malloc_adr (void *adr, unsigned long name, int size):请求分配一个特定地址的共享内存区域,但这只是一个“建议”,返回值可能与adr相同,也可能不同。- 成功:返回分配空间的地址。
- 0:无法分配请求的空间。
-
void rtai_free (unsigned long name, void *adr):释放由name标识的共享内存区域。
4. 实用函数
-
unsigned long nam2num (const char *name):将最多六个字符的 ASCII 名称转换为无符号长整型 ID。有效字符集为'A'到'Z'、'a'到'z'(不区分大小写)、'0'到'9'和'_',其他字符将被转换为'$'。- 成功:返回转换后的 ID。
- 无错误条件。
-
void num2nam (unsigned long id, char *name):使用与nam2num相同的算法将 ID 转换回 ASCII 名称字符串。
5. rtai_lxrt 模块
rtai_lxrt
模块允许用户空间进程(几乎)透明地使用 RTAI API。以下是该模块中与内核空间 API 不同的函数:
5.1 对象初始化
-
RT_TASK *rt_task_init (unsigned int id, int priority, int stack_size, int max_msgsize):在用户空间创建一个名为id的新实时任务,具有指定的优先级。stack_size和max_msgsize可以为 0,此时使用默认值(默认栈大小为 512 字节,默认最大消息大小为 256 字节)。- 成功:返回指向内核空间任务结构的指针,该值不能在用户空间直接使用,只能作为参数传递给其他 LXRT 函数。
-
0:无法创建伙伴任务或
id已在命名空间中注册。
-
SEM *rt_sem_init (unsigned long id, int initial_count):分配并初始化一个名为id、初始值为initial_count的信号量。- 成功:返回指向内核空间信号量结构的指针,该值不能在用户空间直接使用,只能作为参数传递给其他 LXRT 函数。
-
0:无法创建信号量或
id已在命名空间中注册。
-
MBX *rt_mbx_init (unsigned long id, int size):分配并初始化一个名为id、大小为size的邮箱。- 成功:返回指向内核空间邮箱结构的指针,该值不能在用户空间直接使用,只能作为参数传递给其他 LXRT 函数。
-
0:无法创建邮箱或
id已在命名空间中注册。
5.2 内核空间命名空间实用函数
-
int rt_register (unsigned long id, void *adr):将名称id与内核空间对象(由adr指向)关联,并将该对象注册到 LXRT 命名空间中,以便用户空间 LXRT 进程可以引用该对象。- 成功:返回正整数(实际上是命名空间表中的槽号)。
- 0:无法注册对象,命名空间表已满。
-
int rt_drg_on_adr (void *adr)和int rt_drg_on_name (unsigned long id):从命名空间中移除对象,可以通过地址(adr)或名称(id)引用要移除的对象。- 成功:返回正整数(实际上是对象在命名空间表中占用的槽号)。
- 0:无法注销对象,未在命名空间表中找到。
5.3 用户空间命名空间实用函数
-
void *rt_get_adr (unsigned long id):返回名为id的对象的地址。 -
unsigned long rt_get_name (void *adr):返回地址为adr的对象的名称。- 两个函数在请求的对象不在命名空间中时都返回 0。
5.4 用户空间硬实时
-
void rt_make_hard_real_time (void):使用户空间进程具有硬实时特性,进程必须锁定在内存中。硬实时进程应避免进行任何可能导致任务切换的 Linux 系统调用。只有 root 用户运行的进程才能锁定内存,但可以使用rt_allow_nonroot_hrt()允许非 root 用户锁定内存并调用硬实时。 -
void rt_make_soft_real_time (void):使进程恢复正常的软实时行为。 -
void rt_allow_nonroot_hrt (void):允许非 root 用户锁定内存并调用硬实时。
6. Posix 线程(Pthreads)API
RTAI 支持 Posix 1003.1c - 1995 的部分特性。Pthreads 函数通常返回一个整数状态码,0 表示函数成功,负数表示错误条件。Pthreads 区分两种类型的错误:
- 强制性(“如果发生”)错误:涉及程序员无法控制的情况,系统必须始终检测并报告此类错误。
- 可选性(“如果检测到”)错误:通常是程序员的错误,系统可能因处理器时间或其他系统资源成本过高而无法检测和报告所有潜在错误条件。一些系统可能提供“调试”模式,在开发代码时打开这些可选错误,发布生产代码时关闭调试模式。
以下是 Pthreads 相关函数的介绍:
6.1 线程操作
-
int pthread_create (pthread_t *tid, const pthread_attr_t *attr, void *(*start) (void *), void *arg):创建一个新线程,具有可选的创建属性attr。新线程执行函数start,并传递参数arg。-
错误:
-
EINVAL:attr无效。 -
EAGAIN:没有足够的资源来创建线程。
-
-
错误:
-
int pthread_exit (void *ret_val):终止调用线程,先调用任何注册的清理处理程序,ret_val返回给任何加入该线程的线程。 -
pthread_t pthread_self (void):返回调用线程的 ID。 -
int sched_yield (void):让出处理器,只有在该优先级级别的所有其他就绪线程都运行后,该线程才准备好运行。
graph TD;
A[开始] --> B[创建线程];
B --> C{线程执行};
C --> D[线程退出];
D --> E[结束];
6.2 线程属性
-
int pthread_attr_init (pthread_attr_t *attr):初始化一个线程属性对象,使用默认值。-
错误:
ENOMEM:没有足够的内存用于属性对象。
-
错误:
-
int pthread_attr_destroy (pthread_attr_t *attr):销毁一个线程属性对象。-
错误:
EINVAL:attr不是线程属性对象。
-
错误:
-
int pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detach_state)和int pthread_attr_setdetachstate (pthread_attr_t *attr, int detach_state):获取或设置线程的“分离”状态。-
detach_state:-
PTHREAD_CREATE_JOINABLE -
PTHREAD_CREATE_DETACHED(默认)
-
-
错误:
-
EINVAL:attr不是线程属性对象。 -
EINVAL:detach_state无效(仅设置时)。
-
-
6.3 调度策略属性
-
int pthread_attr_getinheritsched (const pthread_attr_t *attr, int *inherit_sched)和int pthread_attr_setinheritsched (pthread_attr_t *attr, int inherit_sched):确定使用此属性创建的线程是继承调用者的调度策略和参数,还是使用attr中的参数。-
inherit_sched:-
PTHREAD_INHERIT_SCHED -
PTHREAD_EXPLICIT_SCHED(默认)
-
-
错误:
-
ENOSYS:不支持优先级调度。 -
EINVAL:attr不是线程属性对象或inherit_sched无效(仅设置时)。
-
-
-
int pthread_attr_getschedparam (const pthread_attr_t *attr, struct sched_param *sched_p)和int pthread_attr_setschedparam (pthread_attr_t *attr, struct sched_param *sched_p):获取或设置使用此属性创建的线程的调度参数。-
错误:
-
ENOSYS:不支持优先级调度。 -
EINVAL:attr不是线程属性对象或sched_p无效(仅设置时)。
-
-
错误:
-
int pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *policy)和int pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy):获取或设置使用此属性创建的线程的调度策略。-
Policy:-
SCHED_FIFO -
SCHED_RR -
SCHED_OTHER(默认)
-
-
错误:
-
ENOSYS:不支持优先级调度。 -
EINVAL:attr不是线程属性对象或policy无效(仅设置时)。
-
-
-
int pthread_attr_getscope (const pthread_attr_t *attr, int *scope)和int pthread_attr_setscope (pthread_attr_t *attr, int scope):获取或设置使用此属性创建的线程的“竞争范围”。-
scope:-
PTHREAD_SCOPE_PROCESS -
PTHREAD_SCOPE_SYSTEM(默认)
-
-
错误:
-
ENOSYS:不支持优先级调度。 -
EINVAL:attr不是线程属性对象或scope无效(仅设置时)。
-
-
6.4 互斥锁操作
-
int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutex_attr):创建一个互斥锁对象,如果mutex_attr不为空,则提供可选属性。-
错误:
-
EAGAIN:除内存外的资源不足。 -
ENOMEM:没有足够的内存用于互斥锁对象。 -
EPERM:没有权限执行此操作。 -
EBUSY:互斥锁已初始化。 -
EINVAL:mutex_attr不是互斥锁属性对象。
-
-
错误:
-
int pthread_mutex_destroy (pthread_mutex_t *mutex):销毁不再需要的现有互斥锁。-
错误:
-
EBUSY:互斥锁正在使用中。 -
EINVAL:mutex不是互斥锁。
-
-
错误:
-
int pthread_mutex_lock (pthread_mutex_t *mutex):锁定互斥锁。如果互斥锁已锁定,调用线程将被阻塞,直到互斥锁被解锁。-
错误:
-
EINVAL:线程优先级超过互斥锁优先级上限。 -
EINVAL:mutex不是互斥锁对象。 -
EDEADLK:调用线程已经拥有互斥锁。
-
-
错误:
-
int pthread_mutex_trylock (pthread_mutex_t *mutex):尝试锁定互斥锁,如果互斥锁当前未锁定,则锁定;如果已锁定,则立即返回错误代码。-
错误:
-
EINVAL:线程优先级超过互斥锁优先级上限。 -
EBUSY:互斥锁已锁定。 -
EINVAL:mutex不是互斥锁对象。 -
EDEADLK:调用线程已经拥有互斥锁。
-
-
错误:
-
int pthread_mutex_unlock (pthread_mutex_t *mutex):解锁互斥锁,如果有线程在等待该互斥锁,其中一个线程将被唤醒并成为新的所有者。-
错误:
-
EINVAL:mutex不是互斥锁。 -
EPERM:调用线程不拥有互斥锁。
-
-
错误:
6.5 互斥锁属性
-
int pthread_mutexattr_init (pthread_mutexattr_t *mutex_attr):初始化一个互斥锁属性对象,使用默认值。-
错误:
ENOMEM:没有足够的内存用于属性对象。
-
错误:
-
int pthread_mutexattr_destroy (pthread_mutexattr_t *mutex_attr):销毁一个互斥锁属性对象。-
错误:
EINVAL:mutex_attr不是互斥锁属性对象。
-
错误:
-
int pthread_mutexattr_getkind_np (const pthread_mutexattr_t *mutex_attr, int *kind)和int pthread_mutexattr_setkind_np (pthread_mutexattr_t *mutex_attr, int kind):获取或设置互斥锁的“类型”。-
kind:-
PTHREAD_MUTEX_FAST_NP -
PTHREAD_MUTEX_RECURSIVE_NP -
PTHREAD_MUTEX_ERRORCHECK_NP
-
-
错误:
-
EINVAL:mutex_attr不是互斥锁属性对象。 -
EINVAL:kind无效(仅设置时)。
-
-
-
int pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *mutex_attr, int *prioceiling)和int pthread_mutexattr_setprioceiling (pthread_mutexattr_t *mutex_attr, int prioceiling):获取或设置使用attr创建的互斥锁的优先级上限,RTAI Pthreads 中未实现。-
错误:
-
ENOSYS:不支持优先级调度。 -
EINVAL:attr不是互斥锁属性对象或prioceiling无效(仅设置时)。 -
ENOPERM:没有权限设置优先级上限。
-
-
错误:
-
int pthread_mutexattr_getprotocol (const pthread_mutexattr_t *mutex_attr, int *protocol)和int pthread_mutexattr_setprotocol (pthread_mutexattr_t *mutex_attr, int protocol):获取或设置互斥锁处理优先级反转的协议,RTAI Pthreads 中未实现。-
protocol:-
PTHREAD_PRIO_NONE -
PTHREAD_PRIO_INHERIT -
PTHREAD_PRIO_PROTECT
-
-
错误:
-
ENOSYS:不支持优先级调度。 -
EINVAL:attr不是互斥锁属性对象或protocol无效(仅设置时)。 -
ENOTSUP:不支持protocol值。
-
-
通过以上介绍,我们可以看到在嵌入式与实时应用的 Linux 编程中,这些模块和函数提供了丰富的功能,帮助开发者实现内核空间与用户空间的通信、共享内存管理以及多线程编程等任务。开发者在使用这些函数时,需要仔细处理可能出现的错误情况,以确保程序的稳定性和可靠性。
嵌入式与实时应用中的Linux编程接口详解(续)
7. 不同模块函数使用注意事项
为了更清晰地使用上述各个模块的函数,我们将不同模块常见函数的注意事项整理成如下表格:
| 模块 | 函数 | 注意事项 |
| — | — | — |
| rtai_fifos | rtf_create |
fifo
必须小于
RTF_NO
,若
fifo
已存在,会根据需要调整大小 |
| rtai_fifos | rtf_destroy | 打开和关闭操作需成对,最后一次关闭才会真正销毁 FIFO |
| rtai_fifos | rtf_put 和 rtf_get | 仅实时任务可用,Linux 进程需通过
read()
和
write()
操作对应设备 |
| rtai_shm | rtai_kmalloc 和 rtai_malloc | 首次调用分配空间,后续同名调用仅附加 |
| rtai_shm | rtai_kfree 和 rtai_free | 最后一个使用该区域的进程/任务调用才会释放内存 |
| rtai_lxrt | rt_task_init |
stack_size
和
max_msgsize
可为 0,使用默认值 |
| Posix 线程 | pthread_create | 确保
attr
有效,避免资源不足 |
| Posix 线程 | pthread_mutex_lock | 防止死锁,注意线程优先级与互斥锁优先级上限 |
8. 函数调用流程示例
下面以一个简单的多线程程序为例,展示如何综合使用上述函数进行多线程编程,同时使用互斥锁保护共享资源。
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t mutex;
int shared_variable = 0;
void *thread_function(void *arg) {
pthread_mutex_lock(&mutex);
shared_variable++;
printf("Thread incremented shared_variable to %d\n", shared_variable);
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t thread;
int result;
// 初始化互斥锁
result = pthread_mutex_init(&mutex, NULL);
if (result != 0) {
perror("pthread_mutex_init");
return 1;
}
// 创建线程
result = pthread_create(&thread, NULL, thread_function, NULL);
if (result != 0) {
perror("pthread_create");
return 1;
}
// 等待线程结束
result = pthread_join(thread, NULL);
if (result != 0) {
perror("pthread_join");
return 1;
}
// 销毁互斥锁
result = pthread_mutex_destroy(&mutex);
if (result != 0) {
perror("pthread_mutex_destroy");
return 1;
}
return 0;
}
在这个示例中,我们创建了一个线程,该线程会对共享变量
shared_variable
进行加 1 操作。为了避免多个线程同时访问该变量导致的数据竞争问题,我们使用了互斥锁
pthread_mutex_t
来保护共享资源。
9. 错误处理策略
在使用上述函数时,错误处理是非常重要的。不同的错误类型需要不同的处理方式,以下是一些常见的错误处理策略:
-
资源不足错误
:如
EAGAIN
、
ENOMEM
等,可尝试释放一些资源或等待一段时间后重试。
-
参数无效错误
:如
EINVAL
,检查传入的参数是否符合函数要求。
-
权限错误
:如
EPERM
,确保程序具有足够的权限执行相应操作。
以下是一个简单的错误处理示例:
#include <stdio.h>
#include <pthread.h>
int main() {
pthread_t thread;
pthread_attr_t attr;
int result;
// 初始化线程属性
result = pthread_attr_init(&attr);
if (result != 0) {
if (result == ENOMEM) {
fprintf(stderr, "Insufficient memory for thread attribute object\n");
} else {
fprintf(stderr, "Unknown error in pthread_attr_init: %d\n", result);
}
return 1;
}
// 创建线程
result = pthread_create(&thread, &attr, NULL, NULL);
if (result != 0) {
if (result == EAGAIN) {
fprintf(stderr, "Insufficient resources to create thread\n");
} else if (result == EINVAL) {
fprintf(stderr, "Invalid thread attributes\n");
} else {
fprintf(stderr, "Unknown error in pthread_create: %d\n", result);
}
pthread_attr_destroy(&attr);
return 1;
}
// 销毁线程属性
result = pthread_attr_destroy(&attr);
if (result != 0) {
if (result == EINVAL) {
fprintf(stderr, "Invalid thread attribute object\n");
} else {
fprintf(stderr, "Unknown error in pthread_attr_destroy: %d\n", result);
}
return 1;
}
return 0;
}
10. 总结
通过对嵌入式与实时应用中的 Linux 编程接口的详细介绍,我们了解了多个重要模块的功能和使用方法。
rtai_fifos
模块实现了内核空间与用户空间的通信,
rtai_shm
模块支持共享内存管理,
rtai_lxrt
模块让用户空间进程能透明使用 RTAI API,而 Posix 线程 API 则提供了多线程编程的能力。
在实际开发中,开发者需要根据具体需求选择合适的模块和函数,同时要注意错误处理,确保程序的稳定性和可靠性。合理运用这些编程接口,可以高效地开发出满足嵌入式与实时应用需求的程序。
graph LR;
A[开始] --> B[选择模块];
B --> C{内核与用户通信?};
C -- 是 --> D[使用 rtai_fifos];
C -- 否 --> E{共享内存需求?};
E -- 是 --> F[使用 rtai_shm];
E -- 否 --> G{多线程编程?};
G -- 是 --> H[使用 Posix 线程];
G -- 否 --> I[其他操作];
D --> J[编写代码];
F --> J;
H --> J;
I --> J;
J --> K[错误处理];
K --> L[调试与优化];
L --> M[结束];
这个流程图展示了在开发过程中选择合适模块的决策过程,以及后续的代码编写、错误处理和调试优化步骤。希望开发者能根据这个流程更好地运用这些编程接口,开发出高质量的嵌入式与实时应用程序。
超级会员免费看
358

被折叠的 条评论
为什么被折叠?



