《操作系统导论》实验一:CPU虚拟化(进程)

Simulation Homework: 模拟进程状态转换

1.作业包:process-run.py

2.作业说明:在这里插入图片描述

3.作业要求:

(1)Run process-run.py with the following flags:-l5:100,5:100.
参数 -l PROCESS_LIST:逗号分隔的进程列表,形式是X1:Y1,X2:Y2,...,其中X代表进程指令数,Y(0~100)代表运行指令还是声明I/O请求
在这里插入图片描述在这里插入图片描述
该命令运行进程0,运行5条指令,且每条指令的CPU占用率为100%,此时进程1处于就绪状态,进程0运行完毕,CPU运行进程1,执行同样操作,总用时10单位时间。

(2) Now run with these flags: ./process-run.py -l 4:100,1:0.
在这里插入图片描述
该命令运行进程0,运行4条指令,且每条指令的CPU占用率为100%,之后进程1发出I/O请求,CPU阻塞该进程,I/O执行3单位时间,CPU解除进程1的阻塞状态,进程1执行完成操作,总用时10单位时间。

(3) Switch the order of the processes: -l 1:0,4:100.
在这里插入图片描述
该命令运行进程0发出I/O请求,CPU阻塞该进程,并执行I/O操作,同时CPU运行进程1,运行4条指令,且每条指令的CPU占用率为100%,4个单位时间后I/O操作和进程1均运行完成,进程0执行完成操作,总用时6个单位时间。

(4) SWITCH_ON_END参数会使得CPU等待I/O操作的完成。
在这里插入图片描述
SWITCH_ON_END参数会使得CPU等待I/O操作的完成。
该命令运行进程0发出I/O请求,CPU阻塞该进程,并执行I/O操作4个单位时间,然后进程0执行完成操作,同时CPU运行进程1的指令,运行4个单位时间,总用时9个单位时间。

(5) SWITCH_ON_IO参数会使得CPU在I/O操作时同时运行进程,为该模拟器的默认操作,结果与(3)一致

(6)(7) IO_RUN_IMMEDIATE参数 VS IO_RUN_LATER参数
为增强对比,减少I/O请求为2条,CPU进程为1个。
在这里插入图片描述
两者的区别在于I/O操作执行完是CPU先接受下一个I/O请求,还是运行进程指令。由于采用默认模式SWITCH_ON_IO,CPU执行指令与I/O操作同时进行,因而 IO_RUN_IMMEDIATE快一个单位时间。

(8)题为上述命令综合运用,提高不大,不做。

Code Homework: 进程API的使用

(1) 写个程序调用fork()。调用fork()之前,主程序使用并设置变量x的值。子程序中x的值是多少?同时在父程序和子程序中修改x的值会发生什么?

// p1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
  int x = 0;
  printf("father x is %d\n", x);	
  
  int rc = fork();
  if (rc < 0) {
    fprintf(stderr, "fork failed\n");
    exit(-1);
  } else if (rc == 0) {
    printf("child x is %d\n", x);	// 子进程
    x = 10;
    printf("child x has changed to %d\n", x);
    
  } else {							// 父进程
    x = 100;
    printf("father x has changed to %d\n", x);
  }
  exit(1);

  return 0;
}

在这里插入图片描述
可以看到,父进程和子进程的x初值均为0,并在各自的进程内变化为100和10,变量互不影响。

(2) 写个进程用open()打开一个文件,调用fork()创造新进程。父进程和子进程都能通过open()访问该文件么?它们同时写入文件时会发生什么?

函数 参数
open O_RDONLY只读模式、O_WRONLY只写模式、O_RDWR读写模式
read(int fd, void *buf, size_t count) 读取文件名fd,读入内存空间名buff,读入字节数count
write (int fd,const void * buf,size_t count) 写入文件名fd、读取内存空间名buff、读取字节数count
// p2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int main() {
  int fd = open("./p2.rtf", O_RDWR);
  
  char buff[20];
  int rc = fork();
  if (rc < 0) {
    fprintf(stderr, "fork failed\n");
    exit(-1);   
  } else if (rc == 0) {
    read(fd,buff,20);
    printf("child process read:%s\n", buff);
    write(fd, "child process write ", 20);
  } else {
    read(fd,buff,20);
    printf("father process read:%s\n", buff);
    write(fd, "father process write\n", 20);
  }
  exit(1);
  
  return 0;
}

在这里插入图片描述
可以看到,只有父进程成功读取文件的内容,但父进程和子进程都成功地写入文件。

(3) 写另一使用fork()的程序。子进程应该打印"hello";父进程应该打印"goodbye"。程序保证先打印子进程的内容,并且不在父进程使用wait()。

// p3.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
  
  int rc = fork();
  if (rc < 0) {
    fprintf(stderr, "fork failed\n");
    exit(-1);   
  } else if (rc == 0) {  // 第1个子进程
    printf("hello\n");
  } else {
    int id = fork();     // 第2个子进程
    if (id == 0) printf("goodbye\n");
  }
  exit(1);
  
  return 0;
}

在这里插入图片描述
主程序main()调用fork()创建子进程1输出hello,然后父进程内再次调用fork()创建子进程2输出goodbye。父进程和子进程同时执行,从进程树的角度看,子进程2比子进程1处于更深位置,执行时间更长,从而保证先输出hello

(4) 写个程序调用fork()exec()函数簇重载程序 /bin/ls,并分析exec()函数簇区别。

#include <stdio.h>   // fprintf printf stderr
#include <stdlib.h>  // exit()
#include <unistd.h>  // fork() execvp()
#include <string.h>  // strdup()
#include <fcntl.h>   // file control options

int main(int argc, char *argv[]) {
  int rc = fork();
  if (rc < 0) {
    fprintf(stderr, "fork failed");
    exit(-1);
  } else if (rc == 0) {
    printf("child process called\n");

    char *myargs[2];
    myargs[0] = strdup("ls");      // program:"ls"
    myargs[1] = NULL;              // mark end of array
    execvp(myargs[0], myargs);
    printf("this shouldn't print out");
  } else {
    int wc = wait(NULL);
    printf("father process called\n");
  }

  return 0;
}

在这里插入图片描述
成功调用fork()创建子进程,并运行ls程序显示当前桌面工作目录下的文件。

(5)(6) 写程序在父进程调用wait()会返回什么?如果在子进程中调用wait()会发生什么?如何使用waitpid()使其发挥与wait()的作用?
父进程调用wait()返回子进程的PID,子进程调用wait(),因为没有可以等待结束的进程,所有出错返回-1。waitpid()的函数原型为waitpid(pid_t pid,int *status,int options),设定pid为-1,options为0,则waitpid()函数就完全退化成了wait()函数。

(7) 写程序创建子进程,在子进程中关闭标准输出STDOUT_FILENO会对printf()有什么影响?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>

int
main(int argc, char *argv[])
{
    int rc = fork();
    if (rc < 0) {
      fprintf(stderr, "fork failed\n");
      exit(1);
    } else if (rc == 0) {
      close(STDOUT_FILENO); 
      printf("child process called\n");
    } else {
      printf("father process called\n");
    }
    return 0;
}

在这里插入图片描述
可以看出只有父进程的printf()语句得到输出,而子进程的printf()语句因为关闭STDOUT_FILENO得不到输出。

(8) 写程序创建两个子进程,使用pipe()将一个进程的标准输出作为另一个进程的标准输入。
学完管道相关知识回来。。。

发布了21 篇原创文章 · 获赞 8 · 访问量 1495

猜你喜欢

转载自blog.csdn.net/K_Xin/article/details/104679401