我是靠谱客的博主 会撒娇方盒,这篇文章主要介绍linux mips 运行,linux-mips启动分析,现在分享给大家,希望可以做个参考。

********************************************

蛐蛐

http://qgjie456.blog.163.com/

MSN:qgjie@hotmail.com

本文适用于

linux-2.6.22.8

V 0.1

欢迎转载,但请保留作者信息

********************************************

在 star_kernel() 函数中调用  init_timers() 函数。

这个函数完成如下功能:

1)初始化本 CPU 上的定时器(timer)相关的数据结构

2)向   cpu_chain 通知链注册元素 timers_nb,该元素的回调函数用于初始化指定 CPU 上的定时器相关的数据结构。

3) 初始化时钟的软中断处理函数

参考《Linux内部的时钟处理机制全面剖析》。

参考《深入理解 linux 内核》。

参考《linux通知链表机制》。

==========================================================================

void __init init_timers(void)

{

初始当前  CPU 的定时器链表。

int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,

(void *)(long)smp_processor_id());

如果没有定义  CONFIG_TIMER_STATS 这个宏,则这个   init_timer_stats() 函数为空函数。

init_timer_stats();

BUG_ON(err == NOTIFY_BAD);

在  CPU 管理的通知链表   cpu_chain 注册通知结构  timers_nb。

register_cpu_notifier(&timers_nb);

注册时钟软中断  TIMER_SOFTIRQ,它的处理函数为   run_timer_softirq()。

这个处理函数如下所示。

open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);

}

********************************************

这个函数是 注册到  CPU 子系统的通知链表的回调函数。

这个函数主要是在新的 CPU 开始工作或者停止工作(支持热插拔)时,通知  timer 管理器,

使    timer 管理器管理器知道,并进行相应的处理。

----------------------------------------

static int __cpuinit timer_cpu_notify(struct notifier_block *self,

unsigned long action, void *hcpu)

{

long cpu = (long)hcpu;

switch(action) {

case CPU_UP_PREPARE:

case CPU_UP_PREPARE_FROZEN:

if (init_timers_cpu(cpu) < 0)

return NOTIFY_BAD;

break;

#ifdef CONFIG_HOTPLUG_CPU

case CPU_DEAD:

case CPU_DEAD_FROZEN:

migrate_timers(cpu);

break;

#endif

default:

break;

}

return NOTIFY_OK;

}

********************************************

注册到通知链表的回调函数支持两类事件(  UP 和   DEAD 类型的事件),

下面为  支持   CPU_UP_PREPARE  和   CPU_UP_PREPARE_FROZEN 类型事件的函数。

----------------------------------------

static int __devinit init_timers_cpu(int cpu)

{

int j;

tvec_base_t *base;

定义静态局部变量    tvec_base_done[] 数组,表示是否已经初始化了由索引号表示的  CPU。

static char __devinitdata tvec_base_done[NR_CPUS];

检测  CPU 的初始化是否已经做过,如果没有做过,继续,如果已经做过,则跳过。

if (!tvec_base_done[cpu]) {

定义静态全局变量,表示是否是第一个启动的  CPU 的初始化。

static char boot_done;

如果不是启动  CPU,则走这个路径。

if (boot_done) {

为每个  CPU 分配   struct tvec_t_base_s 结构的空间。

base = kmalloc_node(sizeof(*base), GFP_KERNEL,

cpu_to_node(cpu));

if (!base)

return -ENOMEM;

if (tbase_get_deferrable(base)) {

WARN_ON(1);

kfree(base);

return -ENOMEM;

}

把这个结构体空间清  0。

memset(base, 0, sizeof(*base));

设置  per_cpu 变量中的指针指向分配的空间。

per_cpu(tvec_bases, cpu) = base;

} else {

这个路径是在  boot 阶段,启动的第一个  CPU 的路径。

设置标志已经启动过了。

由于在启动阶段   per_cpu 数据没有准备好,所以使用静态定义的结构。

这是应为  per_cpu 数据是一个指针,尽管  per_cpu 中为指针预留了空间,

但是指针指向的空间,没有进行分配,都是执行  boot_tvec_bases 的。

boot_done = 1;

base = &boot_tvec_bases;

}

设置  CPU 已经设置过了。

tvec_base_done[cpu] = 1;

} else {

base = per_cpu(tvec_bases, cpu);

}

spin_lock_init(&base->lock);

lockdep_set_class(&base->lock, base_lock_keys + cpu);

初始化  tvec_t_base_s 结构中每个链表的头节点。

for (j = 0; j < TVN_SIZE; j++) {

INIT_LIST_HEAD(base->tv5.vec + j);

INIT_LIST_HEAD(base->tv4.vec + j);

INIT_LIST_HEAD(base->tv3.vec + j);

INIT_LIST_HEAD(base->tv2.vec + j);

}

for (j = 0; j < TVR_SIZE; j++)

INIT_LIST_HEAD(base->tv1.vec + j);

base->timer_jiffies = jiffies;

return 0;

}

