kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

grub修复

http://www.centoscn.com/CentosBug/osbug/2014/0327/2671.html

grub全称在为GRand Unified Bootloader,它的核心功能是引导内核,但是如果grub出了问题,内核无法找到,那岂不是万劫不复了,下面就介绍一下常用的修复方式。

第一种情况:

是由于grub中的grub.conf文件损坏,开机后直接进入到了grub>命令行模式下。下面将图解此过程

这时可以使用help看一下grub可支持命令有那些,以便供修复时使用。

第二个使用的命令是find (hd0,0)/按tab如果能补全就表示系统在此分区上。

各个参数说明:

这时要注意,当你指定内核后,但未指定内核后面的参数(ro root=(此处未指定的话))将无法正常启动,报:请给root参数,一般情况下是系统是可以自动探测到,但这个功能并不靠谱,那么只能靠备份或你的记忆将参数补上(所以定期备份/etc/fstab、与grub.conf、是多么重要的事情,原因你懂的。)

如下图:

而这时就看到你平时的习惯了,备份相当重要

当正常登录系统后,将grub.conf文件重新写就可以了。(上图的完整路径是root=/dev/mapper/vg_www-lv_root,写全了就看不到了,所以在此特别说明)

看到title了吧

过了下面这张图就说明系统是可以正常启动了

第一种情况顺利解决!a_c

第二种情况:

grub损坏(最明显的提示为:Operating System not found)

如mbr数据损坏(注仅是bootloader损坏,分区表是好的),如果没有重新启动还可能修复,但是如果是重启后发现grub损坏,那么只能挂载光盘进入紧急救援模式。(以下将以挂载光盘说明)

dd执行之后的景象,是不是好惊悚a_c

挂载光盘进入紧急救援模式,在BIOS中将光盘设置为第一引导设备。

在菜单中选择"Rescue installed system"

之后将对:语言—-》键盘设置

是否启用网络(不需要,则No,如果选择了Yes将要求选择获取IP地址的方式)

正式进入救援模式

原系统己经挂载的位置,如何切换到原系统下

开启一个shell

切换到原系统

这时可以直接输入grub命令进入grub中(这个grub是光盘中的)

直接使用 help setup会显示setup的使用方法。

设置root(root默认分区)如(hd0,0),此分区一定要root所在的系统分区,之后使用setup安装,命令是setup(hd0)(由于mbr并属于分区,所以将grub安装到hd0设备即可),如果是成功了会有succeeded提示。quit退出即可

重启系统,取出光盘,有如下信息就表示修复完成

如果grub目录都损坏,无法正常启动。则可以在此模式使用grub-install –root-directory=/ /dev/sda(设备是什么就写什么)手写配置文件grub.conf即可

linux软中断机制分析

软中断分析

1. 为什么要软中断

编写驱动的时候,一个中断产生之后,内核在中断处理函数中可能需要完成很多工作。但是中断处理函数的处理是关闭了中断的。也就是说在响应中断时,系统不能再次响应外部的其它中断。这样的后果会造成有可能丢失外部中断。于是,linux内核设计出了一种架构,中断函数需要处理的任务分为两部分,一部分在中断处理函数中执行,这时系统关闭中断。另外一部分在软件中断中执行,这个时候开启中断,系统可以响应外部中断。

关于软件中断的理论各种书籍都有介绍,不多叙述。而要真正体会软件中断的作用就必须从代码的角度来分析。我们做工作时候讲求的是professional,当一个人在某个领域一无所知的时候,我们称他为小白,偶,非苹果电脑。小白的脑子里充满了各种问题。慢慢的当这些疑惑解释完之后,小白就脱白了。此时,我们对这个领域的基本框架有了解,但这和professional还有一定的差距。再加以时日,逐渐融会贯通该领域才能达到专业的境界。

2. 什么时候触发处理软件中断

说了这么多废话,赶快步入正题。初识软中断,脑子里肯定有不少的疑问,首先就是软件中断在什么地方被触发处理?这个问题的答案就是:一个硬件中断处理完成之后。下面的函数在处理完硬件中断之后推出中断处理函数,在irq_exit中会触发软件中断的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);
 
	irq_enter();
 
	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */ 
	if (irq >= NR_IRQS)
	    handle_bad_irq(irq, &bad_irq_desc);
	else 
	    generic_handle_irq(irq);
 
	/* AT91 specific workaround */ 
	irq_finish(irq);
 
	irq_exit();
	set_irq_regs(old_regs);
}

