Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1638608
  • 博文数量: 197
  • 博客积分: 10046
  • 博客等级: 上将
  • 技术积分: 1983
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-07 12:36
个人简介

在外企做服务器开发, 目前是项目经理, 管理两个server开发的项目。不做嵌入式好久了。

文章分类
文章存档

2011年(2)

2010年(6)

2009年(18)

2008年(30)

2007年(100)

2006年(41)

分类: LINUX

2007-08-31 18:10:22

Linux2.6 内核的新特性 (重点在RT方面)

http://www.rtems.net/Documnet/other/Linux2.6%20performence.htm

[ 本帖最后由 bobzhang 于 2007-8-23 12:48 编辑 ]

        顶部
查看 IP
超级版主
Rank: 8Rank: 8



UID 3
精华
积分 10
帖子 90
阅读权限 150
注册 2007-8-3
状态 离线
  
发表于 2007-8-24 14:13          添加 bobzhang 为MSN好友 通过MSN和 bobzhang 交谈
kernel内的0号进程和1(init)进程




查看积分策略说明
附件
2007-8-24 14:13
(43.45 KB)
 
        顶部
查看 IP
超级版主
Rank: 8Rank: 8



UID 3
精华
积分 10
帖子 90
阅读权限 150
注册 2007-8-3
状态 离线
  
发表于 2007-8-27 14:19          添加 bobzhang 为MSN好友 通过MSN和 bobzhang 交谈
看了一段调度的代码。

2.6 kernel的抢占 , 增加了调度点的功能, 是利用这个函数来实现的  


kernel/sched.c ==> int __sched cond_resched(void)





//重新调度是否合法
static inline int __resched_legal(int expected_preempt_count)
{
        if (unlikely(preempt_count() != expected_preempt_count))
                return 0;
        if (unlikely(system_state != SYSTEM_RUNNING))
                return 0;
        return 1;
}

static void __cond_resched(void)
{
#ifdef CONFIG_DEBUG_SPINLOCK_SLEEP
        __might_sleep(__FILE__, __LINE__);
#endif
        /*
         * The BKS might be reacquired before we have dropped
         * PREEMPT_ACTIVE, which could trigger a second
         * cond_resched() call.
         */
        do { //进行调度的时候,禁止本地中断
                local_irq_disable();
                add_preempt_count(PREEMPT_ACTIVE);  //增加值,禁止抢占
                __schedule();
        } while (need_resched());
        local_irq_enable();
}

int __sched cond_resched(void){
        if (need_resched() && __resched_legal(0)) {
                __cond_resched();
                return 1;
        }
        return 0;
}
EXPORT_SYMBOL(cond_resched);

[ 本帖最后由 bobzhang 于 2007-8-27 14:21 编辑 ]

        顶部
查看 IP
超级版主
Rank: 8Rank: 8



UID 3
精华
积分 10
帖子 90
阅读权限 150
注册 2007-8-3
状态 离线
  
发表于 2007-8-27 15:24          添加 bobzhang 为MSN好友 通过MSN和 bobzhang 交谈
对于RT ,要中断线程化

[中断线程化理论部分]:

二、中断线程化

中断线程化是实现Linux实时性的一个重要步骤,在Linux标准内核中,中断是最高优先级的执行单元,不管内核当时处理什么,只要有中断事件,
系统将立即响应该事件并执行相应的中断处理代码,除非当时中断关闭(即使用local_irq_disable失效了IRQ)。因此,如果系统有严重的网络或I/O负载,
中断将非常频繁,实时任务将很难有机会运行,也就是说,毫无实时性可言。中断线程化之后,中断将作为内核线程运行而且赋予不同的实时优先级,
实时任务可以有比中断线程更高的优先级,这样,实时任务就可以作为最高优先级的执行单元来运行,即使在严重负载下仍有实时性保证。


中断线程化的另一个重要原因是spinlock被mutex取代。中断处理代码中大量地使用了spinlock,当spinlock被mutex取代之后,
中断处理代码就有可能因为得不到锁而需要被挂到等待队列上,但是只有可调度的进程才可以这么做,如果中断处理代码仍然使用原来的spinlock,
则spinlock取代mutex的努力将大打折扣,因此为了满足这一要求,中断必须被线程化,包括IRQ和softirq。


【make menuconfig 】后, vi .config
会看到:

#
# Kernel Features
#

为了能并入主流内核,Ingo Molnar的实时补丁也采用了非常灵活的策略,它支持四种抢占模式:

1.No Forced Preemption (Server),这种模式等同于没有使能抢占选项的标准内核,主要适用于科学计算等服务器环境。

