当前位置:文档之家› Linux内核调试技术——进程上下文R状态死锁监测

Linux内核调试技术——进程上下文R状态死锁监测

Linux内核调试技术——进程上下文R状态死锁监测
Linux内核调试技术——进程上下文R状态死锁监测

Linux内核调试技术——进程上下文R

状态死锁监测

一、lockup detector机制分析

lockup detector机制在内核代码的kernel/watchdog.c中实现,本文以Linux 4.1.15版本源码为例进行分析。首先了解其背后的设计原理:利用进程上下文、中断、nmi中断的不同优先级实现死锁监测。它们3者的优先级关系为“进程上下文< 中断< nmi中断”,其中进程上下文优先级最低,可通过中断来进行监测进程的运行状态,nmi中断的优先级最高,它是一种不可屏蔽的中断,在中断上下文中发生死锁时,nmi中断处理也可正常进入,因此可用来监测中断中的死锁。不过可惜的是目前绝大多数的arm32芯片都不支持nmi中断,也包括我手中树莓派的bcm2835芯片。从程序的命名中就可以看出,该程序其实实现了一种软看门狗的功能,下面给出整体的软件流程框图:

该程序为每个cpu创建了一个进程和一个高精度定时器,其中进程用来喂狗,定时器用来唤醒喂狗进程和检测是否存在死锁进程,在检测到死锁进程后就触发报警,接下来详细分析源代码:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

void __init lockup_detector_init(void)

{

set_sample_period();

if (watchdog_enabled)

watchdog_enable_all_cpus();

}

首先入口函数lockup_detector_init(),该函数会在内核启动流程中按如下路径调用:start_kernel() --> rest_init() --> kernel_init()(启内核线程)--> kernel_init_freeable() --> lockup_detector_init()。该函数首先计算高精度定时器的到期时间(即喂狗时间),该值为监测超时时间值的1/5,默认为4s(20s/5),然后判断开关标识来确定是否启用监测机制,该标识在没有启用hard lockup detect的情况下默认为SOFT_WATCHDOG_ENABLED,表示开

启soft lockup detect。于此同时内核也提供了如下的__setup接口,可从内核启动参数cmd line 中设置值和开关:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static int __init softlockup_panic_setup(char *str)

{

softlockup_panic = simple_strtoul(str, NULL, 0);

return 1;

}

__setup("softlockup_panic=", softlockup_panic_setup);

static int __init nowatchdog_setup(char *str)

{

watchdog_enabled = 0;

return 1;

}

__setup("nowatchdog", nowatchdog_setup);

static int __init nosoftlockup_setup(char *str)

{

watchdog_enabled &= ~SOFT_WATCHDOG_ENABLED;

return 1;

}

__setup("nosoftlockup", nosoftlockup_setup);

此处假定开启soft lockup detect,接下来调用watchdog_enable_all_cpus()函数,该函数会尝试为每个CPU创建一个喂狗任务(并不会立即启动主函数执行):

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static int watchdog_enable_all_cpus(void)

{

int err = 0;

if (!watchdog_running) {

err = smpboot_register_percpu_thread(&watchdog_threads);

if (err)

pr_err("Failed to create watchdog threads, disabled\n");

else

watchdog_running = 1;

} else {

/*

* Enable/disable the lockup detectors or

* change the sample period 'on the fly'.

*/

update_watchdog_all_cpus();

}

return err;

}

该函数首先判断是否已经启动了任务,若没有则调用smpboot_register_percpu_thread()函数来创建任务,否则则调用update_watchdog_all_cpus()函数来更新定时器的到期时间。首先分析前一个分支,看一下watchdog_threads结构体的实现:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static struct smp_hotplug_thread watchdog_threads = {

.store = &softlockup_watchdog,

.thread_should_run = watchdog_should_run,

.thread_fn = watchdog,

.thread_comm = "watchdog/%u",

.setup = watchdog_enable,

.cleanup = watchdog_cleanup,

.park = watchdog_disable,

.unpark = watchdog_enable,

};

该结构注册了许多的回调函数,先简单了解一下:(1)softlockup_watchdog是一个全局的per cpu指针,它用来保存创建任务的进程描述符task_struct结构;(2)watchdog_should_run()是任务运行的判断函数,它会判断进程是否需要调用thread_fn指针指向的函数运行;(3)watchdog()是任务运行的主函数,该函数实现线程喂狗的动作;(4)setup回调函数watchdog_enable会在任务首次启动时调用,该函数会创建高精度定时器,用来激活喂狗任务和监测死锁超时;(5)cleanup回调函数用来清除任务,它会关闭定时器;(6)最后的park 和unpark回调函数用于暂停运行和恢复运行任务。(7)thread_comm是任务名字,cpu0是watchdog/0,cpu1是watchdog/1,以此类推。

下面来简单看一下smpboot_register_percpu_thread()函数是如何为每个cpu创建任务的,同时又在何处调用上述的那些回调函数的(kernel/smpboot.c):

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread)

{

unsigned int cpu;

int ret = 0;

get_online_cpus();

mutex_lock(&smpboot_threads_lock);

for_each_online_cpu(cpu) {

ret = __smpboot_create_thread(plug_thread, cpu);

if (ret) {

smpboot_destroy_threads(plug_thread);

goto out;

}

smpboot_unpark_thread(plug_thread, cpu);

}

list_add(&plug_thread->list, &hotplug_threads);

out:

mutex_unlock(&smpboot_threads_lock);

put_online_cpus();

return ret;

}

EXPORT_SYMBOL_GPL(smpboot_register_percpu_thread);

函数遍历所有的online cpu然后为其创建指定的任务,然后将他们添加到hotplug_threads中去(该链表是用来遍历用的);

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static int

__smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)

{

struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);

......

tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu,

ht->thread_comm);

......

return 0;

}

可以看出,为每个cpu创建的任务并不是直接调用前文中注册的thread_fn()回调函数,而是调用了smpboot_thread_fn()函数,该函数会维护任务运行的几个状态,视状态的不同调用不同的注册回调处理函数:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static int smpboot_thread_fn(void *data)

{

struct smpboot_thread_data *td = data;

struct smp_hotplug_thread *ht = td->ht;

while (1) {

set_current_state(TASK_INTERRUPTIBLE);

preempt_disable();

if (kthread_should_stop()) {

__set_current_state(TASK_RUNNING);

preempt_enable();

if (ht->cleanup)

ht->cleanup(td->cpu, cpu_online(td->cpu));

kfree(td);

return 0;

}

if (kthread_should_park()) {

__set_current_state(TASK_RUNNING);

preempt_enable();

if (ht->park && td->status == HP_THREAD_ACTIVE) {

BUG_ON(td->cpu != smp_processor_id());

ht->park(td->cpu);

td->status = HP_THREAD_PARKED;

}

kthread_parkme();

/* We might have been woken for stop */

continue;

}

BUG_ON(td->cpu != smp_processor_id());

/* Check for state change setup */

switch (td->status) {

case HP_THREAD_NONE:

__set_current_state(TASK_RUNNING);

preempt_enable();

if (ht->setup)

ht->setup(td->cpu);

td->status = HP_THREAD_ACTIVE;

continue;

case HP_THREAD_PARKED:

__set_current_state(TASK_RUNNING);

preempt_enable();

if (ht->unpark)

ht->unpark(td->cpu);

td->status = HP_THREAD_ACTIVE;

continue;

}

if (!ht->thread_should_run(td->cpu)) {

preempt_enable_no_resched();

schedule();

} else {

__set_current_state(TASK_RUNNING);

preempt_enable();

ht->thread_fn(td->cpu);

}

}

}

这个函数是一个大循环,在每次循环中都会首先依次判断是否需要停止本任务、是否需要park本任务,如果是则进行相应的处理,可以看到这里就会调用前文中注册的cleanup()和

