0%

进程与线程

进程定义

程序段、数据段、PCB组成进程实体,即进程,创建进程即创建PCB,撤销即撤销PCB(进程存在的唯一标志

定义:进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位

进程组成:

 存放性质
PCB进程管理者(操作系统)所需数据进程存在的唯一标志
程序段要执行的程序代码 
数据段执行过程中的各种数据 

进程的状态与转化

基本状态:运行态,就绪态,阻塞态

进程转化的实质就是PCB的修改以及相关环境的改变(如某些寄存器的值的改变)

进程通信

三种方式:共享存储,消息传递(消息队列),管道通信

  • 共享储存:基于数据结构的共享(低级)与基于存储区的共享(高级)

  • 管道通信:半双工通信,同一时间段内只能实现单向的传输(双向通信需要设置两个

    管道)

    当管道满时,写进程的write()被阻塞;

    当管道空时,读进程的read()被阻塞。

    没写满不能读,没空不能写

    由于数据被读出后就被抛弃,所以读进程最多只有一个

  • 消息传递:格式化的消息为单位通过“发送消息/接收消息”两个原语进行数据交换


//中间的调度内容被吃掉了,以后吐出来//


进程调度的时机

主动放弃与被动放弃进程

不能进行进程调度与切换的情况:

1、处理中断

2、在操作系统内核程序临界区中

3、在原子操作过程中

内核程序临界区与临界区并不相同

进程调度方式:1、非剥夺调度方式(非抢占方式)2、剥夺调度方式(抢占方式)

前者实现简单,但无法及时处理紧急任务

进程切换:指一个进程让出处理机,另一个进程占用处理机的过程

狭义的进程调度:从就绪队列中选中一个要运行的进程

广义的进程调度:选择一个进程并进行进程切换

进程切换过程:1、对原来运行进程各种数据保存

2、对新的进程各种数据的恢复

过于频繁的进程切换会导致系统的效率降低

调度算法的评价指标

  • CPU利用率=忙碌的时间/总时间
  • 系统吞吐量(单位时间完成作业的数量)=总共完成了多少道作业/总共花了多少时间
  • 周转时间=作业完成时间-作业提交时间
  • 平均周转时间=各作业周转时间之和/作业数
  • 带权周转时间(大于等于1)=作业周转时间/作业实际运行的时间
  • 平均带权周转时间=各作业带权周转时间之和/作业数

 

等待时间:

1、对于进程,等待时间指进程建立后等待被服务的时间之和

2、对于作业还要多考虑作业在外存中被等待调度的时间

响应时间:用户提交请求到首次产生响应所用的时间

调度算法

链表数据如下:

typedef struct thread {
int pid;//线程运行信号
int counter;
int nice;
struct thread* next;
}thread,*LinkList;

int algorithm(int counter,int nice) {
int priority = counter + 20 - nice;
return priority;
}

先来先服务(FCFS)

另一种思路——等待时间越久的优先

  • 对于作业与对于进程,先来后到概念不同
  • 对长作业有利,对短作业不利

补充与FCFS相似的FIFO:

void SCHED_FIFO() {
    int Pid = 1;
    LinkList H = (thread*)malloc(sizeof(thread));
    LinkList T = H;
    H->next = NULL;
    while (1) {
        int Counter = 0;
        int Nice = 0;
        scanf_s("%d %d", &Counter, &Nice);
        //两个时间片之间增加线程
        if (Counter != 0) {
            LinkList p = (thread*)malloc(sizeof(thread));
            p->counter = Counter;
            p->nice = Nice;
            p->pid = Pid;
            Pid++;//为下一个线程线程pid自动加一做准备
            //如果队列内暂时为空,那么线程先直接进入cpu运转
            if (H->next == NULL) {
                H->next = p;
                p->next = NULL;
            }
            else {
                for (LinkList p1 = H;; p1 = p1->next) {
                    if (p1->next->nice <p->nice) {
                        p->next = p1->next;
                        p1->next = p;
                        break;
                    }
                    else {
                        if (p1->next->next == NULL) {
                            p1->next->next = p;
                            p->next = NULL;
                            break;
                        }
                    }
                }
            }
        }
        //两个时间片之间不加线程,或者增加线程后
        if (H->next != NULL) {
            H->next->counter--;
            printf("正在运行线程%d", H->next->pid);
            printf("该线程还剩%d运行时间", H->next->counter);
            //如果线程运行完毕
            if (H->next->counter <= 0) {
                LinkList p = H->next;
                H->next = H->next->next;
                free(p);
            }
        }
        else {
            printf("队列空,CPU空闲状态");
        }
    }
}

短作业优先(SJF)

短进程优先调度(SPF)

非抢占式,每次调度时选择当前已到达且运行时间最短的作业、进程

最短剩余时间优先算法(SRTN)

抢占式,每当就绪队列改变时,计算新到达进程的剩余运行时间与当前运行进程的剩余运行时间,由最短的来抢占。同时,在一个进程完成时也需要调度

  • 对短作业有利,对长作业不利,可能产生饥饿现象(某些作业一直得不到运行)

在题目中,提到的“短作业/进程优先算法”默认是非抢占式的,引出概念:在所有进程都几乎同时到达时,采用SJF调度算法的平均等待时间、平均周转时间最少

高响应比优先 (HRRN)

非抢占式,经典折中

  • 等待时间相同时,要求服务时间短的优先(SJF优点)
  • 要求服务时间相同时,等待时间长优先(FCFS优点),避免饥饿

响应比=(等待时间+要求服务时间)/要求服务时间

时间片轮转(RR)

抢占式,轮流让就绪队列中的进程依次执行一个时间片(一般题目给出时间片大小)

  • 下处理机时重新放回队尾,时间片不能太大也不能太小
  • 公平,适用分时操作系统,不区分任务紧急度
void SCHED_RR() {
    int Pid = 1;
    LinkList H = (thread*)malloc(sizeof(thread));
    LinkList T = H;
    H->next = NULL;
    while (1) {
        int Counter = 0;
        int Nice = 0;
        scanf_s("%d %d", &Counter, &Nice);
        //两个时间片之间增加线程
        if (Counter != 0) {
            LinkList p = (thread*)malloc(sizeof(thread));
            p->counter = Counter;
            p->nice = Nice;
            p->pid = Pid;
            Pid++;//为下一个线程线程pid自动加一做准备
            //如果队列内暂时为空,那么线程先直接进入cpu运转
            if (H->next == NULL) {
                H->next = p;
                p->next = NULL;
            }
            else {
                for (LinkList p1 = H;; p1 = p1->next) {
                    if (algorithm(p1->next->counter, p1->next->nice) < algorithm(p->counter, p->nice)) {
                    	p->next = p1->next;
                    	p1->next = p;
                    	break;
                        }
                	else {
                    	if (p1->next->next == NULL) {
                        	p1->next->next = p;
                            p->next = NULL;
                            break;
                        }
                    }
                }
            }
        }
        //两个时间片之间不加线程,或者增加线程后
        if (H->next != NULL) {
        	H->next->counter--;
            printf("正在运行线程%d", H->next->pid);
            printf("该线程还剩%d运行时间", H->next->counter);

        //如果线程运行完毕
        if (H-&gt;next-&gt;counter &lt;= 0) {
            LinkList p = H-&gt;next;
            H-&gt;next = H-&gt;next-&gt;next;
            free(p);
        }
        //如果线程未运行完毕,接入链表尾
        else {
            //如果队列中只有一个线程,则继续分配时间片并运行
            if (H-&gt;next-&gt;next != NULL) {
                LinkList p = H-&gt;next;
                H-&gt;next = H-&gt;next-&gt;next;
              	if (p1-&gt;next == NULL) {
                        p1-&gt;next = p;
                        p-&gt;next = NULL;
                        break;
                    }
                }
            }
        }
    }
    else {
        printf(&quot;队列空,CPU空闲状态&quot;);
    }
}

}

