1 基本概念
1.1. 进程和线程
可执行文件由指令和数据组成。进程就是在计算机上运行的可执行文件针对特定的输入数据的一个实例,同一个可执行程序文件如果操作不同的输入数据就是两个不同的进程。
线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享其所附属的进程的所有的资源,包括打开的文件、页表(因此也就共享整个用户态地址空间)、信号标识及动态分配的内存等等。线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一物理内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。
Linux在核外采用1:1线程模型,即用一个核心进程(轻量进程)对应一个线程,把线程调度等同于进程调度,交给核心完成,而其它诸如线程取消、线程间的同步等工作,都是在核外线程库中完成的。因此可以把进程看作一组线程,这组线程拥有相同的线程组号(TGID),这个TGID就是这组线程序所附属的进程的ID号,每个线程的ID号就是我们用ps命令所看到的LWP号。
为了方便,从现在起我们用任务来代替进程和线程,即每提到任务,我们就是指线程和进程,除非要强调线程和进程之间的不同之处。任务的周期从被fork开始一直到给任务从进程表中消失。一个进程包括:正文段(text),数据段(data),栈段(STACK)和共享内存段(SHARED MEMORY)。
1.2. 中断和信号
(1)中断
中断通常定义为用来改变CPU执行的指令的顺序的事件。对于其分类可谓是仁者见仁,智者见智,但毕竟有胜于无,这里给出一种分类:硬中断、异常中断和软中断。硬中断也称为外部中断,分为两类,可屏蔽和不可屏蔽。/proc/interrupts列出了当前系统定义的所有硬中断。
异常中断是系统运行出现异常时候CPU自动产生的中断,如除数为零、使用虚拟内存机制时的缺页保护异常等。软中断是由程序指令中包含INT 指令产生的中断,如单步跟棕;该中断处理的一部分任务可以延迟一会再处理。
1.3. CPU 的状态
CPU的状态可分为有7种:
(1)常规用户态(目态): CPU所执行的任务在访问该任务自己的内存空间;
(2)核心态(管态):如果CPU正在运行核心程序或在CPU上运行的任务正在通过系统调用请求内核服务,例如访问硬件,这时就称CPU处于核心态;
(3)CPU运行nice任务,一个优先级别低于普通任务的优先级别的任务;
(4)io等待: 由于任务等待I/O而使CPU处于空闲状态,这些I/O主要指block I/O,raw I/O,VM paging/swapins;
(5)闲置:系统中的所有任务由于等待除了I/O以外的事件的发生而处于睡眠状态,或者系统没有任务;
(6)CPU正在处理硬中断,即 在irq状态;
(7)CPU正在处理软中断。
从用户态转换为核心态的唯一途径是中断。CPU处于用户态时,所运行的程序只能执行非特权指令,如果用户程序在用户态下执行特权指令,把发生中断,由操作系统获得控制。操作系统在核心态下运行。从核心态到用户态可以通过修改程序状态字来实现,这把伴随这由操作系统程序到用户程序的转换。
1.4. 任务优先级
每个任务在Kernel2.6 中不是由调度器统一计算,而是独立计算。优先级由两部分构成:
(1)静态优先级。Nice是进程的静态优先级。静态优先级在任务创建的时候就被赋值,并且不变(除非用系统调用改变任务的nice值);
(2)动态优先级(task->counter-MAX_RT_PRIO)。它定义了一个在就绪队列的进程当它得到CPU后可运行的时间。计算机是以时钟中断作为时间的计数器,每发送一个时钟中断,动态优先级上的时间片就减少一个时钟中断的时间,时间片减到0的时候就退出该进程而执行另一个进程。任务的动态优先级则是跟静态优先级和平均等待时间(sleep_avg)有关。对于实时任务的优先级在创建的时候就确定了,而且一旦确定以后就不再改变,所以下面部分仅对于非实时任务而言,任务的平均等待时间越大,任务的动态优先级也就越高。
有以下几种情况需要计算任务的优先级:
(1)创建新任务,使用函数effective_prio()(因为此时任务尚未进行调度,没有sleep_avg和interactive_credit可言);
(2)唤醒等待任务时,使用函数recalc_task_prio ()来计算任务动态优先级。
(3)任务用完时间片以后,被重新插入到active array或者expired array的时候需要
(4)其它情况,如IDLE任务初始化等时候。
动态优先级的计算公式为:PRI=NICE+40+CPU_ PENALTY,从公式中可以看到大多数用户任务的优先级是大于40的。可以使用“ps -l”和“ps -emo THREAD”命令分别查寻任务和线程的CPU使用状态。使用“nice -n proname”和“renice +n proid”来修改任务的优先级。nice值的系统缺值为20。
|
1.5. CPU队列长度
一个任务如果拥有了除CPU以外的所有运行时所需要的资源,我们就称该任务为可运行任务。可运行任务包含等待队列和正在CPU上运行的任务,这些任务构成了运行队列。运行队列长度为任务的个数,运行队列越长,任务等待时间越长。
一个阻塞任务可能在等待I/O数据或等待一个系统调用的结果。当一个任务即把进入运行队列时,内核首先计算其优先权,然后再放入相应的优先级的运行队列里;在运行过程中,可运行的任务的优先级每秒更新一次,因此其在可运行队列的位置可动态调整。
1.6. 上下文切换的比率
CPU一般在某一时刻只能运行一个任务。为了给用户一个并行的感觉,Linux内核不停在各个任务之间切换,这个切换叫做上下文切换。上下文包括:CPU的所有寄存器中的值、任务的状态以及堆栈中的内容。上下文切换的主要任务是保存老任务CPU状态,并加载新任务的保存状态,用新任务的内存映像替换老任务的内存映像。因此上下文切换导致大量信息的转移,导致了昂贵的上下文切换开销。因此,要尽可能减少该切换。
要减少切换,必须知道切换怎么样发生。在下列情况下发生上下文切换:
任务结束;
任务使用完时间片,为使各个任务能公平地使用CPU,内核通过时间中断来实现调度,不能的体系结构以及不同的内核,每秒时间中断的次数不一样;
任务需要的资源当前不可用(如缺页)或任务等待I/O操作的完成;
当睡眠任务被唤醒进入可运行队列时,如果该任务的优先级高于所有可运行的任务而且正在运行的任务可被抢占;
任务利用信号或系统调用自动放弃CPU;