park()回调函数;如果不需要stop和park则接下来按照状态机处理,对于初次运行的任务,这里会调用setup()回调进行相应的初始化动作;最后对于在正常运行中的最一般情况下,会调用thread_should_run回调判断是否需要调用注册主函数,视判断的返回值情况调用thread_fn()函数。下面来看前文中注册的setup回调watchdog_enable():

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static void watchdog_enable(unsigned int cpu)

{

struct hrtimer *hrtimer = raw_cpu_ptr(&watchdog_hrtimer);

/* kick off the timer for the hardlockup detector */

hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

hrtimer->function = watchdog_timer_fn;

/* Enable the perf event */

watchdog_nmi_enable(cpu);

/* done here because hrtimer_start can only pin to smp_processor_id() */

hrtimer_start(hrtimer, ns_to_ktime(sample_period),

HRTIMER_MODE_REL_PINNED);

/* initialize timestamp */

watchdog_set_prio(SCHED_FIFO, MAX_RT_PRIO - 1);

__touch_watchdog();

}

值得注意的是,这个函数是每个online的cpu都会运行的,首先从per cpu变量中获取本cpu 的高精度定时器指针hrtimer并初始化之,注册定时器到期调用函数watchdog_timer_fn(),然后启动定时器,指定到期时间就是前文中计算的sample_period(4s),最后调整当前进程的调度策略为FIFO实时进程并提高优先级,这么做是为了保证本喂狗任务能够以较高的优先级运行,以免无法及时喂狗而出现误报的情况。函数最后调用__touch_watchdog()函数执行首次喂狗动作。

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static void __touch_watchdog(void)

{

__this_cpu_write(watchdog_touch_ts, get_timestamp());

}

这里的watchdog_touch_ts也是一个per cpu变量,每个cpu维护自己独有的。这里将当前系统计时的时钟值(单位ns)以约等于的形式转换的秒单位的值,然后刷新到watchdog_touch_ts 中,以此模拟喂狗的动作。

定时器初始化完成后,接下来smpboot_thread_fn()函数就会调用thread_should_run()回调函数watchdog_should_run():

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static int watchdog_should_run(unsigned int cpu)

{

return __this_cpu_read(hrtimer_interrupts) !=

__this_cpu_read(soft_lockup_hrtimer_cnt);

}

此处只是比较了两个变量,当这两个变量不相等时才会调用thread_fn()回调,否则将任务设置为TASK_INTERRUPTIBLE状态然后调度(睡眠)。那这两个变量值合适才会不一样呢?下面来分析另外一条路,注意前文中的定时器已经启动了,来看一下sample_period时间到期后的调用函数,这个函数比较长,分段来看:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

/* watchdog kicker functions */

static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)

{

unsigned long touch_ts = __this_cpu_read(watchdog_touch_ts);

struct pt_regs *regs = get_irq_regs();

int duration;

int softlockup_all_cpu_backtrace = sysctl_softlockup_all_cpu_backtrace;

/* kick the hardlockup detector */

watchdog_interrupt_count();

/* kick the softlockup detector */

wake_up_process(__this_cpu_read(softlockup_watchdog));

/* .. and repeat */

hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));

首先获取最后一次的喂狗时间并保存在touch_ts中,然后调用watchdog_interrupt_count()函数累加hrtimer_interrupts值,显然该值表示的是当前cpu触发定时器中断的次数。

然后尝试唤醒已经睡眠的喂狗线程(注意,由于这里改变了hrtimer_interrupts值,前文中的watchdog_should_run自然就会返回TRUE了,那么就可以执行注册的主函数了)。接着本函数继续注册下一次的定时器到期时间。

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

if (touch_ts == 0) {

if (unlikely(__this_cpu_read(softlockup_touch_sync))) {

/*

* If the time stamp was touched atomically

* make sure the scheduler tick is up to date.

*/

__this_cpu_write(softlockup_touch_sync, false);

sched_clock_tick();

}

/* Clear the guest paused flag on watchdog reset */

kvm_check_and_clear_guest_paused();

__touch_watchdog();

return HRTIMER_RESTART;

}

这个判断在kgdb的调试中会用到,正常情况下不会进入,不做分析,继续往下:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

/* check for a softlockup

* This is done by making sure a high priority task is

* being scheduled. The task touches the watchdog to

* indicate it is getting cpu time. If it hasn't then

* this is a good indication some task is hogging the cpu

*/

duration = is_softlockup(touch_ts);

这里调用is_softlockup()函数返回当前时刻是否已经超过了“看门狗”的到期时间:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static int is_softlockup(unsigned long touch_ts)

{

unsigned long now = get_timestamp();

if (watchdog_enabled & SOFT_WATCHDOG_ENABLED) {

/* Warn about unreasonable delays. */

if (time_after(now, touch_ts + get_softlockup_thresh()))

return now - touch_ts;

}

return 0;

}

这里首先判断是否开启了soft lockup detect,是且已经超时的情况下(默认的超时时间是20s)返回超时时间间隔,否则返回0。下面来看一下超时的情况下会执行哪些处理:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

if (unlikely(duration)) {

......

/* only warn once */

if (__this_cpu_read(soft_watchdog_warn) == true) {

/*

* When multiple processes are causing softlockups the

* softlockup detector only warns on the first one

* because the code relies on a full quiet cycle to

* re-arm. The second process prevents the quiet cycle

* and never gets reported. Use task pointers to detect

* this.

*/

if (__this_cpu_read(softlockup_task_ptr_saved) !=

current) {

__this_cpu_write(soft_watchdog_warn, false);

__touch_watchdog();

}

return HRTIMER_RESTART;

}

soft_watchdog_warn标识会在已经出现了一次看门狗超时的情况下置位,此处的用意是对于同一个死锁进程,内核只做一次报警动作,如果死锁的进程发生了改变,那该标识会重新设置为false,将可以重新触发报警。

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

if (softlockup_all_cpu_backtrace) {

/* Prevent multiple soft-lockup reports if one cpu is already

* engaged in dumping cpu back traces

*/

if (test_and_set_bit(0, &soft_lockup_nmi_warn)) {

/* Someone else will report us. Let's give up */

__this_cpu_write(soft_watchdog_warn, true);

return HRTIMER_RESTART;

}

}

softlockup_all_cpu_backtrace是一个开关,用来表示是否需要在一个cpu超时时打印所有cpu 的backtrace信息,可以通过sysctrl进行控制。此处的用以是为了避免多个cpu再检测到死锁是同时调用trigger_allbutself_cpu_backtrace函数打印所有cpu的backtrace信息,因为在同一时刻只需要调用一次就可以了。

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

pr_emerg("BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n",

smp_processor_id(), https://www.doczj.com/doc/7a12942540.html,ation,

current->comm, task_pid_nr(current));

__this_cpu_write(softlockup_task_ptr_saved, current);

print_modules();

print_irqtrace_events(current);

if (regs)

show_regs(regs);

else

dump_stack();

这里就开始依次打印出内核模块信息,中断信息,中断栈信息和backtrace信息,然后记录下了触发死锁的任务描述符task_struct。

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

if (softlockup_all_cpu_backtrace) {

/* Avoid generating two back traces for current

* given that one is already made above

*/

trigger_allbutself_cpu_backtrace();

clear_bit(0, &soft_lockup_nmi_warn);

/* Barrier to sync with other cpus */

smp_mb__after_atomic();

}

这里同前面相呼应,调用trigger_allbutself_cpu_backtrace()函数打印出了所有cpu的backtrace 信息,这个函数是arch架构相关的。

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK);

if (softlockup_panic)

panic("softlockup: hung tasks");

__this_cpu_write(soft_watchdog_warn, true);

最后如果设置了panic标识,则直接触发panic,否则置位了报警标识,后续针对触发本次报警的死锁任务将不再报警。分析完超时的处理方式,回过头分析一下前文中的喂狗进程是如何运行的。

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

