操作系统作业

1.操作系统概述

作业内容

(1)简述Linux的发展过程中,开源社区的推动作用,试比较GNU、BSD、Apache、MIT等开源协议的特点及在商业友好程度方面的差异。
如果你是一个开发者,你会如何选择软件许可协议?
(2)请举例一个近年来发生的和操作系统相关的安全事件,梳理事件发展的脉络,分析系统补丁的进展和对用户的影响,
分析如何从技术、法律、社会管理的角度减少安全事件对用户造成的危害。
(3)比较Windows 与 Linux操作系统的特点,包括版权许可。
  1. 在Linus Torvalds写出第一个版本的linux系统之后,就将它发布到网上,征求广大网友的意见。Linux1.0 版本发行时,使用了GPL协议,这说明它是一个开源软件,无数的开源爱好者对它进行扩展和补充。可以说,如果linux没有开源,没有开源社区,那么它可能已经消失在历史的尘埃中,或者仅仅只有少数人知道的操作系统。
  • GPL

    复制自由允许把软件复制到任何人的电脑中,并且不限制复制的数量。传播自由允许软件以各种形式进行传播。收费传播允许在各种媒介上出售该软件,但必须提前让买家知道这个软件是可以免费获得的;因此,一般来讲,开源软件都是通过为用户提供有偿服务的形式来盈利的。修改自由允许开发人员增加或删除软件的功能,但软件修改后必须依然基于GPL许可协议授权。

  • BSD

BSD 协议基本上允许用户“为所欲为”,用户可以使用、修改和重新发布遵循该许可的软件,并且可以将软件作为商业软件发布和销售,前提是需要满足下面三个条件:

  1. 如果再发布的软件中包含源代码,则源代码必须继续遵循 BSD 许可协议。

  2. 如果再发布的软件中只有二进制程序,则需要在相关文档或版权文件中声明原始代码遵循了 BSD 协议。

  3. 不允许用原始软件的名字、作者名字或机构名称进行市场推广。

  • Apache

Apache 和 BSD 类似,都适用于商业软件。Apache 协议在为开发人员提供版权及专利许可的同时,允许用户拥有修改代码及再发布的自由。要遵守以下四个条件。

  1. 该软件及其衍生品必须继续使用 Apache 许可协议。
  2. 如果修改了程序源代码,需要在文档中进行声明。
  3. 若软件是基于他人的源代码编写而成的,则需要保留原始代码的协议、商标、专利声明及其他原作者声明的内容信息。
  4. 如果再发布的软件中有声明文件,则需在此文件中标注 Apache 许可协议及其他许可协议。
  • MIT

目前限制最少的开源许可协议之一(比 BSD 和 Apache 的限制都少),只要程序的开发者在修改后的源代码中保留原作者的许可信息即可,因此普遍被商业软件所使用。

  • 我会选择使用Apache协议。

众所周知,在windows操作系统上,如果你没有在官方网站上下载软件。那么有很大几率会出现蓝屏,死机等问题。或者触摸板不起作用的时候,这个时候需要去官网下载对应的补丁进行修复。可以说补丁对我们使用windows操作系统电脑时,提供了很多帮助。

  • 技术上:屏蔽不良网站,加强安全防范技术。
  • 法律上:完善相关法律法规
  • 社会管理上:宣传网络安全的重要性,不定期举办讲座等。

windows

  • 图形界面;直观高效的面向对象的图形用户界面,易学易用。
  • 多任务;允许用户同时运行多个应用程序,或在一个程序中同时做几件事情。
  • 即插即用。
  • 出色的多媒体功能。
  • 对内存的自动化管理。
  • Windows是一款商业软件,想要使用windows,你必须同意它的软件许可条款。

Linux

  • 开放性 系统遵循世界标准规范,特别是遵循开放系统互连(OSI)国际标准。
  • 多用户 系统资源可以被相同用户使用,每个用户对自己的资源(例如:文件、设备)有特定的权限,互不影响。
  • 多任务 …
  • 良好的用户界面 …
  • 设备独立性 …
  • 提供了丰富的网络功能 …
  • 可靠的安全系统
  • 良好的可移植性
  • Linux系统是通过公共许可协议GPL的自由软件。

