linux内核中的信号机制--信号处理
Kernel version:2.6.14
CPU architecture:ARM920T
Author:ce123(http://blog.csdn.net/ce123)
当进程被调度时,会调用do_notify_resume()来处理信号队列中的信号。信号处理主要就是调用sighand_struct结构中对应的信号处理函数。do_notify_resume()(arch/arm/kernel/signal.c)函数的定义如下:
asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) { if (thread_flags & _TIF_SIGPENDING) do_signal(¤t->blocked, regs, syscall); }_TIF_SIGPENDING标志是在signal_wake_up()函数中设置的,检查该标志后,接下来就调用do_signal()函数,我们来看看do_signal()(arch/arm/kernel/signal.c)的具体定义:
/* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. * * Note that we go through the signals twice: once to check the signals that * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) { struct k_sigaction ka; siginfo_t info; int signr; /* * We want the common case to go fast, which * is why we may in certain cases get here from * kernel mode. Just return without doing anything * if so. */ if (!user_mode(regs))//regs保存的是进入内核态之前的寄存器现场,必须为用户模式,否则直接返回 return 0; if (try_to_freeze()) goto no_signal; if (current->ptrace & PT_SINGLESTEP) ptrace_cancel_bpt(current);//和调试相关,我们在后面的文章中会具体分析 signr = get_signal_to_deliver(&info, &ka, regs, NULL);//取出等待处理的信号 if (signr > 0) { handle_signal(signr, &ka, &info, oldset, regs, syscall);//处理信号 if (current->ptrace & PT_SINGLESTEP) ptrace_set_bpt(current); return 1; } no_signal: /* * No signal to deliver to the process - restart the syscall. */ if (syscall) { if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) { if (thumb_mode(regs)) { regs->ARM_r7 = __NR_restart_syscall; regs->ARM_pc -= 2; } else { u32 __user *usp; regs->ARM_sp -= 12; usp = (u32 __user *)regs->ARM_sp; put_user(regs->ARM_pc, &usp[0]); /* swi __NR_restart_syscall */ put_user(0xef000000 | __NR_restart_syscall, &usp[1]); /* ldr pc, [sp], #12 */ put_user(0xe49df00c, &usp[2]); flush_icache_range((unsigned long)usp, (unsigned long)(usp + 3)); regs->ARM_pc = regs->ARM_sp + 4; } } if (regs->ARM_r0 == -ERESTARTNOHAND || regs->ARM_r0 == -ERESTARTSYS || regs->ARM_r0 == -ERESTARTNOINTR) { restart_syscall(regs); } } if (current->ptrace & PT_SINGLESTEP) ptrace_set_bpt(current); return 0; }
执行do_signal()函数时,进程一定处于内核空间,通常进程只有通过中断或者系统调用才能进入内核空间,regs保存着系统调用或者中断时的现场。user_mode()根据regs中的cpsr寄存器来判断是中断现场环境还是用户态环境。如果不是用户态环境,就不对信号进行任何处理,直接从do_signal()函数返回。
如果user_mode()函数发现regs的现场是内核态,那就意味着这不是一次系统调用的返回,也不是一次普通的中断返回,而是一次嵌套中断返回(或者在系统调用过程中发生了中断)。此时大概的执行路径应该是这样的:假设进场现在运行在用户态,此时发生一次中断,进场进入内核态(此时user_mode(regs)返回1,意味着中断现场是用户态。),此后在中断返回前,发生了一个更高优先级的中断,于是CPU开始执行高优先级的处理函数(此时user_mode(regs)返回0,意味着中断现场是在内核态)。当高优先级中断处理结束后,在它返回时,是不应该处理信号的,因为信号的优先级比中断的优先级低。在这种情况下,对信号的处理将会延迟到低优先级中断处理结束之后。相对于Windows内核来说,尽管linux内核中没有一组显式的操作函数来实现这一系列的优先级管理方案,但是linux内核和Windows内核都使用了同样的机制,优先级关系为:高优先级中断->低优先级中断->软中断(类似Windows内中的DPC)->信号(类似Windows内核中的APC)->进程运行。
如果user_mode(regs)返回1,接下来会执行(中间略去一下和本文关系不大的代码)get_signal_to_deliver(),这个函数从当前进程的信号队列(保存Private Signal Queue和Shared Signal Queue)取出等待处理的信号(调用dequeue_signal()函数),然后根据信号定位到对应的signal_struct结构,如果信号的处理函数sa_handler为SIG_IGN,就忽略该信号,继续取下一个信号;如果信号的处理函数sa_handler为SIG_DFL,意味着按照信号默认的处理方式对待就可以了(例如直接调用do_coredump()等)。
如果get_signal_to_deliver()函数返回值大于0,说明这个信号的处理函数是在用户态空间(通过signal()和sigaction()等函数设置的自定义信号处理函数。),将调用handle_signal()函数进行处理。handle_signal()函数的定义如下:
/* * OK, we're invoking a handler */ static void handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, int syscall) { struct thread_info *thread = current_thread_info(); struct task_struct *tsk = current; int usig = sig; int ret; /* * If we were from a system call, check for system call restarting... */ if (syscall) { switch (regs->ARM_r0) { case -ERESTART_RESTARTBLOCK: case -ERESTARTNOHAND: regs->ARM_r0 = -EINTR; break; case -ERESTARTSYS: if (!(ka->sa.sa_flags & SA_RESTART)) { regs->ARM_r0 = -EINTR; break; } /* fallthrough */ case -ERESTARTNOINTR: restart_syscall(regs); } } /* * translate the signal */ if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) usig = thread->exec_domain->signal_invmap[usig]; /* * Set up the stack frame//设置栈帧 */ if (ka->sa.sa_flags & SA_SIGINFO) ret = setup_rt_frame(usig, ka, info, oldset, regs); else ret = setup_frame(usig, ka, oldset, regs); /* * Check that the resulting registers are actually sane. */ ret |= !valid_user_regs(regs); /* * Block the signal if we were unsuccessful. */ if (ret != 0) { spin_lock_irq(&tsk->sighand->siglock); sigorsets(&tsk->blocked, &tsk->blocked, &ka->sa.sa_mask); if (!(ka->sa.sa_flags & SA_NODEFER)) sigaddset(&tsk->blocked,
1。按F8进入安全模式
2。运行cmd.exe
3。运用net user username password即可更改
Working size超过可用内存
Working Size怎么理解?肯定不是指数据库的大小,应该是在保证业务指标——响应时间、QPS的情况下,数据库使用的内存大小。其超过可用内存后的直接影响就是系统开始使用“swap”,从而大大降低DB的性能。所以,DB服务器要有充足的内存。
长查询和短查询
指运行时间很长和很短的查询。运行时间很长的查询,要是么很消耗内存、CPU,比如联合查询,要么是很消耗磁盘I/O,比如没有用到索引的“遍历”——这应该算是“事故”。长查询对“DB性能”的影响是显而易见的。而短查询呢?
写冲突(需要用锁的场景)
写冲突的场景,通常会遇到“锁”,如MyISA的“表锁”,与InnoDB的“行锁”,当遇上“锁”时,只能有一个“用户”在写,其他用户均需要等待。当有大量的冲突时,用户需要等待的时间就越长,“锁”机制会导致CPU的有效利用率大大下降——花在“获取锁”上的CPU时间变多,从而导致DB可用的有效CPU时间变少,性能下降。
大量的Join操作消耗内存
Join操作本身比较消耗内存和CPU,尽可能不用或少用。
2 虚拟化
共享磁盘,磁盘随机读严重
虚拟化场景,使用共享磁盘(HDD),磁盘会成为瓶颈。磁盘差不多是计算机上最慢的设备了。随机I/O能力极差。
网络I/O波动
因为虚拟化场景同一台物理机上的多个VM之间的网络是共享的,会互相影响。
3 程序
线程:死锁,与“事件驱动型”(方案)相比过重,debug,非线性扩展,等等
事件驱动编程:回调的复杂度,如何保存函数状态,等等
缺乏“profiling”、跟踪、日志机制。
耦合严重,单独故障,不可水平扩展,等等
有状态的应用(不易水平扩展)
糟糕的设计:开发都开发了一个程序,在自己的电脑上运行良好;到了生产环境,对于两三个用户,也运行良好。等到数月、数年过后,用户量上来以后,程序不能运行了,需要重构、重写。
算法复杂性
依赖其他服务,比如DNS查询以及其它,你可会被阻塞。
栈空间
4 磁盘
本地磁盘访问
随机磁盘I/O(引发大量磁盘寻道)
磁盘碎片(增加寻道机会和时间)
当写入数据量超过SSD空间量以后,SSD性能的下降
5 操作系统
Fsync flushing, linux buffer cache filling up
TCP Buffer过小
文件句柄限制
功率分配(CPU节能?)
6 缓存
不使用Memcached(数据库前端)
HTTP:headers,etags,不压缩,等等
不充分利用浏览器的缓存
字节码缓存(比如PHP的APC)
处理器的L1/L2缓存:这是一个重要的“瓶颈”。保持重要的热数据在L1/L2缓存中。这个关系到方方面面太广,
7 处理器
CPU超载
上下文切换->一个核上太多线程,这可能是因为“坏运气”或者是Linux调度器;太多系统调用,等等
I/O等待->所有CPU以相同的速度在等待(同时等待)
CPU缓存:未完……
主板能力(其它限制CPU性能的因素)
8 网络
网卡最大带宽,IRQ饱和,软中断用光CPU资源
DNS查询
丢包
意外路由
网络磁盘访问(比如NFS, drbd)
共享SANs
服务故障->服务不响应
9 进程
测试时间
程序调试时间
团队大小
预算
代码“欠债”
10 内存
OOM->杀进程,使用交换甚至导致死机
OOM导致磁盘超负荷(因为swap)
内存管理库极限
内存碎片
Java中会导致GC停顿,速度变慢
C中,导致malloc()分配内存变慢