static void watchdog(unsigned int cpu)

{

__this_cpu_write(soft_lockup_hrtimer_cnt,

__this_cpu_read(hrtimer_interrupts));

__touch_watchdog();

/*

* watchdog_nmi_enable() clears the NMI_WATCHDOG_ENABLED bit in the

* failure path. Check for failures that can occur asynchronously -

* for example, when CPUs are on-lined - and shut down the hardware

* perf event on each CPU accordingly.

*

* The only non-obvious place this bit can be cleared is through

* watchdog_nmi_enable(), so a pr_info() is placed there. Placing a

* pr_info here would be too noisy https://www.doczj.com/doc/7a12942540.html,s it would result in a message

* every few seconds if the hardlockup was disabled but the softlockup

* enabled.

*/

if (!(watchdog_enabled & NMI_WATCHDOG_ENABLED))

watchdog_nmi_disable(cpu);

}

这里首先将hrtimer_interrupts的值付给soft_lockup_hrtimer_cnt,这样在本次喂狗结束后到下一次定时器到期前,该函数不会投入运行,进程将进入睡眠状态。该函数剩下的就很简单了,直接调用__touch_watchdog()函数执行喂狗动作。

以上就是进程上下文中的R状态死锁的核心监测代码,该程序还提供了一些可以通过sysctrl 控制启停和超时时间等的接口,比较简单就不分析了。从以上实现可以看出其本质就是利用了hr定时器中断处理函数周期性的唤醒进程执行软喂狗动作,同时自身则检测软看门狗是否超时。在正常的情况下,当前cpu的定时器中唤醒的喂狗进程一定是能够得到调度的(视cpu负荷情况可能略有延时),即是不可能超过设定的超时时间的,但是如果当前cpu中的某一个进程占用cpu时间超过了设定的超时时间(20s),就会直接导致软看门狗超时并触发一次报警动作,如果这个进程一直不释放cpu(例如while循环),那么也只会报警一次,反之会重新开启报警功能。

二、示例演示

演示环境:树莓派b(Linux 4.1.15)

1、首先确认启用内核配置

Kernel hacking --->

Debug Lockups and Hangs --->

[*] Detect Hard and Soft Lockups

[*] Panic (Reboot) On Soft Lockups(可选)

2、然后确认内核调度策略配置

Kernel Features --->

Preemption Model (V oluntary Kernel Preemption (Desktop))

( ) No Forced https://www.doczj.com/doc/7a12942540.html,emption (Server)

(X) V oluntary Kernel Preemption (Desktop)

( ) Preemptible Kernel (Low-Latency Desktop)

注意调度策略需要配置为非抢占式的内核,若是抢占式的,则测试程序会无效(因为其他内核进程可能会主动抢占死锁的进程)。

3、编写演示程序

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

#include

#include

#include

static int __init rlock_init(void)

{

while(1);

return 0;

}

static void __exit rlock_exit(void)

{

return;

}

module_init(rlock_init);

module_exit(rlock_exit);

MODULE_LICENSE("GPL");

本程序非常的简单,编写一个模块程序并在模块初始化函数中执行while(1)循环即可,以此来触发insmod进程在rlock_init()函数中陷入R状态死锁。

在树莓派中加载该模块后直接中断就”挂死“了,然后再约20s后内核打印如下:

root@apple:~# insmod rlock.ko

[ 60.254450] NMI watchdog: BUG: soft lockup - CPU#0 stuck for 23s! [insmod:515]

[ 60.261684] Modules linked in: rlock(O+) sg bcm2835_gpiomem bcm2835_wdt(O) uio_pdrv_genirq uio

[ 60.270344] CPU: 0 PID: 515 Comm: insmod Tainted: G O 4.1.15 #8

[ 60.277382] Hardware name: BCM2708

[ 60.280783] task: c591df60 ti: c5eaa000 task.ti: c5eaa000

[ 60.286189] PC is at rlock_init+0xc/0x10 [rlock]

[ 60.290812] LR is at do_one_initcall+0x90/0x1e8

[ 60.295342] pc : [] lr : [] psr: 60000013

[ 60.295342] sp : c5eabdc8 ip : c5eabdd8 fp : c5eabdd4

[ 60.306803] r10: 00000000 r9 : 00000124 r8 : bf02e000

[ 60.312020] r7 : bf02c0a4 r6 : c5eed660 r5 : c0bbd6e8 r4 : c0bbd6e8

[ 60.318539] r3 : 00000000 r2 : c6c01f00 r1 : 60000013 r0 : 60000013

[ 60.325058] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user [ 60.332183] Control: 00c5387d Table: 05828008 DAC: 00000015

[ 60.337924] CPU: 0 PID: 515 Comm: insmod Tainted: G O 4.1.15 #8

[ 60.344958] Hardware name: BCM2708

[ 60.348410] [] (unwind_backtrace) from [] (show_stack+0x20/0x24) [ 60.356168] [] (show_stack) from [] (dump_stack+0x20/0x28)

[ 60.363398] [] (dump_stack) from [] (show_regs+0x1c/0x20)

[ 60.370547] [] (show_regs) from [] (watchdog_timer_fn+0x160/0x1a4)

[ 60.378482] [] (watchdog_timer_fn) from [] (__run_hrtimer+0x68/0x1c4)

[ 60.386668] [] (__run_hrtimer) from [] (hrtimer_interrupt+0x104/0x270)

[ 60.394942] [] (hrtimer_interrupt) from [] (bcm2708_timer_interrupt+0x38/0x48)

[ 60.403911] [] (bcm2708_timer_interrupt) from [] (handle_irq_event_percpu+0x5c/0x200)

[ 60.413481] [] (handle_irq_event_percpu) from [] (handle_irq_event+0x38/0x48)

[ 60.422359] [] (handle_irq_event) from [] (handle_level_irq+0x98/0x114)

[ 60.430712] [] (handle_level_irq) from [] (__handle_domain_irq+0x7c/0xdc)

[ 60.439244] [] (__handle_domain_irq) from [] (handle_IRQ+0x2c/0x30)

[ 60.447251] [] (handle_IRQ) from [] (asm_do_IRQ+0x18/0x1c) [ 60.454485] [] (asm_do_IRQ) from [] (__irq_svc+0x38/0xb0)

[ 60.461613] Exception stack(0xc5eabd80 to 0xc5eabdc8)

[ 60.466670] bd80: 60000013 60000013 c6c01f00 00000000 c0bbd6e8 c0bbd6e8 c5eed660 bf02c0a4

[ 60.474845] bda0: bf02e000 00000124 00000000 c5eabdd4 c5eabdd8 c5eabdc8 c0009558 bf02e00c

[ 60.483010] bdc0: 60000013 ffffffff

[ 60.486515] [] (__irq_svc) from [] (rlock_init+0xc/0x10 [rlock]) [ 60.494271] [] (rlock_init [rlock]) from [] (do_one_initcall+0x90/0x1e8)

[ 60.502721] [] (do_one_initcall) from [] (do_init_module+0x6c/0x1c0)

[ 60.510819] [] (do_init_module) from [] (load_module+0x1690/0x1d34)

[ 60.518827] [] (load_module) from [] (SyS_init_module+0xdc/0x130)

[ 60.526662] [] (SyS_init_module) from [] (ret_fast_syscall+0x0/0x54)

[ 60.534745] Kernel panic - not syncing: softlockup: hung tasks

[ 60.540577] CPU: 0 PID: 515 Comm: insmod Tainted: G O L 4.1.15 #8

[ 60.547613] Hardware name: BCM2708

[ 60.551033] [] (unwind_backtrace) from [] (show_stack+0x20/0x24) [ 60.558781] [] (show_stack) from [] (dump_stack+0x20/0x28)