2.进程同步

作业内容

(1)某车站售票厅,最多可容纳20名购票者进入,当售票厅中少于20名购票者时,其厅外的购票者可立即进入,否则,需在外等待。
若把一个购票者看作是一个进程,请回答下列问题:
①写出用P/V操作管理这些并发进程时,信号量的初值以及信号量的各种取值的含义;
②根据所定义的信号量,编写相应的进程同步算法,以保证进程能够正确地并发执行。
(2)设数据采集系统中有一个单缓冲区,数据采集与数据处理共享该缓冲区,试用整型信号量编写一个该系统中数据采集进程与数据处理进程的同步算法。
(3)请分析Linux源代码中task_struct各个字段含义,进程链表是如何组织的。
  1. 售票
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# include <sys/types.h>
# include <pthread.h>
# include <semaphore.h>
# include <string.h>
# include <unistd.h>

//定义一信号量s,初始值为20。表示空闲位置的数量
// S=0表示售票厅中已有20名顾客(购票者) ;
sem_t s;

void* Ticket(void* param) {
    long ticketid = (long)param;
    while (1){
        sem_wait(&s);
        printf("Customer %ld: is buying\n", ticketid);
        sleep(3);
        sem_post(&s);
    }
    pthread_exit(0);
}
int main() {
    sem_init(&s,0,20);
    pthread_t tid[30];
    for (int i = 0; i < 30 ; ++i) {
        pthread_create(&tid[i], NULL , Ticket , (void*)i);
    }
    int c=0;
    while (1){
        c = getchar();
        if (c=='q' || c=='Q'){
            for (int i = 0; i < 30; ++i) {
                pthread_cancel(tid[i]);
            }
            break;
        }
    }
    return 0;
}

  1. 第二个问题本质上是生产者——消费者问题。
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# include <sys/types.h>
# include <pthread.h>
# include <semaphore.h>
# include <string.h>
# include <unistd.h>

//empty  同步信号量,表示剩余空间的数量
// full 同步信号量,表示产品的数量
//单个缓冲区可以不设置互斥信号量
sem_t full,empty;

//生产者
void *get(void* param) {

    long threadid = (long)param;
    while (1){
        sem_wait(&empty);
        printf("ThreadId %ld : collect data\n", threadid);
        sleep(6);
        sem_post(&full);
    }
    pthread_exit(0);
}

//消费者
void *compute(void* param) {
    long threadid = (long)param;
    while (1){
        sem_wait(&full);
        printf("ThreadId %ld : compute data \n",threadid);
        sleep(3);
        sem_post(&empty);
    }
    pthread_exit(0);
}

