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()
将一个进程的标准输出作为另一个进程的标准输入。
学完管道相关知识回来。。。