操作系统实验二报告
Y,xj
一,进程树寻找
-
实验要求:打开一个vi进程。通过ps命令以及选择合适的参数,只显示名字为vi的进程。寻找vi进程的父进程,直到init进程为止。记录过程中所有进程的ID和父进程ID。将得到的进程树和由pstree命令的得到的进程树进行比较
-
实验步骤:
- (一) 使用PS命令查询父进程
- 使用ps -ef命令 显示所有进程 寻找vi的父进程
- 根据以上三个图我们可以寻找到进程号为3461的vi进程,通过寻找其父进程可以找到进程树 3461—>3450—>3442—>2681—>1658—>1025—>1
- (二)使用PSTREE命令查询进程树
- 终端输入pstree可得到如下进程树
- 可以看到 我们得到的进程树结果是一样的
二,fork函数调用
- 实验要求:编写程序,首先使用fork系统调用,创建子进程。在父进程中继续执行空循环操作;在子进程中调用exec打开vi编辑器。然后在另外一个终端中,通过ps –Al命令、ps aux或者top等命令,查看vi进程及其父进程的运行状态,理解每个参数所表达的意义。选择合适的命令参数,对所有进程按照cpu占用率排序。
- 实验步骤:
-
(一)熟悉fork函数使用操作:
-
调用fork函数,系统会将代码复制,并新建一个进程继续往下执行,会产生两个返回值,若返回值为
-1
,则说明创建子进程失败,若返回值为0
,则进入子进程,若返回值是一个大于0
的数,则此部分为主进程,这个大于0的数为子进程的进程号 -
(二)程序编写:
-
实验要求子进程打开vi编辑器,主进程实现空循环。
-
子进程通过execl函数调用vi命令打开编辑器
-
主进程用空循环来起到保持程序运行状态的作用
#include<stdio.h> #include<unistd.h> #include<stdlib.h> int main() { int pid; pid=fork(); switch(pid) { case -1: printf("fork fail!\n"); exit(1); case 0: //子进程打开vi编辑器部分 execl("/usr/bin/vi","vi","/home/yxj/Desktop/OStest2",NULL); printf("exec fail!\n"); exit(1); default: //主进程执行for循环维持进程正常执行 for (int i=0;;i++){}; printf("vi completed!\n"); exit(1); } }
-
(注):主进程若不写循环,则主进程会先于子进程执行完,则子进程会出现
<defunct>
僵尸进程的情况,如下图前两次 -
-
上图第三次
ps -a
命令显示的结果为正确执行结果,子进程打开了vi编辑器,主进程依旧在执行 -
(三)查看每个进程的状态及cpu占用
-
-
终端输入
top
可以看到,改命令将主机中的所有进程全部动态显示了出来,并且按照cpu使用率进行了排序,主进程cpu占用率为99.7%,状态为R,是执行状态,下面的sl状态是休眠状态
-
三,fork函数创建进程树
-
实验要求:使用fork系统调用,创建如下进程树,并使每个进程输出自己的ID和父进程的ID。观察进程的执行顺序和运行状态的变化。
-
实验步骤:
- 代码逻辑分析:P1为此代码执行的主进程,产生2个子进程P2,P3,在P2进程中,再fork创建两个子进程P4,P5
参考代码
#include "stdio.h"
#include "sys/types.h"
#include "unistd.h"
#include "stdlib.h"
int main()
{
pid_t p1, p2, p3, p4, p5;
int cnt=0;
printf("p1's pid is %d,and its parent_pid is %d\n", getpid(),getppid());
//输出P1主进程的进程号和父进程号
while ((p2 = fork()) == -1);
if (p2==0) //P2进程
{
printf("p2's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
p4 = fork();
if (p4 == 0) //进入P4子进程
{
printf("p4's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
}
else{
p5 = fork();
if (p5 == 0) //进入P5子进程
{
printf("p5's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
}}
}
else
{
p3=fork();
if (p3 == 0) //进入P3子进程
{
printf("p3's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
}
}
}
-
运行结果:
-
在实验中发现,如果
if
后面不加else
,则会多创建出来一些进程,原因是每个进程所复制的代码都会将后面没有else
约束的创建进程代码执行一遍,所得结果如下图所示: -
-
该结果中产生了2个P5进程,可以观察到他们的进程号不同,实则是2个进程
-
在每个
if
后面加上else
后,输出则为期望输出,如下图所示: -
-
-
从两次所得到的结果可以看出,进程创建的顺序并不一样
[注]:我们看到有几个程序的父进程是很奇怪的(如
2859
,2172
),进程号和其他差很多的一个进程,结果查询我们发现可能是由于父进程提前结束,而导致此进程变成了一个孤儿进程
(interesting的名字),都交由一个系统的upstart
父进程统一管理(即上述进程号的进程)。
-
四,终止进程
-
实验要求:修改上述进程树中的进程,使得所有进程都循环输出自己的ID和父进程的ID。然后终止p2进程(分别采用kill -9 、自己正常退出exit()、段错误退出),观察p1、p3、p4、p5进程的运行状态和其他相关参数有何改变。
-
实验步骤:
(一)修改代码:由于要实现不停的循环输出进程号,所以为了避免程序卡在输出不动,将每个子进程的输出语句都放在进程创建的后面。
参考代码
#include "stdio.h"
#include "sys/types.h"
#include "unistd.h"
#include "stdlib.h"
int main()
{
pid_t p1, p2, p3, p4, p5;
int cnt = 0;
while ((p2 = fork()) == -1); //防止进程创建失败
if (p2 == 0) //P2进程
{
int a[5];
p4 = fork();
if (p4 == 0)
{
while (1)
{
printf("p4's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
}
}
else {
p5 = fork();
if (p5 == 0)
{
while (1)
{
printf("p5's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
}
}
}
while (1)
{
printf("p2's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
}
}
else
{
p3 = fork();
if (p3 == 0)
{
while (1)
{
printf("p3's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
}
}
}
while (1)
{
printf("p1's pid is %d,and its parent_pid is %d\n", getpid(), getppid());
}
}
(二)执行程序:执行程序后每个进程都循环输出自己的进程号和父进程号,如下图所示:
每个进程状态如下
(三)kill杀死进程2:找到进程2的pid号4937
,在另一个终端中输入kill -9 -4937
,终止掉进程2
可以看到,进程2已经成为defunct状态,其子进程的父进程都变成了2859,都成为了孤儿
哈哈哈哈哈,
(四)exit退出进程:在代码中p2的部分添加一行exit(0);
可以看到,进程2的状态变为了Zombie
(Z),僵尸程序状态,其两个子进程又变成了孤儿
**(五)Segmentation Fault:**在代码中p2子进程下加入一行段错误程序
int *p=NULL;
*p=3;
进程二就会自动退出,结果如下:
五,代码github地址:
https://github.com/Yxj-yxj/OS/tree/master/Lab2