这里要注意,invoke_softirq必须满足两个条件才能被调用到,一个就是不是在硬件中断处理过程中或者在软件中断处理中,第二个就是必须有软件中断处于pending状态。第二个好理解,有软件中断产生才去处理,没有就不处理。第一个就不好理解了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
* Exit an interrupt context. Process softirqs if needed and possible:
*/ 
void irq_exit(void)
{
	account_system_vtime(current);
	trace_hardirq_exit();
	sub_preempt_count(IRQ_EXIT_OFFSET);
	if (!in_interrupt() && local_softirq_pending())
	    invoke_softirq();
 
#ifdef CONFIG_NO_HZ
	/* Make sure that timer wheel updates are propagated */ 
	rcu_irq_exit();
	if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
	    tick_nohz_stop_sched_tick(0);
#endif 
	preempt_enable_no_resched();
}

在linux系统的进程数据结构里,有这么一个数据结构

1
#define preempt_count() (current_thread_info()->preempt_count),

利用preempt_count可以表示是否处于中断处理或者软件中断处理过程中。

1
2
3
4
5
6
7
8
9
10
11
12
13
#define PREEMPT_MASK    (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
#define SOFTIRQ_MASK    (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
#define HARDIRQ_MASK    (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
 
#define PREEMPT_OFFSET    (1UL << PREEMPT_SHIFT)
#define SOFTIRQ_OFFSET    (1UL << SOFTIRQ_SHIFT)
#define HARDIRQ_OFFSET    (1UL << HARDIRQ_SHIFT)

sub_preempt_count(IRQ_EXIT_OFFSET);

#define in_interrupt() (irq_count())

#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK))

preempt_count的8~23位记录中断处理和软件中断处理过程的计数。如果有计数,表示系统在硬件中断或者软件中断处理过程中。系统这么设计是为了避免软件中断在中断嵌套中被调用,并且达到在单个CPU上软件中断不能被重入的目的。对于ARM架构的CPU不存在中断嵌套中调用软件中断的问题,因为ARM架构的CPU在处理硬件中断的过程中是关闭掉中断的。只有在进入了软中断处理过程中之后才会开启硬件中断,如果在软件中断处理过程中有硬件中断嵌套,也不会再次调用软中断,because硬件中断是软件中断处理过程中再次进入的,此时preempt_count已经记录了软件中断!对于其它架构的CPU,有可能在触发调用软件中断前,也就是还在处理硬件中断的时候,就已经开启了硬件中断,可能会发生中断嵌套,在中断嵌套中是不允许调用软件中断处理的。Why?我的理解是,在发生中断嵌套的时候,表明这个时候是系统突发繁忙的时候,内核第一要务就是赶紧把中断中的事情处理完成,退出中断嵌套。避免多次嵌套,哪里有时间处理软件中断,所以把软件中断推迟到了所有中断处理完成的时候才能触发软件中断。

3. 软件中断的处理过程

之前我已经说到,软中断的一个很大的目的就是避免中断处理中,处理的操作过多而丢失中断。同时中断还需要考虑到一件事情就是中断处理过程过长就会影响系统响应时间。如果一个中断处理一秒钟,那你一定能感受到串口卡住的现象。从另外一方面说呢,我们又必须考虑中断处理的操作一定的优先度,毕竟是硬件触发的事务,关系到网络、块设备的效率问题。Linux内核就中断方面就必须考虑平衡这三个方面的问题。而下面我要分析的__do_softirq函数就恰似在这三者之间打太极,游刃有余,面面俱到!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
* We restart softirq processing MAX_SOFTIRQ_RESTART times,
* and we fall back to softirqd after that.
*
* This number has been established via experimentation.
* The two things to balance is latency against fairness -
* we want to handle softirqs as soon as possible, but they
* should not be able to lock up the box.
*/ 
#define MAX_SOFTIRQ_RESTART 10 
 