int main() {
    //线程id
    pthread_t tid[4];
    //第二个参数 不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享
    //第三个参数 给出了信号量的初始值。  
    sem_init(&empty, 0, 1);
    sem_init(&full, 0, 0);
    //两个生产者,两个消费者
    pthread_create(&tid[0], NULL , compute, (void*)0);
    pthread_create(&tid[1], NULL , get, (void*)1);
    pthread_create(&tid[2], NULL , compute, (void*)2);
    pthread_create(&tid[3], NULL, get, (void*)3);
    int c=0;
    while (1){
        c = getchar();
        if (c=='q' || c=='Q'){
            for (int i = 0; i < 4; ++i) {
                pthread_cancel(tid[i]);
            }
            break;
        }
    }
    return 0;
}
  1. task_struct字段的含义
 struct task_struct {
volatile long state;  //说明了该进程是否可以执行,还是可中断等信息
unsigned long flags;  //Flage 是进程号,在调用fork()时给出
int sigpending;    //进程上是否有待处理的信号
mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同
                        //0-0xBFFFFFFF for user-thead
                        //0-0xFFFFFFFF for kernel-thread
//调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度
volatile long need_resched;
int lock_depth;  //锁深度
long nice;       //进程的基本时间片
//进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER
unsigned long policy;
struct mm_struct *mm; //进程内存管理信息
int processor;
//若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新
unsigned long cpus_runnable, cpus_allowed;
struct list_head run_list; //指向运行队列的指针
unsigned long sleep_time;  //进程的睡眠时间
//用于将系统中所有的进程连成一个双向循环链表, 其根是init_task
struct task_struct *next_task, *prev_task;
struct mm_struct *active_mm;
struct list_head local_pages;       //指向本地页面      
unsigned int allocation_order, nr_local_pages;
struct linux_binfmt *binfmt;  //进程所运行的可执行文件的格式
int exit_code, exit_signal;
int pdeath_signal;     //父进程终止时向子进程发送的信号
unsigned long personality;
//Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序
int did_exec:1; 
pid_t pid;    //进程标识符,用来代表一个进程
pid_t pgrp;   //进程组标识,表示进程所属的进程组
pid_t tty_old_pgrp;  //进程控制终端所在的组标识
pid_t session;  //进程的会话标识
pid_t tgid;
int leader;     //表示进程是否为会话主管
struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
struct list_head thread_group;   //线程链表
struct task_struct *pidhash_next; //用于将进程链入HASH表
struct task_struct **pidhash_pprev;
wait_queue_head_t wait_chldexit;  //供wait4()使用
struct completion *vfork_done;  //供vfork() 使用
unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值

//it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value
//设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据
//it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。
//当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送
//信号SIGPROF,并根据it_prof_incr重置时间.
//it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种
//状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据
//it_virt_incr重置初值。
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_value;
struct timer_list real_timer;   //指向实时定时器的指针
struct tms times;      //记录进程消耗的时间
unsigned long start_time;  //进程创建的时间
//记录进程在每个CPU上所消耗的用户态时间和核心态时间
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; 
//内存缺页和交换信息:
//min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换
//设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。
//cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。
//在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
int swappable:1; //表示进程的虚拟地址空间是否允许换出
//进程认证信息
//uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid
//euid,egid为有效uid,gid
//fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件
//系统的访问权限时使用他们。
//suid,sgid为备份uid,gid
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
int ngroups; //记录进程在多少个用户组中
gid_t groups[NGROUPS]; //记录进程所在的组
//进程的权能,分别是有效位集合,继承位集合,允许位集合
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;
struct rlimit rlim[RLIM_NLIMITS];  //与进程相关的资源限制信息
unsigned short used_math;   //是否使用FPU
char comm[16];   //进程正在运行的可执行文件名
 //文件系统信息
int link_count, total_link_count;
//NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空
struct tty_struct *tty;
unsigned int locks;
//进程间通信信息
struct sem_undo *semundo;  //进程在信号灯上的所有undo操作
struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作
//进程的CPU状态,切换时,要保存到停止进程的task_struct中
struct thread_struct thread;
  //文件系统信息
struct fs_struct *fs;
  //打开文件信息
struct files_struct *files;
  //信号处理函数
spinlock_t sigmask_lock;
struct signal_struct *sig; //信号处理函数
sigset_t blocked;  //进程当前要阻塞的信号,每个信号对应一位
struct sigpending pending;  //进程上是否有待处理的信号
unsigned long sas_ss_sp;
size_t sas_ss_size;
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;
u32 parent_exec_id;
u32 self_exec_id;

spinlock_t alloc_lock;
void *journal_info;
};
  • 组织方式

进程PCB中,会有一个变量 state 来表示进程的当前状态。如:1表示创建态、2表示就绪态、3表示运行态…为了对同一个状态下的各个进程进行统一的管理,操作系统会将各个进程的PCB组织起来。主要的进程组织方式有链接方式以及索引方式两种。

链接方式:这是把具有同一状态的 PCB,用其中的链接字链接成一个队列。这样,可以形成绪队列、若干个阻塞队列和空白队列等。对其中的就绪队列常按进程优先级的高低排列,把优先级高的进程的 PCB 排在队列前面。此外,也可根据阻塞原因的不同而把处于阻塞状态的进程的 PCB 排成等待 I/O 操作完成的队列和等待分配内存的队列等。
索引方式: 系统根据所有进程的状态建立几张索引表。例如,就绪索引表、阻塞索引表等,并把各索引表在内存的首地址记录在内存的一些专用单元中。在每个索引表的表目中,记录具有相应状态的某个 PCB 在 PCB 表中的地址。