2.Voluntary Kernel Preemption (Desktop),这种模式使能了自愿抢占,但仍然失效抢占内核选项,它通过增加抢占点缩减了抢占延迟,因此适用于一些需要较好的响应性的环境,如桌面环境,当然这种好的响应性是以牺牲一些吞吐率为代价的。

3.Preemptible Kernel (Low-Latency Desktop),这种模式既包含了自愿抢占,又使能了可抢占内核选项,因此有很好的响应延迟,实际上在一定程度上已经达到了软实时性。它主要适用于桌面和一些嵌入式系统,但是吞吐率比模式2更低。

4.Complete Preemption (Real-Time),这种模式使能了所有实时功能,因此完全能够满足软实时需求,它适用于延迟要求为100微秒或稍低的实时系统。

实现实时是以牺牲系统的吞吐率为代价的,因此实时性越好,系统吞吐率就越低。


[4个选择]
CONFIG_PREEMPT_NONE=y
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT_DESKTOP is not set
# CONFIG_PREEMPT_RT is not set

【线程化与否】
CONFIG_PREEMPT_SOFTIRQS=y
CONFIG_PREEMPT_HARDIRQS=y


# CONFIG_PREEMPT_BKL is not set
# CONFIG_CLASSIC_RCU is not set
CONFIG_PREEMPT_RCU=y
CONFIG_RCU_TRACE=y
# CONFIG_NO_IDLE_HZ is not set
CONFIG_HZ=200




下面是中断线程化的代码,

kernel/irq/manage.c :




#ifdef CONFIG_PREEMPT_HARDIRQS
int hardirq_preemption = 1;

EXPORT_SYMBOL(hardirq_preemption);

/*
* Real-Time Preemption depends on hardirq threading:
*/
#ifndef CONFIG_PREEMPT_RT
static int __init hardirq_preempt_setup (char *str)
{
        if (!strncmp(str, "off", 3))
                hardirq_preemption = 0;
        else
                get_option(&str, &hardirq_preemption);
        if (!hardirq_preemption)
                printk("turning off hardirq preemption!\n");

        return 1;
}

__setup("hardirq-preempt=", hardirq_preempt_setup);

#endif

/*
* threaded simple handler
*/
static void thread_simple_irq(irq_desc_t *desc)
{
        struct irqaction *action = desc->action;
        unsigned int irq = desc - irq_desc;
        irqreturn_t action_ret;

        if (action && !desc->depth) {
                spin_unlock(&desc->lock);
                action_ret = handle_IRQ_event(irq, NULL, action);
                local_irq_enable();
                cond_resched_all();
                spin_lock_irq(&desc->lock);
                if (!noirqdebug)
                        note_interrupt(irq, desc, action_ret, NULL);
        }
        desc->status &= ~IRQ_INPROGRESS;
}

/*
* threaded level type irq handler
*/
static void thread_level_irq(irq_desc_t *desc)
{
        unsigned int irq = desc - irq_desc;

        thread_simple_irq(desc);
        if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
                desc->chip->unmask(irq);
}

/*
* threaded fasteoi type irq handler
*/
static void thread_fasteoi_irq(irq_desc_t *desc)
{
        unsigned int irq = desc - irq_desc;

        thread_simple_irq(desc);
        if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
                desc->chip->unmask(irq);
}

/*
* threaded edge type IRQ handler
*/
static void thread_edge_irq(irq_desc_t *desc)
{
        unsigned int irq = desc - irq_desc;

        do {
                struct irqaction *action = desc->action;
                irqreturn_t action_ret;

                if (unlikely(!action)) {
                        desc->status &= ~IRQ_INPROGRESS;
                        desc->chip->mask(irq);
                        return;
                }

                /*
                 * When another irq arrived while we were handling
                 * one, we could have masked the irq.
                 * Renable it, if it was not disabled in meantime.
                 */
                if (unlikely(((desc->status & (IRQ_PENDING | IRQ_MASKED)) ==
                            (IRQ_PENDING | IRQ_MASKED)) && !desc->depth))
                        desc->chip->unmask(irq);

                desc->status &= ~IRQ_PENDING;
                spin_unlock(&desc->lock);
                action_ret = handle_IRQ_event(irq, NULL, action);
                local_irq_enable();
                cond_resched_all();
                spin_lock_irq(&desc->lock);
                if (!noirqdebug)
                        note_interrupt(irq, desc, action_ret, NULL);
        } while ((desc->status & IRQ_PENDING) && !desc->depth);

        desc->status &= ~IRQ_INPROGRESS;
}

