我的GIS/CS学习笔记:https://github.com/yunwei37/ZJU-CS-GIS-ClassNotes
<一个浙江大学本科生的计算机、地理信息科学知识库 >
本章将对进程进行全面的描述。
进程定义:所谓进程是由正文段(Text)、用户数据段(User Segment)以及系统数据段(System Segment)共同组成的一个执行环境。它代表程序的执行过程,是一个动态的实体。
程序定义:程序只是一个普通文件,是一个机器代码指令和数据的集合。程序是一个静态的实体。
进程实体由 3 个独立的部分组成:
Linux 中的每个进程由一个 task_struct 数据结构来描述,任务(Task)和进程(Process)是两个相同的术语,task_struct 其实就是通常所说的“进程控制块”即 PCB。
task_struct 数据结构按其功能可做如下划分:
进程执行时,它会根据具体情况改变状态;
进程调度信息:调度程序利用这部分信息决定系统中哪个进程最应该运行
标识符(Identifiers):每个进程有进程标识符、用户标识符、组标识符
进程通信有关信息(IPC,Inter_Process Communication)
进程链接信息(Links):程序创建的进程具有父/子关系。
时间和定时器信息(Times and Timers):实时定时器、虚拟定时器和概况定时器
虚拟内存信息(Virtual Memory)
页面管理信息:当物理内存不足时,Linux 内存管理子系统需要把内存中的部分页面交换到外存
对称多处理机(SMP)信息、和处理器相关的环境(上下文)信息(Processor Specific Context)
task_struct 结构是进程实体的核心,Linux 内核通过该结构来控制进程:首先通过其中的调度信息决定该进程是否运行;当该进程运行时,根据其中保存的处理机状态信息来恢复进程运行现场,然后根据虚拟内存信息,找到程序的正文和数据;通过其中的通信信息和其他进程实现同步、通信等合作。
task_struct 结构是一个进程存在的唯一标志。
task_struct 结构在内存的存放与内核栈是分不开的。
在/include/linux/sched.h 中定义了如下一个联合结构:
union task_union {
struct task_struct task;
unsigned long stack[2408];
};
进程的 task_struct 结构所占的内存是由内核动态分配的.
当前进程(current 宏):
static inline struct task_struct * get_current(void)
{
struct task_struct *current;
__asm__("andl %%esp,%0; ":"=r" (current) : "0" (~8191UL));
return current;
}
为了对系统中的很多进程及处于不同状态的进程进行管理,Linux 采用了如下几种组织方式:
内核线程(thread)或叫守护进程(daemon)它们周期性地执行,例如,磁盘高速缓存的刷新,网络连接的维护,页面的换入换出等。
Linux 用“权能(capability)”表示一进程所具有的权力。一种权能仅仅是一个标志,它表明是否允许进程执行一个特定的操作或一组特定的操作。
内核同步
信号量:
进程间对共享资源的互斥访问是通过“信号量”机制来实现的。Linux 内核中提供了两个函数 down()和 up(),分别对应于操作系统教科书中的 P、V 操作。
原子操作:
避免干扰的最简单方法就是保证操作的原子性,即操作必须在一条单独的指令内执行。有两种类型的原子操作,即位图操作和数学的加减操作:
自旋锁、读写自旋锁和大读者自旋锁:
本章首先讨论与时间相关的主题,然后才讨论进程的调度,最后介绍了 Linux 中进程是如何进行切换的。
操作系统建立的时间系统是整个操作系统活动的动力。
时间系统通常又被简称为时钟,它的主要任务是维持系统时间并且防止某个进程独占 CPU 及其他资源,也就是驱动进程的调度。
时钟硬件:
两个时钟源:
时钟运作机制:
RTC 和 OS 时钟之间的关系通常也被称作操作系统的时钟运作机制。
一般来说,RTC 是 OS 时钟的时间基准,操作系统通过读取 RTC 来初始化 OS 时钟,此后二者保持同步运行,共同维持着系统时间。在 Linux 中,RTC 处于最底层,提供最原始的时钟数据。OS 时钟建立在 RTC 之上,初始化完成后将完全由操作系统控制,和 RTC 脱离关系。
Linux 时间基准:
不同的操作系统采用不同的“时间基准”。定义“时间基准”的目的是为了简化计算,这样计算机中的时间只要表示为从这个时间基准开始的时钟滴答数就可以了。
Linux 的时间基准是 1970 年 1 月 1 日凌晨 0 点。
Linux 的时间系统:
Linux 中用全局变量 jiffies 表示系统自启动以来的时钟滴答数目。
unsigned long volatile jiffies
inux 的 OS 时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入 CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。系统利用时钟中断维持系统时间、促使环境的切换,以保证所有进程共享 CPU;利用时钟中断进行记帐、监督系统工作以及确定未来的调度优先级等工作。
从总体上浏览一下时钟中断:
调度的实质就是资源的分配。系统通过不同的调度算法(Scheduling Algorithm)来实现这种资源的分配。
一个好的调度算法应当考虑以下几个方面:
主要的调度算法及其基本原理:
Linux 进程调度时机:
每个时钟中断(timer interrupt)发生时,由 3 个函数协同工作,共同完成进程的选择和切换:
进程调度的依据:在每个进程的 task_struct 结构中有如下 5 项
进程可运行程度的衡量:
函数 goodness()就是用来衡量一个处于可运行状态的进程值得运行的程度。该函数综合使用了上面我们提到的 5 项,给每个处于可运行状态的进程赋予一个权值(weight),调度程序以这个权值作为选择进程的唯一依据。
进程调度的实现: void schedule(void)
为了控制进程的执行,内核必须有能力挂起正在 CPU 上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换,任务切换,或上下文切换。
硬件支持:
Intel i386 体系结构包括了一个特殊的段类型,叫任务状态段(TSS)。
每个任务包含有它自己最小长度为 104 字节的 TSS 段,在/include/ i386/processor.h 中定义为 tss_struct 结构;每个 TSS 有它自己 8 字节的任务段描述符。
另外一个数据结构,这就是 thread_struct 结构;
任务门中包含有 TSS 段的选择符。当 CPU 因中断而穿过一个任务门时,就会将任务门中的段选择符自动装入 TR 寄存器,使 TR 指向新的 TSS,并完成任务切换。在 Linux内核中,TSS 并不是属于某个进程的资源,而是全局性的公共资源,只更换 TSS 中的 SS0 和 ESP0,而不更换 TSS 本身,也就是根本不更换 TR 的内容。
进程切换:
前面所介绍的 schedule() 中调用了 switch_to 宏,这个宏实现了进程之间的真正切换。