嵌入式Linux下的实时性增强方案
在中断处理阶段当中断发生时,CPU调用do_IRQ( )函数来处理中断,do_IRQ( )在做了必要的相关处理之后调用_do_IRQ( )。_do_IRQ( )主要功能为判断该中断是否已经被线程化(核对终端描述符的状态字段是否包含IRQ_NODELAY标志),对于没有线程化的中断,将直接调用 handle_IRQ_event( )函数来处理。功能实现等同于如下代码:
fastcall notrace unsigned int __do_IRQ(unsigned int irq,
struct pt_regs *regs)
{ ……
if (redirect_hardirq(desc))
//检测是否为线程化中断,若是则唤醒中断线程
goto out_no_end;
……
action_ret = handle_IRQ_event(irq, regs, action);
//处理非线程化中断
……
}
int redirect_hardirq(struct irq_desc *desc)
//检测irq_desc结构体,判断是否线程化
{ ……
if (!hardirq_preemption || (desc->status IRQ_
NODELAY) || !desc->thread)
return 0;
……
if (desc->thread desc->thread->state != TASK_
RUNNING)
wake_up_process(desc->thread);
……
}
针对已线程化的情况,调用wake_up_process( )函数唤醒中断处理线程执行,内核线程将调用do_hardirq( )来处理相应的中断。具体实现是通过handle_IRQ_event( )函数直接调用相应的中断处理函数完成的。对于紧急的中断(如时钟中断),内核保持原来的中断处理方式,而不为其创建中断线程,这样就保证了紧急中断的快速响应。
2.2 内核可抢占性设计
在Linux标准内核中,因不具有可抢占性和导致较大的延迟,增加内核的可抢占性能,可提高系统的实时任务处理能力。当前修改Linux内核提高实时性的方法主要有增加抢占点和改造成抢占式内核两种方法。增加抢占点方法是在内核中插入抢占点,通过检测抢占点调度标志来决定是否进行实时任务的调度。采用这种方法,在检测抢占点标志时大大增加了系统开销,因此本方案采用直接改造Linux内核的方法,通过修改自旋锁为互斥锁来提高内核的可抢占性 [5]。即借鉴Ingo Molnar的实时补丁的实时化方法,使用mutex互斥锁来替换spinlock自旋锁。使用mutex替换spinlock,可以让spinlock 可抢占。起初spinlock不可抢占性设计目的是避免死锁,可抢占性设计可能导致竞争者与保持者的死锁局面。中断处理函数中也可以使用 spinlock,如果spinlock已经被某一进程保持,则中断处理函数无法进行,从而形成死锁。中断线程化以后,中断线程将挂在等待队列上并放弃 CPU让别的线程或进程来运行,让每个spinlock都有一个等待队列,该等待队列按进程或线程优先级排队,如果一个进程或线程竞争的spinlock 已经被另一个线程保持,它将把自己挂在该spinlock的优先级化的等待队列上,然后发生调度把CPU让给别的进程或线程。mutex替换 spinlock后,spinlock结构定义如下代码:
typedef struct {
struct rt_mutex lock; //新的实时互斥锁
unsigned int break_lock;
} spinlock_t;
其中struct rt_mutex结构如下:
struct rt_mutex {
raw_spinlock_t wait_lock;
struct plist wait_list; //优先级等待队列
struct task_struct *owner; //拥有该锁进程的信息
int owner_prio;
… …
};
在如上代码中,类型raw_spinlock_t就是原来的spinlock_t。即代码中的spinlock_t就是新设计的自旋锁。 rt_mutex结构中,wait_list字段为优先级等待队列。在mutex使用中,当遇到锁住的临界资源时,任务被挂起到wait_list中,临界资源解锁时等待任务被激活。临界资源被保护的同时可以抢占。
linux操作系统文章专题:linux操作系统详解(linux不再难懂)
评论