进程的不同状态
进程在其生命周期内,由于系统中各个进程的相互制约关系及系统运行环境的变化,使得进程的状态也在不断地发生变化。这些状态有:
- 运行态:进程在处理机上运行。
- 就绪态:进程已处于准备运行的状态。此时进程获得了除处理机以外的全部资源。
- 阻塞态:进程正在等待某一事件而暂停运行。
- 创建态:进程正在被创建,尚未转到就绪态。
- 结束态:进程正从系统中消失。
一个进程从运行态变成阻塞态是主动行为(进程请求某一资源的使用或等待某一事件的发生),从阻塞态变成就绪态是被动行为(进程等待的事件到来)。
进程的创建
进程的创建分以下的步骤进行:
- 接受参数:如程序程序初始优先级、可执行程序及输入参数等。
- 申请PCB空间和PID,初始化PCB。
- 设置进程地址空间的相关数据结构,建立代码段、数据段、用户栈等。
- 初始化进程现场。
- 将进程设置为就绪态。
Linux 0.11中进程的创建
在Linux 0.11中,创建进程是由fork系统调用实现的。
sys_fork:
call find_empty_process
testl %eax,%eax
js 1f
push %gs
pushl %esi
pushl %edi
pushl %ebp
pushl %eax
call copy_process
addl $20,%esp
1: ret
- 查找空进程,find_empty_process
- 申请并设置新进程控制块
- 内存拷贝
- 修改GDT
第2,3,4步在copy_process可体现:
int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
long ebx,long ecx,long edx,
long fs,long es,long ds,
long eip,long cs,long eflags,long esp,long ss)
{
struct task_struct *p;
int i;
struct file *f;
p = (struct task_struct *) get_free_page();/*申请新的进程控制块*/
if (!p)
return -EAGAIN;
task[nr] = p;
__asm__ volatile ("cld");
*p = *current; /* 设置新进程的地址空间 */
/*设置新的进程控制块*/
p->state = TASK_UNINTERRUPTIBLE; /*不可中断睡眠态*/
p->pid = last_pid;
p->father = current->pid;
p->counter = p->priority;
p->signal = 0;
p->alarm = 0;
p->leader = 0; /* process leadership doesn't inherit */
p->utime = p->stime = 0;
p->cutime = p->cstime = 0;
p->start_time = jiffies;
p->tss.back_link = 0;
/*核心栈,esp0正好指向该页的顶端*/
p->tss.esp0 = PAGE_SIZE + (long) p;
p->tss.ss0 = 0x10;
p->tss.eip = eip;
p->tss.eflags = eflags;
p->tss.eax = 0;
p->tss.ecx = ecx;
p->tss.edx = edx;
p->tss.ebx = ebx;
p->tss.esp = esp;
p->tss.ebp = ebp;
p->tss.esi = esi;
p->tss.edi = edi;
p->tss.es = es & 0xffff;
p->tss.cs = cs & 0xffff;
p->tss.ss = ss & 0xffff;
p->tss.ds = ds & 0xffff;
p->tss.fs = fs & 0xffff;
p->tss.gs = gs & 0xffff;
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;
if (last_task_used_math == current)
__asm__("clts ; fnsave %0"::"m" (p->tss.i387));
if (copy_mem(nr,p)) {
task[nr] = NULL;
free_page((long) p);
return -EAGAIN;
}
for (i=0; i<NR_OPEN;i++)
if (f=p->filp[i])
f->f_count++;
if (current->pwd)
current->pwd->i_count++;
if (current->root)
current->root->i_count++;
if (current->executable)
current->executable->i_count++;
/*修改GDT*/
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING; /* do this last, just in case */
return last_pid;
}
在Linux 0.11中,创建进程后,系统没有急着给进程分配系统的空间,而是采取写时复制的策略,这样加大了内存空间的利用率。
进程调度算法
进程调度是内核决定将CPU分配给某个就绪进程的过程。
有一些典型的进程调度算法
先来先服务调度算法(FCFS)
这个算法的基本思想就是按照进程的就绪次序来调度进程。(非剥夺调度)
这个算法相对简单,且对所有的进程都比较公平。但如果有一个长进程先于一些短进程到达,就会使得短进程等待很长的时间,因此,这种算法不适合在交互环境中使用。
时间片轮转调度算法
这个算法的基本思想是:赋给每个进程一个CPU时间片,进程轮流占用CPU,当时间片用完时,释放CPU。
和FCFS算法不同,这个算法适合在交互环境中使用。
这个算法保证了每个进程的相对公平,每个进程都有均等的机会运行,同时改善了短进程的响应时间,提高了系统的吞吐量。
优先级调度算法
这个算法的基本思想是:赋给每个进程一个优先级,优先级最高的进程占用CPU。
其中,优先级可以是静态的,也可以是动态的;这个调度算法可以是剥夺的,也可以是非剥夺的。
这个算法的优点是:调度灵活,可适应多种调度需求。
缺点是:低优先级进程可能会一直得不到CPU。
在Linux 0.11中的进程调度算法是综合了时间片轮转调度算法和优先级调度算法实现的。
进程调度的时机
- 当前进程主动放弃处理机,如进程结束、进程被阻塞或挂起、进程执行“自愿放弃”的系统调用。
- 当时间片用完。
- 当更高优先级的进程出现时。
- 系统调用返回前的延时调度。
进程切换
进程切换往往在调度完成后立即发生,进程切换时,会保存原进程的执行现场,恢复被调度进程的执行现场。
为了进行进程现场切换,内核将原进程的执行现场(PC等一些寄存器的内容、堆栈指针、进程存储空间的指针)压入当前进程的核心栈中,并更新堆栈指针。内核从新进程的核心栈中装入新进程的现场信息,更新当前进程的存储空间的指针,重新设置PC,控制便转到新进程,新进程开始运行。