实验二:进程控制

本文通过实验展示了如何使用ps、pstree等命令控制和观察进程,包括创建进程树、使用fork系统调用、处理孤儿进程和僵尸进程等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一题、

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值