[ 60.566005] [] (dump_stack) from [] (panic+0x90/0x1fc)

[ 60.572885] [] (panic) from [] (watchdog_timer_fn+0x188/0x1a4) [ 60.580464] [] (watchdog_timer_fn) from [] (__run_hrtimer+0x68/0x1c4)

[ 60.588648] [] (__run_hrtimer) from [] (hrtimer_interrupt+0x104/0x270)

[ 60.596917] [] (hrtimer_interrupt) from [] (bcm2708_timer_interrupt+0x38/0x48)

[ 60.605881] [] (bcm2708_timer_interrupt) from [] (handle_irq_event_percpu+0x5c/0x200)

[ 60.615450] [] (handle_irq_event_percpu) from [] (handle_irq_event+0x38/0x48)

[ 60.624326] [] (handle_irq_event) from [] (handle_level_irq+0x98/0x114)

[ 60.632680] [] (handle_level_irq) from [] (__handle_domain_irq+0x7c/0xdc)

[ 60.641211] [] (__handle_domain_irq) from [] (handle_IRQ+0x2c/0x30)

[ 60.649218] [] (handle_IRQ) from [] (asm_do_IRQ+0x18/0x1c) [ 60.656450] [] (asm_do_IRQ) from [] (__irq_svc+0x38/0xb0)

[ 60.663578] Exception stack(0xc5eabd80 to 0xc5eabdc8)

[ 60.668633] bd80: 60000013 60000013 c6c01f00 00000000 c0bbd6e8 c0bbd6e8 c5eed660 bf02c0a4

[ 60.676806] bda0: bf02e000 00000124 00000000 c5eabdd4 c5eabdd8 c5eabdc8 c0009558 bf02e00c

[ 60.684972] bdc0: 60000013 ffffffff

[ 60.688477] [] (__irq_svc) from [] (rlock_init+0xc/0x10 [rlock]) [ 60.696227] [] (rlock_init [rlock]) from [] (do_one_initcall+0x90/0x1e8)

[ 60.704671] [] (do_one_initcall) from [] (do_init_module+0x6c/0x1c0)

[ 60.712765] [] (do_init_module) from [] (load_module+0x1690/0x1d34)

[ 60.720771] [] (load_module) from [] (SyS_init_module+0xdc/0x130)

[ 60.728607] [] (SyS_init_module) from [] (ret_fast_syscall+0x0/0x54)

PANIC: softlockup: hung tasks

三、总结

R状态死锁是在进程上下文或中断上下文中出现的一种长期占用cpu的非正常现象,在不易复现的环境中比较难以定位。本文分析了内核提供的监测其中在进程上下文中死锁的SOFTLOCKUP_DETECTOR机制原理及实现方式。开发人员可通过这种机制较为有效的定位问题。

Linux内核崩溃原因分析及错误跟踪技术

Linux内核崩溃原因分析及错误跟踪技术 随着嵌入式Linux系统的广泛应用,对系统的可靠性提出了更高的要求,尤其是涉及到生命财产等重要领域,要求系统达到安全完整性等级3级以上[1],故障率(每小时出现危险故障的可能性)为10-7以下,相当于系统的平均故障间隔时间(MTBF)至少要达到1141年以上,因此提高系统可靠性已成为一项艰巨的任务。对某公司在工业领域14 878个控制器系统的应用调查表明,从2004年初到2007年9月底,随着硬软件的不断改进,根据错误报告统计的故障率已降低到2004年的五分之一以下,但查找错误的时间却增加到原来的3倍以上。 这种解决问题所需时间呈上升的趋势固然有软件问题,但缺乏必要的手段以辅助解决问题才是主要的原因。通过对故障的统计跟踪发现,难以解决的软件错误和从发现到解决耗时较长的软件错误都集中在操作系统的核心部分,这其中又有很大比例集中在驱动程序部分[2]。因此,错误跟踪技术被看成是提高系统安全完整性等级的一个重要措施[1],大多数现代操作系统均为发展提供了操作系统内核“崩溃转储”机制,即在软件系统宕机时,将内存内容保存到磁盘[3],或者通过网络发送到故障服务器[3],或者直接启动内核调试器[4]等,以供事后分析改进。 基于Linux操作系统内核的崩溃转储机制近年来有以下几种: (1) LKCD(Linux Kernel Crash Dump)机制[3]; (2) KDUMP(Linux Kernel Dump)机制[4]; (3) KDB机制[5]; (4) KGDB机制[6]。 综合上述几种机制可以发现,这四种机制之间有以下三个共同点: (1) 适用于为运算资源丰富、存储空间充足的应用场合; (2) 发生系统崩溃后恢复时间无严格要求; (3) 主要针对较通用的硬件平台,如X86平台。 在嵌入式应用场合想要直接使用上列机制中的某一种,却遇到以下三个难点无法解决: (1) 存储空间不足 嵌入式系统一般采用Flash作为存储器,而Flash容量有限,且可能远远小于嵌入式系统中的内存容量。因此将全部内存内容保存到Flash不可行。

关于Linux 内核中五个主要子系统的介绍

关于Linux 内核中五个主要子系统的介绍 发布时间:2008.01.02 06:23来源:赛迪网作者:sixth 1.进程调度(SCHED):控制进程对CPU的访问。当需要选择下一个进程运行时,由调度程序选择最值得运行的进程。可运行进程实际上是仅等待CPU资源的进程,如果某个进程在等待其它资源,则该进程是不可运行进程。Linux使用了比较简单的基于优先级的进程调度算法选择新的进程。 2.内存管理(MM)允许多个进程安全的共享主内存区域。Linux的内存管理支持虚拟内存,即在计算机中运行的程序,其代码,数据,堆栈的总量可以超过实际内存的大小,操作系统只是把当前使用的程序块保留在内存中,其余的程序块则保留在磁盘中。必要时,操作系统负责在磁盘和内存间交换程序块。内存管理从逻辑上分为硬件无关部分和硬件有关部分。硬件无关部分提供了进程的映射和逻辑内存的对换;硬件相关的部分为内存管理硬件提供了虚拟接口。 3.虚拟文件系统(VirtualFileSystem,VFS)隐藏了各种硬件的具体细节,为所有的设备提供了统一的接口,VFS提供了多达数十种不同的文件系统。虚拟文件系统可以分为逻辑文件系统和设备驱动程序。逻辑文件系统指Linux所支持的文件系统,如ext2,fat等,设备驱动程序指为每一种硬件控制器所编写的设备驱动程序模块。 4.网络接口(NET)提供了对各种网络标准的存取和各种网络硬件的支持。网络接口可分为网络协议和网络驱动程序。网络协议部分负责实现每一种可能的网络传输协议。网络设备驱动程序负责与硬件设备通讯,每一种可能的硬件设备都有相应的设备驱动程序。 5.进程间通讯(IPC) 支持进程间各种通信机制。处于中心位置的进程调度,所有其它的子系统都依赖它,因为每个子系统都需要挂起或恢复进程。一般情况下,当一个进程等待硬件操作完成时,它被挂起;当操作真正完成时,进程被恢复执行。例如,当一个进程通过网络发送一条消息时,网络接口需要挂起发送进程,直到硬件成功地完成消息的发送,当消息被成功的发送出去以后,网络接口给进程返回一个代码,表示操作的成功或失败。其他子系统以相似的理由依赖于进程调度。

Linux内核移植开发手册

江苏中科龙梦科技有限公司 Linux内核移植开发手册 修 订 记 录 项 次 修订日期 版 本修订內容修订者审 核 1 2009‐02‐04 0.1 初版发行陶宏亮, 胡洪兵 2 2009‐11‐20 0.2 删除一些 多余文字 陶宏亮, 胡洪兵