3.进程同步算法设计与模拟仿真

作业内容

作业内容:
(1)在Linux环境中,使用信号量编程实现银行存取款模型的同步问题模拟。
(2)在Linux 环境中,使用信号量编程实现生产者和消费者问题模拟。
  1. 银行存取款
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# include <sys/types.h>
# include <pthread.h>
# include <semaphore.h>
# include <string.h>
# include <unistd.h>
//mutex保证存钱和取钱互斥访问
sem_t mutex;
int money = 100;
//存钱
void *saveMoney(void* param) {
    long threadid = (long)param;
    sem_wait(&mutex);
    printf("存钱线程 %ld : 存入金额100\n", threadid);
    money+=100;
    sleep(3);
    sem_post(&mutex);
    printf("存钱线程 %ld : 当前金额为%d\n",threadid,money);
    pthread_exit(0);
}

//取钱
void *withDraw(void* param) {
    long threadid = (long)param;
    sem_wait(&mutex);
    printf("取钱线程 %ld : 取出金额50\n",threadid);
    money-=50;
    sleep(1);
    sem_post(&mutex);
    printf("取钱线程 %ld : 当前金额为%d\n",threadid,money);
    pthread_exit(0);
}

int main() {

    //线程id
    pthread_t tid[4];
    sem_init(&mutex, 0, 1);
    pthread_create(&tid[0], NULL , withDraw, (void*)0);
    pthread_create(&tid[1], NULL , saveMoney, (void*)1);
    pthread_create(&tid[2], NULL , withDraw, (void*)2);
    pthread_create(&tid[3], NULL, saveMoney, (void*)3);
    sleep(10);
    printf("最终余额: %d\n", money);
    //释放进程
    for (int i = 0; i < 4; ++i) {
        pthread_cancel(tid[i]);
    }
    return 0;
}

image-20211111164441351

  1. 生产者——消费者问题
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# include <sys/types.h>
# include <pthread.h>
# include <semaphore.h>
# include <string.h>
# include <unistd.h>
#define BUFFER_SIZE 5

//empty  同步信号量,表示剩余空间的数量
// full 同步信号量,表示产品的数量
// mutex 互斥信号量,实现对缓冲区的互斥访问
sem_t empty, full, mutex;

typedef int buffer_item;

//缓冲区
buffer_item buffer[BUFFER_SIZE];

int in, out;

// 记录产品的id
int id = 0;

//生产产品
int insert_item(buffer_item item) {
    buffer[out] = item;
    out = (out + 1) % BUFFER_SIZE;
    return 0;
}

//消费产品
int remove_item(buffer_item *item) {
    // 将buffer[in]移除,并将item填充进去
    *item = buffer[in];
    in = (in + 1) % BUFFER_SIZE;
    return 0;
}

//生产者
void *producer(void* param) {
    long threadid = (long)param;
    while (1){
        sem_wait(&empty);
        sem_wait(&mutex);
        //生产产品
        insert_item(id);
        sleep(6);
        printf("ThreadId %ld : Producer produce product %d \n", threadid,id);
        id++;
        sem_post(&mutex);
        sem_post(&full);
    }
    pthread_exit(0);
}

//消费者
void *consumer(void* param) {
    long threadid = (long)param;
    while (1){
        sem_wait(&full);
        sem_wait(&mutex);
        //消费产品
        int item;
        remove_item(&item);
        sleep(3);
        printf("ThreadId %ld : Consumer consume product %d \n", threadid ,item);
        sem_post(&mutex);
        sem_post(&empty);
    }
    pthread_exit(0);
}