----------------------------------------

在这个   init_timers_cpu() 函数中,使用了全局变量   boot_tvec_bases。

它的定义如下所示:

typedef struct tvec_s {

struct list_head vec[TVN_SIZE];

} tvec_t;

typedef struct tvec_root_s {

struct list_head vec[TVR_SIZE];

} tvec_root_t;

struct tvec_t_base_s {

spinlock_t lock;

struct timer_list *running_timer;

unsigned long timer_jiffies;

tvec_root_t tv1;

tvec_t tv2;

tvec_t tv3;

tvec_t tv4;

tvec_t tv5;

} ____cacheline_aligned;

typedef struct tvec_t_base_s tvec_base_t;

tvec_base_t boot_tvec_bases;

----------------------------------------

lock                 spinlock_t                 用于同步操作

----------------------------------------

running_timer     struct timer_list *     正在处理的定时器

----------------------------------------

timer_jiffies     unsigned long             当前正在处理的定时器到期时间

----------------------------------------

tv1              struct tvec_root         保存了到期时间从 timer_jiffies 到(2^8 -1)

之间(包括边缘值)的所有定时器

----------------------------------------

tv2              struct tvec             保存了到期时间从 timer_jiffies +(2^8)到

timer_jiffies + (2^14-1)之间(包括边缘值)的 所有定时器

----------------------------------------

tv3              struct tvec             保存了到期时间从 timer_jiffies +(2^14)到

timer_jiffies +(2^20-1)之间(包括边缘值)的所有定时器

----------------------------------------

tv4              struct tvec             保存了到期时间从 timer_jiffies +(2^20)到

timer_jiffies + (2^26-1)之间(包括边缘值)的所有定时器

----------------------------------------

tv5              struct tvec             保存了到期时间从 timer_jiffies +(2^16)到

timer_jiffies +(2^32-1)之间(包括边缘值)的所有定时器

----------------------------------------

********************************************

下面为  支持    CPU_DEAD  和  CPU_DEAD_FROZEN 类型事件的函数。

当没有配置这个宏   CONFIG_HOTPLUG_CPU  定义时,即系统不支持  CPU 热插拔时,

这个  migrate_timers() 为空函数;

当配置了这个宏定义时,这个函数如下所示。

----------------------------------------

static void __devinit migrate_timers(int cpu)