asmlinkage void __do_softirq(void)
{
	struct softirq_action *h;
	__u32 pending;
	int max_restart = MAX_SOFTIRQ_RESTART;
	int cpu;
 
	pending = local_softirq_pending();
	account_system_vtime(current);
 
	__local_bh_disable((unsigned long)__builtin_return_address(0));
	trace_softirq_enter();
 
	cpu = smp_processor_id();
restart:
	/* Reset the pending bitmask before enabling irqs */ 
	set_softirq_pending(0);
 
	local_irq_enable();
 
	h = softirq_vec;
 
	do 
	{
	    if (pending & 1)
	    {
	        int prev_count = preempt_count();
 
	        h->action(h);
 
	        if (unlikely(prev_count != preempt_count()))
	        {
	            printk(KERN_ERR "huh, entered softirq %td %p" 
	                   "with preempt_count %08x," 
	                   " exited with %08x?\n", h - softirq_vec,
	                   h->action, prev_count, preempt_count());
	            preempt_count() = prev_count;
	        }
 
	        rcu_bh_qsctr_inc(cpu);
	    }
	    h++;
	    pending >>= 1;
	}
	while (pending);
 
	local_irq_disable();
 
	pending = local_softirq_pending();
	if (pending && --max_restart)
	    goto restart;
 
	if (pending)
	    wakeup_softirqd();
 
	trace_softirq_exit();
 
	account_system_vtime(current);
	_local_bh_enable();
}

__do_softirq函数处理软件中断过程如下图流程分析

  1. 首先调用local_softirq_pending函数取得目前有哪些位存在软件中断

  2. 调用local_bh_disable关闭软中断,其实就是设置正在处理软件中断标记,在同一个CPU上使得不能重入do_softirq函数

  3. 重新设置软中断标记为0,set_softirq_pending重新设置软中断标记为0,这样在之后重新开启中断之后硬件中断中又可以设置软件中断位。

  4. 开启硬件中断

  5. 之后在一个循环中,遍历pending标志的每一位,如果这一位设置就会调用软件中断的处理函数。在这个过程中硬件中断是开启的,随时可以打断软件中断。这样保证硬件中断不会丢失。

  6. 之后关闭硬件中断,查看是否又有软件中断处于pending状态,如果是,并且在本次调用__do_softirq函数过程中没有累计重复进入软件中断处理的次数超过10次,就可以重新调用软件中断处理。如果超过了10次,就调用wakeup_softirqd();唤醒内核的一个进程来处理软件中断。设立10次的限制,也是为了避免影响系统响应时间。

4. 处理软中断内核线程

之前我说到不能让CPU长时间来处理中断事务,这样会影响系统的响应时间,严重影响用户和系统之间的交互式体验。所以在之前的__do_softirq中最多将循环执行10次,那么当执行了10次仍然有软中断在pending状态,这个时候应该怎么处理呢?系统将唤醒一个软件中断处理的内核进程,在内核进程中处理pending中的软件中断。这里要注意,之前我们分析的触发软件中断的位置其实是中断上下文中,而在软中断的内核线程中实际已经是进程的上下文。

这里说的软中断上下文指的就是系统为每个CPU建立的ksoftirqd进程。

看完这个函数,我不得不佩服这个函数设计的精巧!而我更多的从中体会到其中蕴藏的一种做人的道理。那就是做人要霸道一点,太谦和太恭维不行,但是又不能横行霸道,原则的问题要公平讲理,一定的时候顾及别人的利益,好处不能一个人独吞。这就跟下面ksoftirqd处理过程一样,该狠的时候禁止抢占,其它进程别想调度到哦,但是自己占用CPU时间过长的话,也自觉的问一问是不是该释放CPU给其它进程了。