/*
* threaded edge type IRQ handler
*/
static void thread_do_irq(irq_desc_t *desc)
{
        unsigned int irq = desc - irq_desc;

        do {
                struct irqaction *action = desc->action;
                irqreturn_t action_ret;

                if (unlikely(!action)) {
                        desc->status &= ~IRQ_INPROGRESS;
                        desc->chip->disable(irq);
                        return;
                }

                desc->status &= ~IRQ_PENDING;
                spin_unlock(&desc->lock);
                action_ret = handle_IRQ_event(irq, NULL, action);
                local_irq_enable();
                cond_resched_all();
                spin_lock_irq(&desc->lock);
                if (!noirqdebug)
                        note_interrupt(irq, desc, action_ret, NULL);
        } while ((desc->status & IRQ_PENDING) && !desc->depth);

        desc->status &= ~IRQ_INPROGRESS;
        desc->chip->end(irq);
}

static void do_hardirq(struct irq_desc *desc)
{
        spin_lock_irq(&desc->lock);

        if (!(desc->status & IRQ_INPROGRESS))
                goto out;

        if (desc->handle_irq == handle_simple_irq)
                thread_simple_irq(desc);
        else if (desc->handle_irq == handle_level_irq)
                thread_level_irq(desc);
        else if (desc->handle_irq == handle_fasteoi_irq)
                thread_fasteoi_irq(desc);
        else if (desc->handle_irq == handle_edge_irq)
                thread_edge_irq(desc);
        else
                thread_do_irq(desc);
out:
        spin_unlock_irq(&desc->lock);

        if (waitqueue_active(&desc->wait_for_handler))
                wake_up(&desc->wait_for_handler);
}

extern asmlinkage void __do_softirq(void);

static int curr_irq_prio = 49;

static int do_irqd(void * __desc)
{
        struct sched_param param = { 0, };
        struct irq_desc *desc = __desc;
#ifdef CONFIG_SMP
        int irq = desc - irq_desc;
        cpumask_t mask;

        mask = cpumask_of_cpu(any_online_cpu(irq_desc[irq].affinity));
        set_cpus_allowed(current, mask);
#endif
        current->flags |= PF_NOFREEZE | PF_HARDIRQ;

        /*
         * Set irq thread priority to SCHED_FIFO/50:
         */
        param.sched_priority = MAX_USER_RT_PRIO/2;

        sys_sched_setscheduler(current->pid, SCHED_FIFO, ¶m);

        while (!kthread_should_stop()) {
                set_current_state(TASK_INTERRUPTIBLE);
                do_hardirq(desc);
                cond_resched_all();
                local_irq_disable();
                __do_softirq();
                local_irq_enable();
#ifdef CONFIG_SMP
                /*
                 * Did IRQ affinities change?
                 */
                if (!cpus_equal(current->cpus_allowed, irq_desc[irq].affinity))
                        set_cpus_allowed(current, irq_desc[irq].affinity);
#endif
                schedule();
        }
        __set_current_state(TASK_RUNNING);
        return 0;
}

static int ok_to_create_irq_threads;


//中断线程化
static int start_irq_thread(int irq, struct irq_desc *desc)
{
        if (desc->thread || !ok_to_create_irq_threads)
                return 0;

        desc->thread = kthread_create(do_irqd, desc, "IRQ %d", irq);
        if (!desc->thread) {
                printk(KERN_ERR "irqd: could not create IRQ thread %d!\n", irq);
                return -ENOMEM;
        }

        /*
         * An interrupt may have come in before the thread pointer was
         * stored in desc->thread; make sure the thread gets woken up in
         * such a case:
         */
        smp_mb();
        wake_up_process(desc->thread);

        return 0;
}

void __init init_hardirqs(void)
{
        int i;
        ok_to_create_irq_threads = 1;

        for (i = 0; i < NR_IRQS; i++) {
                irq_desc_t *desc = irq_desc + i;

                if (desc->action && !(desc->status & IRQ_NODELAY))
                        start_irq_thread(i, desc);
        }
}

#else

static int start_irq_thread(int irq, struct irq_desc *desc)
{
        return 0;
}

#endif

[ 本帖最后由 bobzhang 于 2007-8-27 15:30 编辑 ]

        顶部
查看 IP
超级版主
Rank: 8Rank: 8



UID 3
精华
积分 10
帖子 90
阅读权限 150
注册 2007-8-3
状态 离线
  
发表于 2007-8-28 16:18          添加 bobzhang 为MSN好友 通过MSN和 bobzhang 交谈
spinlock转换成mutex

>>spinlock是一个高效的共享资源同步机制,在SMP(对称多处理器Symmetric Multiple Proocessors)的情况下,它用于保护共享资源,
>>如全局的数据结构或一个只能独占的硬件资源。但是spinlock保持期间将使抢占失效(注意仅仅是抢占失效, 可没有说是中断失效(spinlock_irq()是中断失效的)),
>>用spinlock保护的区域称为临界区(Critical Section),
>> 在内核中大量地使用了spinlock,有大量的临界区存在,因此它们将严重地影响着系统的实时性。