优先级调度算法

抢占式,非抢占都有(注意看题),每个作业/进程都有各自优先级,选择优先级最高的

多级反馈队列调度算法

对其他算法的权衡


/多级调度算法有很多细节补充

然后进程同步和互斥也被吃了


死锁

对资源分配不合理容易发生

至少两个或两个以上的进程同时发生死锁(争夺资源而导致阻塞)

注意与“死循环”和“饥饿”的区别

必要条件:

  • 互斥条件(必须相互争夺资源)
  • 不剥夺条件(资源在未使用完之前不能被强行夺走)
  • 请求和保持条件(已经保持了资源,但又请求资源)
  • 循环等待条件(存在资源循环等待链)

处理策略:预防死锁,避免死锁,死锁的检测和解除(纯废话,但还是记一下)

银行家算法

安全序列:

按照这种序列分配顺序,就能顺利完成。

所以说安全序列可有多个

如果找不出任何安全序列,就进入不安全状态

如果能找出任何一个安全序列,就进入安全状态

算法:

每个进程声明一个m*n的最大需求矩阵Max与分配矩阵Allocation

另外留一个长度为m的一维数组Available

然后逐个对比并实验分配

  • Available向量:系统中可利用的资源数目
  • Max矩阵:每个进程对每种资源的最大需求
  • Allocation矩阵:每个进程已分配的各类资源的数目
  • Need矩阵:每个进程还需要的各类资源数

Need[i,j] = Max[i,j] - allocation[i, j]

死锁的检测和解除

资源剥夺法(挂起进程,容易饥饿)、撤销进程法、进程回退法(要记录进程历史信息)