{

tvec_base_t *old_base;

tvec_base_t *new_base;

int i;

首先检查这个 CPU 是否在工作,如果仍然在工作,则发出警告信息。

BUG_ON(cpu_online(cpu));

这个 tvec_bases 是一个  per_cpu 变量,是   tvec_base_t 的指针。

这个  per_cpu() 函数取得这个 cpu 的私有变量,即tvec_base_t 的指针

old_base = per_cpu(tvec_bases, cpu);

这个  get_cpu_var()函数取得当前 cpu 的tvec_base_t 的指针 。

new_base = get_cpu_var(tvec_bases);

禁止当前 cpu 的中断,同样也禁止了内核抢占。

local_irq_disable();

根据需求锁定这两个自旋锁 new_base->lock, old_base->lock 。

double_spin_lock(&new_base->lock, &old_base->lock, smp_processor_id() < cpu);

如果参数  cpu 上有正在发生的定时器,则发出 bug 信息。

BUG_ON(old_base->running_timer);

把参数 cpu 上注册的 tv1 定时器链表上的定时器迁移到当前 cpu 的定时器链表上。

for (i = 0; i < TVR_SIZE; i++)

migrate_timer_list(new_base, old_base->tv1.vec + i);

把参数 cpu 上注册的 tv2、 tv3、tv4、tv5 定时器链表上的定时器迁移到当前 cpu 的定时器链表上。

for (i = 0; i < TVN_SIZE; i++) {

migrate_timer_list(new_base, old_base->tv2.vec + i);

migrate_timer_list(new_base, old_base->tv3.vec + i);

migrate_timer_list(new_base, old_base->tv4.vec + i);

migrate_timer_list(new_base, old_base->tv5.vec + i);

}

根据需求为这两个自旋锁 new_base->lock, old_base->lock 解锁。

double_spin_unlock(&new_base->lock, &old_base->lock, smp_processor_id() < cpu);

恢复本地 cpu 的中断。

local_irq_enable();

put_cpu_var(tvec_bases);

}

----------------------------------------

static void migrate_timer_list(tvec_base_t *new_base, struct list_head *head)

{

struct timer_list *timer;

如果定时器链表为 空链表,则直接退出。

while (!list_empty(head)) {

timer = list_first_entry(head, struct timer_list, entry);

detach_timer(timer, 0);

timer_set_base(timer, new_base);

internal_add_timer(new_base, timer);

}

}

********************************************

static inline void double_spin_lock(spinlock_t *l1, spinlock_t *l2,

bool l1_first)

__acquires(l1)

__acquires(l2)

{

使用标志 l1_first 判断先锁定这两个中的那个锁,以防止发生死锁现象。

if (l1_first) {

spin_lock(l1);

spin_lock(l2);

} else {

spin_lock(l2);

spin_lock(l1);

}

}

----------------------------------------

# define __acquires(x)  __attribute__((context(x,0,1)))

********************************************

如果没没有定义这个   CONFIG_TIMER_STATS 宏,这个  init_timer_stats() 函数就是个空函数。

这个      CONFIG_TIMER_STATS 宏的作用是是否在  /proc 文件系统中生成  timer_stats 文件,

这个文件允许你查看Linux内核里使用定时器的常规事件一些信息。

通过查看这个文件,你可以看到那些常规事件使用定时器的次数最多,使用的频率是多少。

更详细的信息可以参考内核源码树下面的  Documentation/filesystems/proc.txt 文件。

如果定义了如下所示:

这个  init_timer_stats() 函数对于每个  CPU 来说初始化了 timer_stat 的自旋锁。

----------------------------------------

void __init init_timer_stats(void)

{

int cpu;

for_each_possible_cpu(cpu)

spin_lock_init(&per_cpu(lookup_lock, cpu));

}

********************************************

在  CPU 管理的通知链表   cpu_chain 注册通知结构  timers_nb。

static struct notifier_block __cpuinitdata timers_nb = {

.notifier_call  = timer_cpu_notify,

};

这个通知结构的回调函数为   timer_cpu_notify() ,在上面也讲解过。

-----------------------------------------

这个   Raw 类型的通知链表   cpu_chain 是在  kernel/cpu.c 文件中定义的。

static __cpuinitdata RAW_NOTIFIER_HEAD(cpu_chain);

int __cpuinit register_cpu_notifier(struct notifier_block *nb)

{

int ret;

锁定   cpu_add_remove_lock 互斥锁。

mutex_lock(&cpu_add_remove_lock);

在   Raw 类型的通知链表   cpu_chain 上注册通知结构   nb。

ret = raw_notifier_chain_register(&cpu_chain, nb);

释放   cpu_add_remove_lock 互斥锁。

mutex_unlock(&cpu_add_remove_lock);

return ret;

}

********************************************

这个函数   run_timer_softirq() 是 TIMER_SOFTIRQ 的处理函数。

这个函数对当前  CPU 到期的定时器进行处理。

参考《深入理解 linux 内核》第六章。

-----------------------------------------

static void run_timer_softirq(struct softirq_action *h)