下面我们就来分析一下这个处理过程怎么就体现了上面的这种说法呢?软中断的内核进程中主要有两个大循环,外层的循环处理有软件中断就处理,没有软件中断就休眠。内层的循环处理软件中断,并每循环一次都试探一次是否过长时间占据了CPU,需要调度释放CPU给其它进程。具体的操作在注释中做了解释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
static int ksoftirqd(void *__bind_cpu)
{
	set_current_state(TASK_INTERRUPTIBLE);
 
	while (!kthread_should_stop())
	{
	    /*不管三七二十一首先禁止抢占,我掌握CPU,并全凭我自己掌握调度*/ 
	    preempt_disable();
	    if (!local_softirq_pending())
	    {
	        preempt_enable_no_resched();
	        /*如果没有软中断在pending,那就让出CPU来吧*/ 
	        schedule();
	       /*我被唤醒了,首先掌握CPU,不让自己被抢占,自己决定自己的是否要调度*/ 
	        preempt_disable();
	    }
 
	    __set_current_state(TASK_RUNNING);
 
	    while (local_softirq_pending())
	    {
	        /* Preempt disable stops cpu going offline.
	           If already offline, we'll be on wrong CPU:
	           don't process */ 
	        if (cpu_is_offline((long)__bind_cpu))
	            goto wait_to_die;
	        /*处理软中断*/ 
	        do_softirq();
	        /*虽然我自己掌握是否要调度,虽然我可以一直不调度,但是我是
	        个正直的人,运行一段时间后我会看看是否需要调度,还其它进程运行*/ 
	        preempt_enable_no_resched();
	        cond_resched();
	        preempt_disable();
	        rcu_qsctr_inc((long)__bind_cpu);
	    }
	    preempt_enable();
	    set_current_state(TASK_INTERRUPTIBLE);
	}
	__set_current_state(TASK_RUNNING);
	return 0;
 
wait_to_die:
	preempt_enable();
	/* Wait for kthread_stop */ 
	set_current_state(TASK_INTERRUPTIBLE);
	while (!kthread_should_stop())
	{
	    schedule();
	    set_current_state(TASK_INTERRUPTIBLE);
	}
	__set_current_state(TASK_RUNNING);
	return 0;
}

中断机制

1、

cpu的中断管理和指令执行(运算器)是两套硬件,他们互相独立又有关联。

2、

无论中断是否允许,运算器都按自己的节奏工作,无须花时间去查询是否由中断到达。

3、

中断管理器则不断地探测是否有中断信号到达,若有且中断允许,则保存当前执行状态信息,然后打断当前取指序列,强行转到特定地址(中断向量)取指令,整个过程运算器并不知道,它只是忠实地执行取指电路取得的指令。
因此,只要没有中断信号到达,就不存在cpu边走边看的问题。 为保证正确访问临界数据区和正确执行临界代码段,操作系统一般有:关中断、关调度、信号量,还有些操作系统提供原子变量的方法,linux中广为人知的锁其实是用信号量实现的。那么,这么多的方法中,什么情况适用哪一种方法呢?是有规律的。

1、原子变量

原子变量可以保证一个变量单次操作的正确性,其保护甚至比信号量还完善,信号量只能保护全局数据不被其他线程破坏,而原子变量能保证全局数据不被中断破坏。

1
2
3
4
example1:
	atomic int a;
int b,c;
a = a + b + c;

上述代码中,cpu对a有一个读、修改、写的过程,这个过程如果被打断,并在其他线程中修改了a值,执行结果将出现错误,而原子变量将保证不会发生这样的错误。 原子变量不能保护一系列操作的原子性,若把上述代码改为

1
2
3
4
5
example2:
atomic int a;
int b,c;
a = a + b; //L1
a = a + c; //L2

原子变量不能保证L1和L2两行程序间a不被其他线程修改,因此example2不一定能得到正确的结果。

2、信号量

稍微完善一点的操作系统都提供信号量机制,用于保护临界代码。上述example2就应该用下列代码替代L1和L2:

1
2
3
4
获取信号量;
a = a + b; //L1
a = a + c; //L2
释放信号量;

当一个全局变量可能被多个线程操作时,就应该用信号量保护,注意,不能在中断中访问用信号量保护的变量。

3、关调度

关调度是一种比较粗暴的方式,关调度后,操作系统不会再进行线程上下文切换,而是专心执行一个线程,但是中断仍然开着。关调度可以起到替代信号量保护全局变量的作用,但一般不这样用,太粗暴了。但是如果某一段代码的执行时间有要求,希望cpu全速执行不被打断,但又不希望关中断时,可用关调度的方法。注意,从逻辑上,关调度能替换信号量,但不能替换原子变量。

4、关中断

关中断是最粗暴的一种方式,用于保证最严格的时序执行,比如某段代码要在IO上输出两个高精度脉冲,脉冲宽度2uS,间隔2uS,这种需求只能通过用精确的指令延时来实现,延时过程中,如果被中断,或者发生线程切换,将不能正确输出脉冲。从逻辑上,前面所讲的三种保护,都可以用关中断实现,只是,太粗暴了。