DISCLAIMER THIS DOCUMENTATION IS PROVIDED FOR USE WITH LEMOTE PRODUCTS. NO LICENSE TO LEMOTE PROPERTY RIGHTS IS GRANTED. LEMOTE ASSUMES NO LIABILITY, PROVIDES NO WARRANTY EITHER EXPRESSED OR IMPLIED RELATING TO THE USAGE, OR INTELLECTUAL PROPERTY RIGHT INFRINGEMENT EXCEPT AS PROVIDED FOR BY LEMOTE TERMS AND CONDITIONS OF SALE. LEMOTE PRODUCTS ARE NOT DESIGNED FOR AND SHOULD NOT BE USED IN ANY MEDICAL OR LIFE SUSTAINING OR SUPPORTING EQUIPMENT. ALL INFORMATION IN THIS DOCUMENT SHOULD BE TREATED AS PRELIMINARY. LEMOTE MAY MAKE CHANGES TO THIS DOCUMENT WITHOUT NOTICE. ANYONE RELYING ON THIS DOCUMENTATION SHOULD CONTACT LEMOTE FOR THE CURRENT DOCUMENTATION AND ERRATA. JIANGSU LEMOTE TECHNOLOGY CORPORATION LIMITED MENGLAN INDUSTRIAL PARK,YUSHAN,CHANGSHU CITY,JIANGSU PROVINCE,CHINA Tel: 0512‐52308661 Fax: 0512‐52308688 Http: //https://www.doczj.com/doc/7a12942540.html,

嵌入式Linux之Kernel(裁减移植)启动调试、打印技术 printascii(补充)、内核调试

嵌入式系统搭建过程中,对于系统平台搭建工程师在完成Bootloader 的调试之后就进入Kernel 裁减移植的阶段,其中最重要的一步是Kernel 启动的调试,在调试Kernel 过程中通常遇到最常见的问题是启动异常: Uncompressing Linux............................................................ ........................... done, booting the kernel.( 挂死在此处) 注意:这里是arch/arm/boot/compressed/head.S的解压过程,调用了decompress_kernel()(同目录下的misc.c)->include/asm-arm/arch-xxx/uncompress.h的putc()实现。这是在uboot中初始化的,用的是物理地址,因为此时内核还没有起来。 而printascii则是调用了汇编。printascii()位于arch/arm/kernel/debug.S,他需要调用虚拟地址,此虚拟地址通过machine_start提供,而相关的宏在include/asm/arch-xxx/debug-macro.S实现,这下明白了。 10-05-14添加:debug.s里面需要判断一下当前是否打开了mmu,然后指定uart的基址。在解压阶段的head.s,mmu是1:1映射,目的是加快速度。到了内核的head.s,就是真正的mmu了,此时就是虚拟地址了。 导致驱动异常(启动挂死)的原因有很多,如基于EVM 板的硬件做了修改(如更改了FLASH 空间大小、地址和型号,更改了SDRAM 、DDR SDRAM 空间大小、地址和型号,更改了晶振频率等),板卡ID号不支持等。那么如何进行调试那,其实有两种调试技术比较有效。 Kernel 启动调试技术- 使用printascii() 函数跟踪start_kernel() 有没运行,在booting the kernel 之后Kernel 最先执行的是start_kernel() 函数,确认start_kernel() 有否执行就是在其开始代码段添加printascii("start_kernel …") ,如果串口没有打印出start_kernel …,说明start_kernel() 没有运行,那么可能的原因有Bootloader 配置的启动参数错误、 Kernel 加载到(DDR) SDRAM 的地址不正确, Kernel 编译时指定的(DDR) SDRAM 运行地址不正确等。这样就需要一项一项排查错误,当错误被排查完毕,通常打印出start_kernel …是种必然,如果打印出这仪信息说明 Kernel已进入到start_kernel() 执行,如果此时有串口启动打印就比较成功了,如果仍然没有打印启动信息,就需要另外一种调试技术。 附代码修改:init/main.c <<- … extern void printascii(const char*); // Modify asmlinkage void __init start_kernel(void)

使用QEMU+GDB调试Linux内核

使用QEMU调试Linux内核 一.使用QEMU安装Ubuntu10.04 1.安装qemu ubuntu下使用sudo apt-get install 安装的qemu版本是0.12.3,该版本中存在bug,使得无法在断点处停下;因此需要在qemu官方网站https://www.doczj.com/doc/7a12942540.html,/Download上下载最新的版本qemu-0.12.5.tar.gz的源代码包自己进行编译安装: ●Sudo apt-get install zlib1g-dev libsdl-dev ●解压源代码后,进入源代码所在目录执行./confingure ●执行make ●执行sudo make install 2.创建QEMU格式的硬盘 qemu-img create –f qcow2name.img size 例如:qemu-img create –f qcow2 ubuntu10.04.img 4GB 3.在创建的硬盘上安装操作系统 qemu–hdaname.img–cdrom ~/Download/ubuntu10.04.iso –boot d 说明:使用hda指定硬盘镜像,使用CDROM选定光驱。-boot d 指从cdrom启动,-boot a是软盘,-boot c 是硬盘;使用qemu或qemu-system-x86_64(64为机子),有时安装系统会很慢,这是可以考虑使用kvm来代替。 例如:kvm–hda ubuntu10.04.img –cdrom ./ubuntu-10.04.iso -boot

d 4.从已经装好操作系统的硬盘启动 qemu–hda ubuntu10.04.img 5.在64位的主机上要使用qemu-system-x86_64命令来代替qemu 二.自己编译内核 现将Linux的编译调节过程简述为: 1. 下载自己要调试的Linux内核的源代码,这个可以从Linux内 核的官方网站上得到:https://www.doczj.com/doc/7a12942540.html, 2. 编译内核最主要的便是配置文件.config,为了能够准确的得到 结果(第一次不要求编译时间),将本机的config文件直接拷 贝到解压后的源代码中。 3.然后进行make操作,结束后将产生的bzImage文件拷到boot 目录下,重启,选择自己编译的内核,这样一般不会出问题,但时间较慢,大约编译一次需要40分钟。 3.1以前编译内核是为调试内核服务的,现在做华为的项目, 发现需要在实际的机器上运行自己编译的内核,参考网站: https://www.doczj.com/doc/7a12942540.html,/tips/compiling-linux-kernel-26.html 4.为了降低编译时间,就需要对配置文件进行裁剪,在配置文件 中有好多是本机不需要的模块,参考: https://www.doczj.com/doc/7a12942540.html,/Linux/kernel_options.html。另外调试内 核与模块无关,所以辨识为M的直接可以不选。 5.剪裁的时候采用“逐步瘦身”法,先剪裁掉某个或某几个模块, 然后在进行编译,若没错,在进行模块裁剪,这样可以最大程

在vmware虚拟机上调试内核

安装系统(在创建一个新的虚拟机时一定要用IDE硬盘) 1、在vmware上安装一个linux虚拟机(我用的软件版本:vmware 5.5, fedora 6) 从Internet下载源代码 1、linux-2.6.16 2、kgdb-2.6.16 编译内核 1、将kgdb-2.6.16补丁打到Linux内核源码里。 2、编译内核(make menuconfig, make, make modules_install, make install) 在内核配置菜单的Kernel hacking选项中选择kgdb调试项,例如: [*] KGDB: kernel debugging with remote gdb [*] KGDB: Console messages through gdb Method for KGDB communication (KGDB: On generic serial port (8250)) ---> < > KGDB: On ethernet [*] Simple selection of KGDB serial port (115200) Debug serial port baud rate (0) Serial port number for KGDB 3、修改grub.conf 例如: title Fedora Core (2.6.16) root (hd0,0) kernel /vmlinuz-2.6.16 ro root=/dev/VolGroup00/LogVol00 initrd /initrd-2.6.16_debug.img 改为: title Fedora Core (2.6.16) root (hd0,0) kernel /vmlinuz-2.6.16 ro root=/dev/VolGroup00/LogVol00 kgdb8250=0,115200 initrd /initrd-2.6.16_debug.img 4、请检验新编译的内核是否可用。如果不能启动,可能是编译选项没有选好(一直困扰我的问题。实在不行就多选点)。如果没有问题,关闭这个linux虚拟机,进入下一步。