{

首先获得到本地 CPU 的定时器链表的   base 地址。

tvec_base_t *base = __get_cpu_var(tvec_bases);

这个函数和高精度时钟定时器有关,检测高精度时钟定时器是否  active,如果是  active 的就替换到

hres tick机制。参考《linux高精度时钟分析》。

hrtimer_run_queues();

检测如果 jiffies大于等于 timer_jiffies ,说明可能已经有软件时钟到期了,

此时就要进行软件时钟的处理,调用函数 __run_timers() 函数 进行处理。

如果 jiffies 小于 timer_jiffies ,表明没有软件时钟到期,则不用对软件时钟进行处理。函数返回。

if (time_after_eq(jiffies, base->timer_jiffies))

__run_timers(base);

}

********************************************

在 TIMER_SOFTIRQ 软中断的处理函数调用    __run_timers() 对到期的定时器进行处理。

-----------------------------------------

static inline void __run_timers(tvec_base_t *base)

{

struct timer_list *timer;

spin_lock_irq(&base->lock);

while (time_after_eq(jiffies, base->timer_jiffies)) {

struct list_head work_list;

struct list_head *head = &work_list;

int index = base->timer_jiffies & TVR_MASK;

if (!index &&

(!cascade(base, &base->tv2, INDEX(0))) &&

(!cascade(base, &base->tv3, INDEX(1))) &&

!cascade(base, &base->tv4, INDEX(2)))

cascade(base, &base->tv5, INDEX(3));

++base->timer_jiffies;

list_replace_init(base->tv1.vec + index, &work_list);

while (!list_empty(head)) {

void (*fn)(unsigned long);

unsigned long data;

timer = list_first_entry(head, struct timer_list,entry);

fn = timer->function;

data = timer->data;

timer_stats_account_timer(timer);

set_running_timer(base, timer);

detach_timer(timer, 1);

spin_unlock_irq(&base->lock);

{

int preempt_count = preempt_count();

fn(data);

if (preempt_count != preempt_count()) {

printk(KERN_WARNING "huh, entered %p "

"with preempt_count %08x, exited"

" with %08x?n",

fn, preempt_count,

preempt_count());

BUG();

}

}

spin_lock_irq(&base->lock);

}

}

set_running_timer(base, NULL);

spin_unlock_irq(&base->lock);

}

-----------------------------------------

1.  获得 base 的同步锁

2.  如果 jiffies 大于等于 timer_jiffies (当前正要处理的软件时钟的到期时间,

说明可能有软件时钟到期了),就一直运行3~7,否则跳转至8

3. 计算得到 tv1 的索引,该索引指明当前到期的软件时钟所在 tv1 中的链表(结构参见3.2节),代码:

int index = base->timer_jiffies & TVR_MASK;

1.  调用 cascade 函数对软件时钟进行必要的调整(稍后会介绍调整的过程)

2.  使得 timer_jiffies 的数值增加1

3.  取出相应的软件时钟链表

4.  遍历该链表,对每个元素进行如下操作

*  设置当前软件时钟为 base 中正在运行的软件时钟(即保存当前软件时钟到 base-> running_timer 成员中)

*  将当前软件时钟从链表中删除,即卸载该软件时钟

*  释放锁,执行软件时钟处理程序

*  再次获得锁

1.  设置当前 base 中不存在正在运行的软件时钟

2.  释放锁

********************************************

问题:

1)在   init_timers_cpu() 函数中,为什么不直接使用   per_cpu() 函数定义的静态数组

而是动态分配呢?

在源码注释中,解释由于内存分配器没有工作,所以启动时使用静态分配的,但是在  RCU_init() 函数

中已经使用了  per_cpu()  函数了?

其实已经在     setup_per_cpu_areas() 函数为  per_cpu 数据分配了空间。

这是应为  per_cpu 数据是一个指针,尽管  per_cpu 中为指针预留了空间,

但是指针指向的空间,没有进行分配,都是执行  boot_tvec_bases 的。

参考《每CPU变量的数据组织和访问》。

2)这个   __acquires(x) 宏定义的意义?

3)与  timer_stat 相关的代码没有看?

最后

以上就是会撒娇方盒最近收集整理的关于linux mips 运行,linux-mips启动分析的全部内容,更多相关linux内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部