关于spinlock和信号量(semaphore)的具体讨论,可以参阅:


的讨论。

一句话, 标准的内核中,spinlock适合临界区代码很短的情况下使用。最重要的一点,在标准内核中,
中断处理函数不能使用spinlock(当然外面的某个地方使用了spinlock)


spinlock 被mutex 替换的学习笔记,自己的注释 。


>> Ingo Molnar的实时补丁使用mutex来替换spinlock,它的意图是让spinlock可抢占,但是可抢占后将产生很多后续影响。
标准内核中, spinlock是不能被抢占的(disable preempt),  除了 _irq()版本外, 是能被中断打断的,
信号量可以被抢占。


Spinlock失效抢占的目的是避免死锁。

Spinlock如果可抢占了,一个spinlock的竞争者将可能抢占该spinlock的保持者来运行,但是由于得不到spinlock将自旋在那里,
如果竞争者的优先级高于保持者的优先级,将形成一种死锁的局面,因为保持者无法得到运行而永远不能释放spinlock,
而竞争者由于不能得到一个不可能释放的spinlock而永远自旋在那里。[/
color]


由于中断处理函数也可以使用spinlock,如果它使用的spinlock已经被一个进程保持,中断处理函数将无法继续进行,从而形成死锁,
这样的spinlock在使用时应当中断失效来避免这种死锁的情况发生。标准linux内核就是这么做的。

>>> 中断线程化之后,中断失效就没有必要,因为遇到这种状况后,中断线程将挂在等待队列上并放弃CPU让别的线程或进程来运行。
设计的太好了。 中断获得不了锁,就睡眠呗 , 不会一直傻在那里自旋了。 [bob] 这还是自旋锁吗?


等待队列就是解决这种死锁僵局的方法,在Ingo Molnar的实时补丁中,每个spinlock都有一个等待队列,该等待队列是按进程或线程的优先级排队的。如果一个进程或线程竞争的spinlock已经被另一个线程保持,它将把自己挂在该spinlock的优先级化的等待队列上,然后发生调度把CPU让给别的进程或线程。

>>需要特别注意,对于非线程化的中断,必须使用原来的spinlock,原因前面已经讲得很清楚。
原来的spinlock 是指哪个呢?
现在看起来, spin_lock_xx() 版本要不调用__spin_lock_xx() 要不调用_spin_lock_xx()版本(根据参数的spinlock_t的类型)。
类似这样:
// #define spin_lock_irq(lock)        _spin_lock_irq(lock)
// #define spin_lock_bh(lock)        _spin_lock_bh(lock)
#define spin_lock_irq(lock)        PICK_OP(_lock_irq, lock)
#define spin_lock_bh(lock)        PICK_OP(_lock_bh, lock)



+++++++++++++++++++++++++++++++++++++++++++++++

[bob]
对于标准的内核来说, 中断处理函数肯定是不能睡眠的, 如果要是睡了, 谁来唤醒它呢? 最后的结果就是kernel 死在那里了。
但是现在Ingo 的patch ,已经把spinlock 用mutex替代了, 那么中断处理函数就可能睡眠了, 那么kernel就要作相应的处理了。
怎么处理呢? 就是中断线程化了。

那中断线程化到底是什么意思呢? 还得仔细研究下先。
先研究什么是kernel thread 。





[ 本帖最后由 bobzhang 于 2007-8-31 15:59 编辑 ]

        顶部
查看 IP
超级版主
Rank: 8Rank: 8



UID 3
精华
积分 10
帖子 90
阅读权限 150
注册 2007-8-3
状态 离线
  
发表于 2007-8-30 16:29          添加 bobzhang 为MSN好友 通过MSN和 bobzhang 交谈
在分析 linux-2.6.18_pro500关于实时实现方面的代码。  

现在看起来 ,

抢占的实现:

#define preempt_disable() \
do { \
        inc_preempt_count(); \
        barrier(); \
} while (0)

--

#define inc_preempt_count() add_preempt_count(1)

--
#if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_CRITICAL_TIMING)
  extern void notrace add_preempt_count(unsigned int val);
  extern void notrace sub_preempt_count(unsigned int val);
  extern void notrace mask_preempt_count(unsigned int mask);
  extern void notrace unmask_preempt_count(unsigned int mask);
#else
# define add_preempt_count(val)        do { preempt_count() += (val); } while (0)
# define sub_preempt_count(val)        do { preempt_count() -= (val); } while (0)
# define mask_preempt_count(mask) \
                do { preempt_count() |= (mask); } while (0)
# define unmask_preempt_count(mask) \
                do { preempt_count() &= ~(mask); } while (0)
#endif

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
禁止抢占很容易, 就是增加 preempt_count()的值即可。
阅读(3071) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~