int main() {
    //线程id
    pthread_t tid[4];
    //对mutex进行初始化
    //第二个参数 不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享
    //第三个参数 给出了信号量的初始值。  
    sem_init(&mutex, 0, 1);
    sem_init(&empty, 0, BUFFER_SIZE);
    sem_init(&full, 0, 0);
    in = out = 0;
    //两个生产者,两个消费者
    pthread_create(&tid[0], NULL ,consumer, (void*)0);
    pthread_create(&tid[1], NULL ,producer, (void*)1);
    pthread_create(&tid[2], NULL ,consumer, (void*)2);
    pthread_create(&tid[3], NULL, producer, (void*)3);
    int c=0;
    while (1){
        c = getchar();
        if (c=='q' || c=='Q'){
            for (int i = 0; i < 4; ++i) {
                pthread_cancel(tid[i]);
            }
            break;
        }
    }
    //释放信号量
    sem_destroy(&mutex);
    sem_destroy(&empty);
    sem_destroy(&full);
    return 0;
}

image-20211020155958008

4.进程调度与死锁

作业内容

(1)简述多级反馈队列调度算法的基本原理,分析它对短作业、长作业的调度性能。
(2)在银行家算法中,若出现下述资源分配情况,试问:

image-20211028204254022

  1. 多级反馈队列调度算法的实现思想
  • 应设置多个就绪队列,并为各个队列赋予不同的优先级,第1级队列的优先级最高,第2级队列次之,其余队列的优先级逐次降低。

  • 赋予各个队列中进程执行时间片的大小也各不相同,在优先级越高的队列中,每个进程的运行时间片就越小。例如,第2级队列的时间片要比第1级队列的时间片长一倍,以此类推。

  • 当一个新进程进入内存后,首先将它放入第1级队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统,否则,调度程序便将该进程转入第2级队列的末尾,再同样地按FCFS 原则等待调度执行。如此下去,当一个长进程从第1级队列依次降到第 n 级队列后,在第 n 级队列中便釆用时间片轮转的方式运行。

  • 仅当第1级队列为空时,调度程序才调度第2级队列中的进程运行;仅当第1 ~ (i-1)级队列均为空时,才会调度第i级队列中的进程运行。如果处理机正在执行第i级队列中的某进程时,又有新进程进入优先级较高的队列(第 1 ~ i-1中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i级队列的末尾,把处理机分配给新到的更高优先级的进程。

多级反馈队列的优势有:

  • 终端型作业用户:短作业优先。
  • 短批处理作业用户:周转时间较短。
  • 长批处理作业用户:经过前面几个队列得到部分执行,不会长期得不到处理。
  1. 银行家
  • 状态安全,可用资源可以满足p0进程的需求,把分配给进程p0的资源进行回收。最后序列为 p0->p3->p1->p2->p4。
  • 不能分配给它。分配完之后,可用资源只剩下(0,4,0,0),不能满足任何一个进程的资源需求,进程会进入死锁状态。所以不能分配出去。

5.进程调度算法设计与模拟仿真

作业内容:

(1)编写程序,模拟FCFS和SPF二种进程调试算法。

(2)编写程序,模拟银行家算法。
  1. FCFS 和SPF
typedef struct PCB{
    char name[20];
//  运行时间
    int running_time;
//   到达时间
    int enter_time;
//    优先级
    int priority;
    //完成时间
    int done_time;
    int copyRunning_time;  //用于时间片轮转
//  进程开始运行的时间
    int start_time;
    struct PCB* next;
} PCB;

typedef struct PCBQueue{
    PCB* firstProg;
    PCB* LastProg;
    int size;
} PCBQueue;

void Queueinit(PCBQueue* queue){
    if(queue==NULL){
        return;
    }
    queue->size = 0;
    queue->LastProg = (PCB*)malloc(sizeof(PCB));
    queue->firstProg = queue->LastProg;
}


void EnterQueue(PCBQueue* queue,PCB* pro){   //加入进程队列
    queue->LastProg->next = (PCB*)malloc(sizeof(PCB));
    queue->LastProg = queue->LastProg->next;
    queue->LastProg->enter_time = pro->enter_time;
//    将name复制给 LastProg
    memcpy(queue->LastProg->name,pro->name,sizeof(pro->name));
    queue->LastProg->priority = pro->priority;
    queue->LastProg->running_time = pro->running_time;
    queue->LastProg->copyRunning_time = pro->copyRunning_time;
    queue->LastProg->start_time = pro->start_time;
    queue->size++;
}
PCB* poll(PCBQueue* queue){
    PCB* temp = queue->firstProg->next;
    if(temp == queue->LastProg){
        queue->LastProg=queue->firstProg;
        queue->size--;
        return temp;
    }
    queue->firstProg->next = queue->firstProg->next->next;
    queue->size--;
    return temp;
}

void inputPCB(PCB pro[],int num){
    for(int i=0;i<num;i++){
        PCB prog ;
        printf("请输入第%d个进程的名字,到达时间 ,服务时间,优先级\n",i+1);
        scanf("%s",prog.name);
        scanf("%d",&prog.enter_time) ;
        scanf("%d",&prog.running_time);
        prog.copyRunning_time = prog.running_time;
        scanf("%d",&prog.priority);
        pro[i]=prog;
    }
}

//冒泡排序算法(每次找到最大的放在末尾)
void sortWithEnterTime(PCB pro[],int num){
    for(int i=1;i<num;i++){
        for(int j= 0;j<num-i;j++){
            if(pro[j].enter_time>pro[j+1].enter_time){
                PCB temp = pro[j];
                pro[j] = pro[j+1];
                pro[j+1] = temp;
            }
        }
    }
}

void FCFS(PCB pro[],int num){
    printf("进程 到达时间  服务时间 开始时间 完成时间 周转时间 带权周转时间\n");
    sortWithEnterTime(pro,num);    //按照进入顺序排序
    PCBQueue* queue = (PCBQueue*)malloc(sizeof(PCBQueue));
    Queueinit(queue);
    EnterQueue(queue,&pro[0]);
    int time = pro[0].enter_time;
    int pronum=1;    //记录当前的进程
    //平均周转时间
    float sum_T_time = 0;
//    带权平均周转时间
    float sum_QT_time = 0 ;
    while(queue->size>0){
        PCB* curpro = poll(queue);   //从进程队列中取出进程
        if(time < curpro->enter_time)
            time =  curpro->enter_time;
        //完成时间
        int done_time = time+curpro->running_time;
        //周转时间(作业完成的时间-作业到达的时间)
        int T_time = done_time - curpro->enter_time;
        sum_T_time += T_time;
        // 带权周转时间((作业完成的时间-作业到达的时间)/ 作业运行时间)
        float QT_time = T_time / (curpro->running_time+0.0) ;
        sum_QT_time += QT_time;
        for(int tt = time;tt<=done_time && pronum<num;tt++){    //模拟进程的执行过程
            if(tt >= pro[pronum].enter_time){
                EnterQueue(queue,&pro[pronum]);
                pronum++;
            }
        }
        printf("%s\t%d\t%d\t%d\t%d\t%d\t%.2f\n",curpro->name,curpro->enter_time,curpro->running_time,time,done_time
               ,T_time,QT_time);
        time += curpro->running_time;
        if(queue->size==0 && pronum < num){   //防止出现前一个进程执行完到下一个进程到达之间无进程进入
            EnterQueue(queue,&pro[pronum]);
            pronum++;
        }
    }
    printf("平均周转时间为%.2f\t平均带权周转时间为%.2f\n",sum_T_time/(num+0.0),sum_QT_time/(num+0.0));
}

//按照运行时间排序
void sortWithLongth(PCB pro[],int start,int end){
    int len = end - start;
    if(len == 1)
        return;
    for(int i=1;i<len;i++){
        for(int j= start;j<end-i;j++){
            if(pro[j].running_time>pro[j+1].running_time){
                PCB temp = pro[j];
                pro[j] = pro[j+1];
                pro[j+1] = temp;
            }
        }
    }
}
void SJF(PCB pro[],int num) {
    printf("进程 到达时间  服务时间 开始时间 完成时间 周转时间 带权周转时间\n");
    sortWithEnterTime(pro,num);
    PCBQueue* queue = (PCBQueue*)malloc(sizeof(PCBQueue));;
    Queueinit(queue);
    EnterQueue(queue,&pro[0]);
    int time = pro[0].enter_time;
    int pronum=1;    //记录当前的进程
    float sum_T_time = 0,sum_QT_time = 0;
    while(queue->size>0){
        PCB* curpro = poll(queue);   //从进程队列中取出进程
        if(time <  curpro->enter_time)
            time =  curpro->enter_time;
        int done_time = time+curpro->running_time;
        int T_time = done_time - curpro->enter_time;
        float QT_time = T_time / (curpro->running_time+0.0) ;
        sum_T_time += T_time;
        sum_QT_time += QT_time;
        int pre = pronum;
        for(int tt = time;tt<=done_time&&pronum<num;tt++){    //模拟进程的执行过程
            if(tt>=pro[pronum].enter_time){ // 统计从此任务开始到结束之间有几个进程到达
                pronum++;
            }
        }
        sortWithLongth(pro,pre,pronum);//将到达的进程按照服务时间排序
        for(int i=pre;i<pronum;i++){    //将进程链入队列
            EnterQueue(queue,&pro[i]);
        }
        pre = pronum;
        printf("%s\t%d\t%d\t%d\t%d\t%d\t%.2f\n",curpro->name,curpro->enter_time,curpro->running_time,time,done_time
               ,T_time,QT_time);
        time +=  curpro->running_time;
        if(queue->size==0&&pronum<num){   //防止出现前一个进程执行完到下一个进程到达之间无进程进入
            EnterQueue(queue,&pro[pronum]);
            pronum++;
        }
    }
    printf("平均周转时间为%.2f\t平均带权周转时间为%.2f\n",sum_T_time/(num+0.0),sum_QT_time/num);
}

image-20211020115816280

  1. 银行家算法
import java.util.Scanner;

public class Bank1 {
    static int pcb_nums;  // 进程数量
    static int res_nums;  // 资源种类数量
    static int max[][];     // 最大需求资源向量
    static int alloc[][];   // 拥有资源数向量
    static int need[][];    // 还需要资源数向量
    static int ava[];     // 可用资源数向量
    static int request[];  //本次申请的资源量
    static int safe_seq[]; //安全序列数组
    static void bank_init()
    {
        Scanner in = new Scanner(System.in);
        System.out.println("请输入进程数量:");
        pcb_nums = in.nextInt();
        System.out.println("请输入资源数量");
        res_nums = in.nextInt();
        safe_seq = new int[pcb_nums+1];
        max = new int[pcb_nums][res_nums];
        alloc = new int[pcb_nums][res_nums];
        need = new int[pcb_nums][res_nums];
        ava = new int[res_nums];     // 可用资源
        System.out.println("请输入最大资源:");
        for (int i = 0; i < pcb_nums; i++) {
            for (int j = 0; j < res_nums; j++) {
                max[i][j] = in.nextInt();
            }
        }
        System.out.println("请输入分配资源:");
        for (int i = 0; i < pcb_nums; i++) {
            for (int j = 0; j < res_nums; j++) {
                alloc[i][j] = in.nextInt();
            }
        }
        //计算需求矩阵
        for (int i = 0; i < pcb_nums; i++) {
            for (int j = 0; j < res_nums; j++) {
                need[i][j] = max[i][j] - alloc[i][j];
            }
        }
        System.out.println("请输入可用资源:");
        for (int j = 0; j < res_nums; j++)
        {
            ava[j] = in.nextInt();
        }
    }
    //比较进程为m中的元素全大于n中的元素
    static boolean compare(int m[],int n[]){
        for (int i = 0; i < res_nums; i++) {
            if (m[i]<n[i])
                return false;
        }
        return true;
    }
    //安全性检验函数,检测是否存在安全序列
    static boolean safe(){
        boolean finish[] = new boolean[pcb_nums];
        int work[] = new int[res_nums];
        int num = 0;
        for (int i = 0; i < res_nums; i++) {
            work[i] = ava[i];
        }
        for (int i = 0; i < pcb_nums; i++) {
            if (num == pcb_nums )
                break;
            for (int j = 0; j < pcb_nums; j++) {
                if (finish[j])
                    continue;
                else{
                    if (compare(work,need[j])){
                        finish[j] = true;
                        safe_seq[num] = j+1;
                        num++;
                        //释放进程资源
                        for (int k = 0; k < res_nums; k++) {
                            work[k] = work[k] + alloc[j][k];
                        }
                    }
                }
            }
        }
        for (int i = 0; i < pcb_nums; i++) {
            if (!finish[i])
                return false;
        }
        //输出安全序列
        for (int i = 0; i < pcb_nums; i++) {
            System.out.print(safe_seq[i]+" ");
        }
        return true;
    }
    //申请进程后的安全性检验函数  n代表进程号
    static void resafe(int n){
        //available>=request 并且 need >=request
        if (compare(ava,request) && compare(need[n-1],request)){
            for (int i = 0; i < res_nums; i++) {
                alloc[n-1][i] =alloc[n-1][i] + request[i];
                need[n-1][i] = need[n-1][i] - request[i];
                ava[i] = ava[i] - request[i];
            }
            if (safe()){
                System.out.println("允许进程"+n+"申请资源");
            }
            else{
                System.out.println("不允许进程"+n+"申请资源");
                for (int i = 0; i < res_nums; i++) {
                    alloc[n-1][i] = alloc[n-1][i] - request[i];
                    need[n-1][i] = need[n-1][i] + request[i];
                    ava[i] = ava[i] + request[i];
                }
            }
        }
        else
            System.out.println("申请资源量越界");
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        //进程编号
        int n;
        bank_init();
        if (safe()){
            System.out.println("  存在安全序列,初始状态安全。");
        }
        else
            System.out.println("不存在安全序列,初始状态不安全。");
        System.out.println("请输入发出请求向量request的进程编号:");
        n = in.nextInt();
        request = new int[res_nums];
        System.out.println("请输入请求向量request:");
        for (int i = 0; i < res_nums; i++) {
            request[i] = in.nextInt();
        }
        resafe(n);
    }
}

img

https://gitee.com/infiniteStars/picgoimages/raw/master/image-20211029170647213.png

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
A.手工操作 B.单道批处理 C.多道批处理 D.多用户分时系统 2. 与计算机硬件关系最密切的软件是( )。 A.编译程序 B.数据库管理系统 C.游戏程序 D.OS 3. 现代OS具有并发性和共享性,是( )的引入导致的。 A.单道程序 B. 磁盘 C. 对象 D.多道程序 4. 早期的OS主要追求的是( )。 A.系统的效率 B.用户的方便性 C.可移植 D.可扩充性 5.( )不是多道程序系统 A.单用户单任务 B.多道批处理系统 C.单用户多任务 D.多用户分时系统 6.( )是多道操作系统不可缺少的硬件支持。 A.打印机 B.中断机构 C.软盘 D.鼠标 7. 特权指令可以在( )执行。 A.目 态 B.浏览器中 C.任意的时间 D.进程调度中 8. 没有了( )计算机系统就启动不起来。 A.编译器 B.DBMS C.OS D.浏览器 9. 通道能够完成( )之间的数据传输。 A.CPU与外设 B.内存与外设 C.CPU与主存 D.外设与外设 10. 操作系统的主要功能有( )。 A. 进程管理、存储器管理、设备管理、处理机管理 B. 虚拟存储管理、处理机管理、进程调度、文件系统 C. 处理机管理、存储器管理、设备管理、文件系统 D. 进程管理、中断管理、设备管理、文件系统 11. 单处理机计算机系统中,( )是并行操作的。 A.处理机的操作与通道的操作是并行的 B.程序与程序 C.主程序与子程序 D.用户程序与操作系统程序 12. 处理机的所有指令可以在( )执行。 A.目态 B.浏览器中 C.任意的时间 D.系统态 13.( )功能不是操作系统直接完成的功

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值