Linux内核启动流程分析(一)

很久以前分析的,一直在电脑的一个角落,今天发现贴出来和大家分享下。由于是word直接粘过来的有点乱,敬请谅解! S3C2410 Linux 2.6.35.7启动分析(第一阶段) arm linux 内核生成过程 1. 依据arch/arm/kernel/vmlinux.lds 生成linux内核源码根目录下的vmlinux,这个vmlinux属于未压缩, 带调试信息、符号表的最初的内核,大小约23MB; 命令:arm-linux-gnu-ld -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o init/built-in.o --start-group arch/arm/mach-s3c2410/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o drivers/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o 2. 将上面的vmlinux去除调试信息、注释、符号表等内容,生成arch/arm/boot/Image,这是不带多余信息的linux内核,Image的大小约 3.2MB; 命令:arm-linux-gnu-objcopy -O binary -S vmlinux arch/arm/boot/Image 3.将 arch/arm/boot/Image 用gzip -9 压缩生成arch/arm/boot/compressed/piggy.gz大小约 1.5MB;命令:gzip -f -9 < arch/arm/boot/compressed/../Image > arch/arm/boot/compressed/piggy.gz 4. 编译arch/arm/boot/compressed/piggy.S 生成arch/arm/boot/compressed/piggy.o大小约1.5MB,这里实 际上是将piggy.gz通过piggy.S编译进piggy.o文件中。而piggy.S文件仅有6行,只是包含了文件piggy.gz; 命令:arm-linux-gnu-gcc -o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/piggy.S 5. 依据arch/arm/boot/compressed/vmlinux.lds 将arch/arm/boot/compressed/目录下的文件head.o 、piggy.o 、misc.o链接生成arch/arm/boot/compressed/vmlinux,这个vmlinux是经过压缩且含有自解压代码的内核, 大小约1.5MB; 命 令:arm-linux-gnu-ld zreladdr=0x30008000 params_phys=0x30000100 -T arch/arm/boot/compressed/vmlinux.lds a rch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/misc.o -o arch/arm /boot/compressed/vmlinux

Linux内核漏洞调试环境搭建

Linux内核漏洞调试环境搭建 一.前言 之前没怎么用过Linux,但是那天看到exploit-db上有不少Linux内核漏洞的POC。当时想如果可以请自动手调试一下这些漏洞,肯定会学到一些Linux下特定漏洞的利用技巧。(比如怎么利用空指针引用漏洞来进行本地提权)。所以就GOOGLE了很多关于Linux内核调试的文章,虽然一步一步老老实实照前人的指点的做,但是还是问题连着问题。反反复复的尝试,才历尽千心万苦搭建起了这个内核漏洞调试环境。 在此过程中得到了广大网友的帮助,特别是wzt85和塞(他的ID为塞)这两位前辈的指点。既然取之于“网”,那我觉得应该把这个过程用文字描述出来放到网络上,与同道中人分享。 本文的第二部分将简单介绍目前Linux下内核调试的几种常用调试技术路线,由于我对Linux的了解确实不多,所以这一部分写的肯定会很不专业,但目的在于抛砖引玉——更专业的文章烦请自行GOOGLE。 本文的第三部分会详细介绍该调试环境的搭建过程,关键点会有截图说明。 本文的第四部分是一点补充性的文字。 二.Linux下内核调试技术路线 1.QEMU+GDB QEMU是一款开源的虚拟机软件,它自身带有gdb stub可用于和Host 主机上的GDB通信来对Guest主机的Linux内核进行源码(C代码)级调试。为实现源码级调 试,那必须要有调试信息以及符号表,所以首先从https://www.doczj.com/doc/7a12942540.html,上下载一份 Linux内核源代码进行编译。编译成功后会得到bzImage文件和vmlinux文件。其中vmlinux就是要供Host主机上的GDB进行调试的带有调试信息,符号表的内核文 件。 使用这种方法试验环境的搭建比较简单,而且最吸引人的地方在于它能够实现源码级的调试。但是遗憾的是,这种方法调试不了漏洞。因为当Guest主机上的内核发生内存访问异常的时候,Host主机中的GDB根本得不到异常事件,这样一来也就无法获知是那条指令引发的异常,以及被访问的内存地址是什么。

Linux内核中的Kconfig用法与说明

Linux内核中的Kconfig文件 本节不对内核的Kconfig文件进行深入展开,更多Kconfig语法和说明请阅读 。 内核源码树每个目录下都还包含一个Kconfig文件,用于描述所在目录源代码相关的内核配置菜单,各个目录的Kconfig文件构成了一个分布式的内核配置数据库。通过make menuconfig(make xconfig或者make gconfig)命令配置内核的时候,从Kconfig文件读取菜单,配置完毕保存到文件名为.config的内核配置文件中,供Makefile文件在编译内核时使用。 1.1.1 Kconfig基本语法 如程序清单0.1所示代码摘自文件,是一个比较典型的Kconfig 文件片段,包含了Kconfig的基本语法。 程序清单0.1drivers/char/Kconfig片段 menu "Character devices" source "drivers/tty/Kconfig" config DEVKMEM bool "/dev/kmem virtual device support" default y help Say Y here if you want to support the /dev/kmem device. The /dev/kmem device is rarely used, but can be used for certain kind of kernel debugging operations. When in doubt, say "N". …… endmenu 1.子菜单 通过menu和endmenu来定义一个子菜单,程序清单0.1所示代码定义了一个“Character devices”子菜单,子菜单在界面中用“--->”表示,如图0.1所示。 图0.1menu定义的子菜单 子菜单的菜单项则由config来定义,随后的“bool”、“default”、“help”等都是该菜单 项的属性:

Linux内核的配置与编译

