3.3 实时任务
实时任务是一个用户定义的程序,它按照在内核控制下的特定的调度方式来执行。
最开始的设计是给每一个实时任务有自己的地址空间来提供内存保护。这通过80x86处理器内置的分页机制[10]。在每次上下文切换中,页目录是基于寄存器的变化来指向新任务的页目录。
任务间的切换非常频繁,如果在TLB没有命中时,使得系统在上下文切换的开销很大,系统性能会降低。别的系统开销还有是系统的调用,在保护模式下也是个费时的操作。
一种提高性能的方法是所有的实时任务运行在一个地址空间。通过使用内核地址空间,除去了保护模式变换的系统开销。Linux一个很有用的特性是:可装载内核模块。内核模块可以动态连接到内核地址空间,和链接为内核代码。每个模块定义了两个例程:init_module()和cleanup_module()。init_module()在模块装载到内核是调用,cleanup_module()在删除模块时调用。这就提供了一个简单的方法在Linux中操作驱动程序和文件系统。
可链接模块用以在当前的RTLinux中动态创建实时任务。这种实现方法也更脆弱:一个实时任务的错误可能引起整个系统的崩溃。C语言的使用加重了这个问题。数组、指针等的应用,很容易引起与内存相关的程序错误。另一方面,由于实时任务一般控制昂贵的外围设备,理所当然要使用与系统内核编程时相同的警告级别。
实时任务运行在内核地址空间有几个好处。除了上面提到的TLB命中问题和保护模式切换的问题外,这种方法使我们通过名字引用函数和对象,胜于通过描述符来引用。比如,实时任务表现为一个C的结构体。每个任务可以赋予一个C标识符,别的任务也可以通过这个标识符引用任务。动态链接执行过程中,模块装载解决了符号寻址问题,所以访问是非常高效的。
所有的任务在系统的地址空间,任务的切换也更简单。一个上下文切换是保存所有整数寄存器到栈中,改变栈的指针指向新的任务。同样也支持有浮点运算的任务。
为实时任务进行实时编程的接口将在第四章介绍。
3.3.1 实时线程数据结构
struct rtl_thread_struct
struct rtl_thread_struct {
int *stack;
int fpu_initialized;
RTL_FPU_CONTEXT fpu_regs;
int uses_fp;
int *kmalloc_stack_bottom;
struct rtl_sched_param sched_param; /* 线程调度参数 */
struct rtl_thread_struct *next; /* 链表中下一个线程 */
int cpu; /* 线程的CPU号 */
hrtime_t resume_time; /* 恢复时间 */
hrtime_t period; /* 任务周期 */
hrtime_t timeval;
struct module *creator; /* 线程创建者 */
void (*abort)(void *);
void *abortdata;
int threadflags;
rtl_sigset_t pending;
rtl_sigset_t blocked;
void *user[4];
int errno_val;
struct rtl_cleanup_struct *cleanup;
int magic;
struct rtl_posix_thread_struct posix_data;
void *tsd [RTL_PTHREAD_KEYS_MAX];
};
程序3.2 rtl_thread_struct结构
3.3.2 创建线程和线程调度
一个实时程序使用一个或几个线程来执行。线程是轻量级进程,它们共享公共的地址空间。在RTLinux中,所有的线程共享Linux内核地址空间。
线程操作相关的函数将在第四章介绍。
[1] [2] [3] [4] 下一页 没有相关教程
|