第一题、
0.试用ps命令
1.打开一个vi进程。通过ps命令以及选择合适的参数,只显示名字为vi的进程。
2.找vi进程的父进程,直到init进程为止。记录过程中所有进程的ID和父进程ID。
由之前查找得vi指令的pid=2720。
3.将得到的进程树和由pstree命令的得到的进程树进行比较
得到的进程树为:2720—>2710—>2268—>1579—>1562—>941—>1(init指令)
Pstree命令得到的进程树为:
4.比较不同:
第二题、
1.编写程序,首先使用fork系统调用,创建子进程。在父进程中继续执行空循环操作;在子进程中调用exec打开vi编辑器。
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
pid_t fpid;
fpid=fork();
if(fpid<0)printf("error in fork!");
else if(fpid==0)
{
execl("/usr/bin/vi","vi","/home/zym/lll.txt",NULL);
}
else{
for( int i=0;;i++){}
}
return 0;
}
2.然后在另外一个终端中,通过ps –Al命令、ps aux或者top等命令,查看vi进程及其父进程的运行状态。
上图:运行forktest程序即可进入子进程“vi”界面
上图:输入ps aux指令查看父进程(./forktest)处于运行状态,子进程(vi)处于休眠状态。
上图:查看两个进程的进程关系可以得知,vi进程为父进程(./forktest)的子进程。
3.理解每个参数所表达的意义。选择合适的命令参数,对所有进程按照cpu占用率排序
每个参数所表达的意义:
- F: 代表这个程序的旗标 (flag), 4 代表使用者为 superuser;
- S: 代表这个程序的状态 (STAT);
- UID: 代表执行者身份
- PID: 进程的ID号!底下的 PPID 则父进程的ID;
- C: CPU 使用的资源百分比
- PRI: 指进程的执行优先权(Priority的简写),其值越小越早被执行;
- NI: 这个进程的nice值,其表示进程可被执行的优先级的修正数值。
- ADDR: 这个是内核函数,指出该程序在内存的那个部分。如果是个执行
- SZ: 使用掉的内存大小;
- WCHAN: 目前这个程序是否正在运作当中,若为 - 表示正在运作;
- TTY: 登入者的终端机位置啰;
- TIME: 使用掉的 CPU 时间。
- CMD: 所下达的指令名称
在STAT中对应字母的意义为:
- D :不可中断
- R 正在运行,或在队列中的进程
- S 处于休眠状态
- T 停止或被追踪
- Z 僵尸进程
- W 进入内存交换(从内核2.6开始无效)
- X 死掉的进程
- < 高优先级
- N 低优先级
- L 有些页被锁进内存
- s 包含子进程
- + 位于后台的进程组;
- l 多线程,克隆线程
上图:运用“top”指令将所有进程按照CPU占用率进行排序。
第三题、
使用fork系统调用,创建如下进程树,并使每个进程输出自己的ID和父进程的ID。观察进程的执行顺序和运行状态的变化。
为了创建进程树,编写代码如下:
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
int P1pid=getpid();
int P2pid,P3pid,P4pid,P5pid;
printf("\nin P1,pid=%d\n",P1pid);
pid_t P2,P3,P4,P5;
P2=fork();
if(P2<0)printf("error in fork!");
else if(P2==0)
{
P2pid=getpid();
printf("in P2,pid=%d,fatherpid=%d\n",P2pid,getppid());
P4=fork();
if(P4==0)
{
P4pid=getpid();
printf("in P4,pid=%d,fatherpid=%d\n",P4pid,getppid());
}
else{P5=fork();
if(P5==0)
{
P5pid=getpid();
printf("in P5,pid=%d,fatherpid=%d\n",P5pid,getppid());
}}
}
else
{P3=fork();
if(P3<0)printf("error in fork!");
else if(P3==0)
{
P3pid=getpid();
printf("in P3,pid=%d,fatherpid=%d\n",P3pid,getppid());
}}
return 0;
}
- 其中运用getpid()和getppid(),分别得到子进程的进程号和其父进程的进程号。
但是两次不同的代码使得得到的程序运行结果不同。
- 上图中,是将P4与P5、P2与P3并列执行,即:在执行P4后,输出P4的pid与ppid,之后,不管是否此进程是否是P4仍旧执行fork()命令创建P5。(P2与P3同理)。
- 而下图中则是判断P4的faid是否等于0,若等于0,则输出P4的pid,ppid,若不等于0,则执行fork()函数,生成P5子进程。(P2与P3同理)。
- 从上图中可以看出,第一种编写程序的方法会多次输出P3 P5,但进程号不同,则证明产生的子进程仍可以执行fork()函数,生成子进程的子进程。不同的子进程生成的子进程进程号也不同。经查阅得知:一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
- 下图中则是按照题目要求的进程树,构建子进程,由于每次的进程完成时间不同导致P3 P4 P5输出的顺序是随机的,同时由于过程完成的太快(没有无限循环输出此进程的pid,ppid),所以有的子进程要比父进程先执行完,此时子进程则成为了孤儿进程,在我的电脑中他被进程号为1582的upstart进程收养。从输出的结果来看,没有成为孤儿进程的进程的pid与ppid都是符合条件的。
- 上述两种代码虽然没有能够准确地看出其pid,及其父进程的pid,但是让我对fork函数以及子进程,父进程之间的关系有了更深入的了解。同时也了解了孤儿进程,僵尸进程,以及僵尸进程的危害和防止方法等。
下面是第二种形式的代码(即运行会得出上图的运行结果)
- 在第四题的代码中,我将其改为无限循环,则不会出现孤儿进程。同时能够清楚地看见子进程与父进程之间的关系,画出进程树即为上图。
第四题、
1.修改上述进程树中的进程,使得所有进程都循环输出自己的ID和父进程的ID。
修改后的代码如下:
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
int P1pid=getpid();
int P2pid,P3pid,P4pid,P5pid;
pid_t P2,P3,P4,P5;
P2=fork();
if(P2==0)
{
P4=fork();
if(P4==0)
{
for(;;)printf("in P4,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
else
{
P5=fork();
if(P5==0)
{
for(;;)printf("in P5,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
}
for(;;)printf("in P2,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
else
{
P3=fork();
if(P3<0)printf("error in fork!");
else if(P3==0)
{
for(;;)printf("in P3,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
}
for(;;)printf("\nin P1,pid=%d\n",P1pid);
return 0;
}
程序运行截图如下:
- 下图:运用top指令可以看出 现在有关的进程共有五个,即为P1,P2,P3,P4,P5五个进程
- 下图则可以看出 五个进程之间的子父关系
- 运用kill -9 语句杀死P2进程,可以看到,已经P2已经不再输出,并且P4,P5作为P2的子进程已经不再有父进程,成为了孤儿进程。
之后再在代码中P2加入exit,代码如下
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
int P1pid=getpid();
int P2pid,P3pid,P4pid,P5pid;
pid_t P2,P3,P4,P5;
P2=fork();
if(P2==0)
{
P4=fork();
if(P4==0)
{
for(;;)printf("in P4,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
else
{
P5=fork();
if(P5==0)
{
for(;;)printf("in P5,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
}
printf("in P2,pid=%d,fatherpid=%d\n",getpid(),getppid());exit(0);
}
else
{
P3=fork();
if(P3<0)printf("error in fork!");
else if(P3==0)
{
for(;;)printf("in P3,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
}
for(;;)printf("\nin P1,pid=%d\n",P1pid);
return 0;
}
查看进程 可以看到P2的状态为defunct,同时P4,P5也成为了孤儿进程。
段错误退出
代码如下:
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
int P1pid=getpid();
int P2pid,P3pid,P4pid,P5pid;
pid_t P2,P3,P4,P5;
P2=fork();
if(P2==0)
{
P4=fork();
if(P4==0)
{
for(;;)printf("in P4,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
else
{
P5=fork();
if(P5==0)
{
for(;;)printf("in P5,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
}
for(;;){
printf("in P2,pid=%d,fatherpid=%d\n",getpid(),getppid());
int *p=NULL;
*p=1;
}
}
else
{
P3=fork();
if(P3<0)printf("error in fork!");
else if(P3==0)
{
for(;;)printf("in P3,pid=%d,fatherpid=%d\n",getpid(),getppid());
}
}
for(;;)printf("\nin P1,pid=%d\n",P1pid);
return 0;
}
P2进程已经退出
下图可以看出其子进程变成了孤儿进程
github 代码链接:https://github.com/zymmoa/OS-Labs/tree/master/Lab2