Computer Knowledge and Technology 电脑知识 与技术第5卷第3期(2009年1月)Linux 内核的配置与编译 胡庆烈 (佛山职业技术学院电子信息工程系,广东佛山528000) 摘要:Linux 是一种实用性很强的现代操作系统,它开放源代码,并允许用户升级其内核。在Redhat 7.2环境中,详细分析了Linux 2.4.18版本的内核配置、编译及新内核切换等操作过程。 关键词:Linux ;内核;配置;编译 中图分类号:TP316文献标识码:A 文章编号:1009-3044(2009)03-0730-02 Configuration and Compiling of Linux Kernel HU Qing-lie (Department of Electonics &Information,Foshan Polytechnic College,Foshan 528000,China) Abstract:Linux is a very practical modern operating system,which opens source coding and allows the user to upgrade its kernel.In the environment of Redhat 7.2,the paper analysis the Linux 2.4.18version of kernel configuration,compiling and new kernel process switch -ing,and so on. Key words:Linux;kernel;configuration;compile 1引言 Linux 是一个自由的多任务操作系统,它以开放源码、对硬件的配置要求低并兼具现代操作系统的优点而得到了迅猛的发展。操作系统的内核是操作系统的核心,它有很多基本的功能,如虚拟内存、多任务、共享库、需求加载、共享的写时拷贝(copy-on-write)、可执行程序和TCP/IP 网络功能等。 用户编译配置Linux 的内核,主要有以下三个原因:1)从现有内核中去除一些不需要的功能,使自定制的内核运行速度更快、更稳定,且具有更少的代码;2)使系统拥有更多的内存,内核部分将不会被交换到虚拟内存中;3)为了提高速度,将某种功能编译到内核中。 2Linux 内核升级的准备 2.1安装一个Linux 操作系统 在编译一个新的Linux 内核之前,首先应在微机中安装一个Linux 操作系统,以便利用该Linux 环境进行新内核的配置和安装。这里是以Redhat 7.2为例,在安装Redhat 7.2的过程中,有两个问题需要注意: 1)硬盘的分区:由于每个硬盘只能拥有4个主分区(Primary Partition ),故用户需要扩展分区,则至少需要腾出一个主分区来划分逻辑分区。在安装Linux 操作系统时,至少需要两个分区,其中本机分区(Linux Native )是供Linux 存放系统文件,而置换分区(Linux Swap )是用作虚拟内存的存取空间。此外,为了和Windows 系统进行文件的复制转换,还应创建一个FAT32类型的分区。 2)安装LILO 启动程序:LILO 是Linux 的核心加载程序,它提供了从DOS 环境启动Linux 的功能,并支持多重启动菜单,让用户选择启动哪一个分区的操作系统。 2.2获取新的Linux 内核源代码 安装了Linux 操作系统后,接下来的工作是寻找新内核的源代码。目前,在Internet 上提供Linux 源代码的站点有很多,如https://www.doczj.com/doc/7a12942540.html, 就是Linux 内核版本发布的官方网站,用户可以从该站点上获得最新版本的Linux 内核源代码,这里是以linux- 2.4.18版本为例。 2.3对新的Linux 内核源代码包进行解压 由于大部分开放性操作系统的程序都是以压缩文件(tgz 、zip 、gz 与bz2)的形式进行发布,所以从网络上取得这些压缩文件后,都先要解压缩之后才能安装使用。具体过程如下: 1)执行“GNOME Terminal ”,把X Windows System 图形用户界面切换至文件操作模式; 2)执行“#cp /root/linux-2.4.18.tar.gz /usr/src ”,把从网络下载的压缩包复制至/usr/src 处; 3)执行“#tar -zxvf linux-2.4.18.tar.gz ”,对压缩包进行解压,解压文件存放在/usr/src/linux-2.4.18目录中。 2.4清除不正确文件及其它从属文件 为了确保源代码目录中没有不正确的文件和其它从属文件,一般需要运行mrproper 命令进行清理,具体操作如下: #cd /usr/src/linux-2.4.18 #make mrproper 如果是使用刚下载的完整的源程序包进行编译,则可以省略mrproper 操作。但若已反复多次使用这些源程序来进行内核编译的,则应要先运行一下这个命令。 收稿日期:2008-12-11 作者简介:胡庆烈(1969-),男,揭阳惠来人,电子助理工程师,主要从事电子技术的教研工作。 ISSN 1009-3044Computer Knowledge and Technology 电脑知识与技术Vol.5,No.3,January 2009,pp.730-731,735E-mail:kfyj@https://www.doczj.com/doc/7a12942540.html, https://www.doczj.com/doc/7a12942540.html, Tel:+86-551-56909635690964

Linux内核调试

0x00 前言 这段时间开始学习了linux内核提权,也跟进看了一些漏洞。但是由于linux系统版本、内核版本的不同,驱动模块或者是某个函数的加载地址都是不同的,如果不能自己亲自调试内核,就算给了exp也是无法利用。之前也没有怎么接触过内核调试,所以这几天找了许多资料开始学习调试内核的方法,总结整理在这。 本文测试系统是Ubuntu12.04 64位。 0x01 准备环境 首先当然是准备需要调试的内核版本,linux的所有历史版本都可以在这里找到。 down下来后进入源码树根目录,开始配置内核,这里使用基于ncurse库编制的图形界面工具: $ make menuconfig 由于我们需要使用kgdb调试内核,注意下面这几项一定要配置好: KernelHacking --> 选中Compile the kernel with debug info 选中Compile the kernel with frame pointers 选中KGDB:kernel debugging with remote gdb,其下的全部都选中。 Processor type and features--> 去掉Paravirtualized guest support KernelHacking--> 去掉Writeprotect kernel read-only data structures(否则不能用软件断点) 保存config文件之后make、make modules_install、make install编译安装就好。 具体安装编译内核可以看我另一篇笔记 0x02 使用kvm、gdb调试内核 先介绍一下现在用得比较多的调试内核方法吧,简单地说就是在linux系统里再装一个kvm虚拟机配置好gdb远程调试支持,然后在linux主机上连接调试。但是我因为电脑配置不高,装了kvm太卡就放弃了这个方法orz 总之还是讲一下怎么调试吧。 查看cpu是否支持,如果没有输出,要在虚拟机设置里选上Inter VT: $ grep vmx /proc/cpuinfo

linux内核编程规范

竭诚为您提供优质文档/双击可除 linux内核编程规范 篇一:00-linux系统编程常识-王保明 linux系统编程-应用编程常识--专题讲座 writtenby王保明 计算机系统组成 1计算机系统硬件组成 2操作系统 篇二:linux2.6驱动开发系列教程 [置顶]linux2.6驱动开发系列教程 这段时间一直在做android下的驱动,android驱动底层跟linux如出一辙,所以这里准备做一个专题,把linux 驱动做一个总结,为android接下来的驱动开发打好基础,大致的思想如下: 一、linux驱动基础开发 0、 1、 2、 3、

4、linux驱动基础开发4——linux字符驱动模型(memdriver内存读写) 5、linux驱动基础开发5——linux设备文件注册(devfs、mdev、sys、proc)讲解 6、linux驱动基础开发6——linuxgpio驱动实例分析(s3c2440/6410io操作) 7、linux驱动基础开发7——linux1*3io键盘驱动实例分析 8、linux驱动基础开发8——linux中断机制讲解与实 例分析(s3c2440/6410外部中断机制) 9、linux驱动基础开发9——linux数据缓冲机制(kfifo)讲解与实例分析 10、linux驱动基础开发10——linux并发、同步、互 斥机制(信号量、互斥锁、等待任务队列)讲解与实例分析 11、linux驱动基础开发11——linux周期性事件(内 核定时器)讲解 12、linux驱动基础开发12——linux周期性事件(内 核线程)讲解 13、linux驱动基础开发13——linux任务阻塞 (select\poll)机制讲解 14、linux驱动基础开发14——linux异常处理(内核 信号)讲解

基于Linux内核定制X86平台的微操作系统(很完整详尽文章)

基于Linux内核定制X86平台的微操作系统摘要:1 0 前言2 0.1 Linux系统简介2 0.2 Linux的基本思想2 0.3 Linux内核2 0.4 Linux内核版本命名3 0.5 Linux文件系统3 0.6Linux内核引导4 0.7Linux系统组成4 1 平台的搭建4 1.1 硬件平台4 1.2 软件平台4 1.2.1 Ubuntu系统的下载4 1.2.2 Ubuntu系统的安装4 1.2.3 Ubuntu系统的配置4 2 Linux内核的编译5 2.1 内核的下载5 2.2 内核的定制5 2.3 内核的编译5 2.4 内核的制作6 3 BusyBox的编译6 3.1 BusyBox的下载6 3.2 BusyBox的配置6 3.3 BusyBox的编译7 4 Linux文件系统的制作7 4.1 文件系统的制作7 4.2 文件系统的配置9 4.3 文件系统的压缩7 5 Linux引导程序配置10 5.1 ISOLINUX的下载10 5.2 ISOLINUX的配置10 6 LinuxCD-ROM的制作10 7 Linux定制系统的运行11 7.1 VirtualBox下的运行11 7.2 U盘引导在X86平台下的运行12 8定制系统过程中的问题12 8.1 平台搭建中的问题12 8.2 内核编译中的问题12

8.3 BusyBox编译中的问题12 8.4 文件系统制作中的问题12 8.5 引导程序制作中的问题12 8.6 CD-ROM制作中的问题13 8.7 定制系统运行的问题13 参考13 基于Linux内核定制X86平台的微操作系统 王林强 (河南大学物理与电子学院通信专业,河南开封,475004) 摘要: Linux是一种自由和开放,用C语言和汇编语言写成,并符合POSIX标准的类Unix操作系统。并且由于其可定制、可裁剪的特性,不仅在桌面操作系统中有重要的地位,而且在手机、平板电脑、路由器和视频游戏控制台等嵌入式设备有其巨大的优势。 为了更好、更深入的了解及掌握Linux系统。本文详细的讲述并实践,如何从Linux内核源码,经过定制、裁剪、编译、制作文件系统、内核引导,iso光盘制作到最终完整的基于Linux内核定制的微操作系统。 通过基于Linux内核定制的微操作系统的制作,深入的理解Linux内核的工作原理、文件系统结构、内核引导等,从而精通嵌入式开发。 关键词: Linux;定制;嵌入式;微系统 An implementation of micro-operating system based on the x86 platform Linux kernel customization Wang Lin-qiang (School of Physics and Electronics, Henan University, Henan Kaifeng 475004, China) Abstract: Linux is a free and open, and POSIX-compliant Unix-like operating system written in C and assembly language. And can be cut because of its customizable features, not only in the desktop o perating system in an important position, and its huge advantage in the embedded devices, mobile phones, tablet PCs, routers, and video game consoles. In order to better and deeper understanding of and master Linux system. This article tells in d etail and practice, from the Linux kernel source code has been customized, cutting, compiling, pro

关于嵌入式Linux操作系统的内核调试技术详解

关于嵌入式Linux操作系统的内核调试技术详解 近年处理器技术发展速度加快,嵌入式领域发生了翻天覆地的变化。特别是网络的普及,消费电子异军突起,嵌入式与互联网成为最热门的技术。在所有操作系统中,Linux是发展很快、应用很广泛的一种操作系统。Linux的开放性以及其他优秀特性使其成为嵌入式系统开发的首选。 总的来说,嵌入式开发所面临的问题主要表现在以下几个方面。 涉及多种CPU 及多种OS嵌入式的CPU或处理器包括MIPS、PPC、ARM,XScale等不同的架构,这些处理器上运行的操作系统也有VxWorks、Linux、C/OS、WinCE等多种。在一个企业之内,可能会同时使用好几种处理器,甚至几种嵌入式操作系统。如果需要同时调试多种类型的电路板,那复杂性是可想而知的。这也是我们选用瑞士Abatron公司的BDI2000的原因之一,它是一款功能强大的JTAG/BDM通用仿真器。它支持:PPC/MIPS/ARM/XSCALE/ CPU12/CPU32/M-CORE/ColdFire等多种处理器,支持Windows/Linux系统平台,以及多种第三方调试器,并且对Flash的烧写也很简单方便。开发工具种类繁多通常各种操作系统有各自的开发工具,在同一系统下开发的不同阶段也会应用不同的开发工具。如在用户的目标板开发初期,需要硬件仿真器来调试硬件系统和基本的引导程序,然后进行操作系统及驱动程序的开发调试。在调试应用程序阶段可以使用交互式的开发环境进行软件调试,在测试阶段需要一些专门的测试工具软件进行功能和性能的测试。在生产阶段需要固化程序及出厂检测等等。BDI2000可以适应开发的各个阶段,节约企业的支出和简化管理难度。 对目标系统的观察和控制由于嵌入式硬件系统千差万别,软件模块和系统资源也多种多样,要使系统能正常工作,软件开发者必须要对目标系统具有完全的观察和控制能力,例如硬件的各种寄存器、内存空间、操作系统的信号量、消息队列、任务、堆栈等。 此外,嵌入式系统变化更新比较快,对开发时间要求比较紧,需要一套功能强大的嵌入式软件集成开发工具,用于嵌入式软件开发的各个阶段。美国UlTImate SoluTIon公司的LinuxScope-JTD调试器是一个很好的选择,它专门用于配合BDI2000仿真器,特点是基

Linux内核的一些函数--总结笔记

Linux内核的一些函数 1、MKDEV(ma, mi) 构造设备号,将主设备号和次设备号转换为设备号类型(dev_t)。MKDEV宏将主设备号(ma)左移20位,然后与次设备号(mi)相与,得到设备号。 dev_t结构 主设备号12位次设备号20位 2、int register_chrdev_region(dev_t from, unsigned count, const char *name) 静态分配设备号,成功时返回0;错误时返回一个负的错误码,并且不能为字符设备分配设备号。 from是要分配的设备号范围的起始值,一般只提供from的主设备号,次设备号通常被设置为0。count是需要申请的连续设备号的个数。name是和该范围编号有关的设备名称,该名称不能超过64字节。 3、void unregister_chrdev_region(dev_t from, unsigned count) 释放申请的设备号。from表示要释放的设备号,count表示从from开始要释放的设备号个数。通常,在模块卸载函数中调用unregister_chrdev_region()函数。4、static inline void *kmalloc(size_t size, gfp_t flags) 在物理内存中为程序分配一个连续的存储空间。这个存储空间的数据不会被清零,也就是保存内存中原有的数据。 size表示分配内存的大小,flags是分配标志,可以通过这个标志控制kmalloc()函数的多种分配方式。常用的两种分配标志GFP_KERNEL,GFP_ATOMIC。GFP_KERNEL:内存分配时最常用的方法。当内存不足时,可能会引起休眠;GFP_ATOMIC:在不允许睡眠的进程中使用,不会引起睡眠。 5、void cdev_init(struct cdev *cdev, struct file_operations *fops) 静态初始化cdev设备。将设备结构体与file_operations指针相关联。事例如下:cdev_init(&my_cdev->cdev, &dsp2arm_fops); // 初始化cedv设备 my_cdev->cdev.owner = THIS_MODULE; // 使驱动程序属于该模块 my_cdev->cdev.ops = &dap2arm_fops; // cdev连接file_operations指针 6、int cdev_add(struct cdev *p, dev_t dev, unsigned count)

Linux内核调试技术——进程上下文R状态死锁监测

Linux内核调试技术——进程上下文R 状态死锁监测 一、lockup detector机制分析 lockup detector机制在内核代码的kernel/watchdog.c中实现,本文以Linux 4.1.15版本源码为例进行分析。首先了解其背后的设计原理:利用进程上下文、中断、nmi中断的不同优先级实现死锁监测。它们3者的优先级关系为“进程上下文< 中断< nmi中断”,其中进程上下文优先级最低,可通过中断来进行监测进程的运行状态,nmi中断的优先级最高,它是一种不可屏蔽的中断,在中断上下文中发生死锁时,nmi中断处理也可正常进入,因此可用来监测中断中的死锁。不过可惜的是目前绝大多数的arm32芯片都不支持nmi中断,也包括我手中树莓派的bcm2835芯片。从程序的命名中就可以看出,该程序其实实现了一种软看门狗的功能,下面给出整体的软件流程框图: 该程序为每个cpu创建了一个进程和一个高精度定时器,其中进程用来喂狗,定时器用来唤醒喂狗进程和检测是否存在死锁进程,在检测到死锁进程后就触发报警,接下来详细分析源代码: [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void __init lockup_detector_init(void) { set_sample_period(); if (watchdog_enabled) watchdog_enable_all_cpus(); } 首先入口函数lockup_detector_init(),该函数会在内核启动流程中按如下路径调用:start_kernel() --> rest_init() --> kernel_init()(启内核线程)--> kernel_init_freeable() --> lockup_detector_init()。该函数首先计算高精度定时器的到期时间(即喂狗时间),该值为监测超时时间值的1/5,默认为4s(20s/5),然后判断开关标识来确定是否启用监测机制,该标识在没有启用hard lockup detect的情况下默认为SOFT_WATCHDOG_ENABLED,表示开

相关主题
文本预览
相关文档 最新文档