实验一 进程控制

实验一 进程控制

1.实验目的:

·加深对进程概念的理解,明确进程和程序的区别。
·掌握Linux系统中的进程创建,管理和删除等操作。
·熟悉使用Linux下的命令和工具,如man, find, grep, whereis, ps, pgrep, kill, ptree, top, vim, gcc,gdb, 管道|等。

2.基础知识:

·进程的创建

Linux中,载入内存并执行程序映像的操作与创建一个新进程的操作是分离的。将程序映像载入内存,并开始运行它,这个过程称为运行一个新的程序,相应的系统调用称为exec系统调用。而创建一个新的进程的系统调用是fork系统调用。

·exec系统调用
#include <unistd.h>
int execl (const char *path, const char *arg,...);

execl()将path所指路径的映像载入内存,arg是它的第一个参数。参数可变长。参数列表必须以NULL结尾。
通常execl()不会返回。成功的调用会以跳到新的程序入口点作为结束。发生错误时,execl()返回-1,并设置errno值。
例 编辑/home/kidd/hooks.txt:

int ret;
ret = execl (/bin/vi”, ”vi”,/home/kidd/hooks.txt”, NULL);
if (ret == -1)
perror (”execl”);
·fork系统调用
#include <sys/types.h>
#include <unistd.h>
pid_t fork (void);

成功调用fork()会创建一个新的进程,它与调用fork()的进程大致相同。发生错误时,fork()返回-1,并设置errno值。
例:

pid_t pid;
pid = fork ();
if (pid > 0)
printf (”I am the parent of pid=%d!\n”, pid);
else if (!pid)
printf (”I am the baby!\n”);
else if (pid == -1)
perror (”fork”);
·终止进程

exit()系统调用:

#include <stdlib.h>
void exit (int status);
·进程挂起

pause() 系统调用:

int pause( void );

函数pause会把进程挂起,直到接收到信号。在信号接收后,进程会从pause函数中退出,继续运行。

·wait(等待子进程中断或结束)
	#include<sys/types.h>
    #include<sys/wait.h>
        pid_t wait (int * status);

wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
如果在调用 wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。
子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一起返回。
如果不在意结束状态值,则参数status可以设成 NULL。

VIM常用命令速查:
在这里插入图片描述

3.实验内容

任务一:打开一个vi进程。通过ps命令以及选择合适的参数,只显示名字为vi的进程。寻找vi进程的父进程,直到init进程为止。记录过程中所有进程的ID和父进程ID。将得到的进程树和由pstree命令的得到的进程树进行比较。

1.首先打开一个新的进程
在这里插入图片描述
2.打开另一个终端 输入ps -A命令,来查看所有进程的ID
在这里插入图片描述
3.查找vi的进程ID是 25735在这里插入图片描述
4.查看cpu运行时间
在这里插入图片描述
5.调用ps -lax查看vi的进程信息在这里插入图片描述
6.查阅资料可知:pgrep可以通过程序名查找进程,可以判断程序是否正在运行,
xargs可以捕获命令的输出。两个程序一起使用可以完成进程的输出,再用ps一步步找到父进程
在这里插入图片描述
7.调用pstree得到进程树
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
8.对比两种方法获得的父进程,发现两个都是由systemd→lightdm→upstart→gnome-terminal→bash→vi。其中,systemd是一组系统管理命令,取代了init命令成为系统的第一个进程;LightDM即Light Display Manager,是一个全新的、轻量的Linux桌面的桌面显示管理器;upstart是一种基于事件通知机制启动相应服务的init进程,即较早版本的Ubuntu的初始化进程为init→upstart;gnome-terminal为终端的管理进程;bash是其中一套shell规范(类似于Ubuntu和Linux的关系),而shell是一套linux接口。

任务二:编写程序,首先使用fork系统调用,创建子进程。在父进程中继续执行空循环操作;在子进程中调用exec打开vi编辑器。然后在另外一个终端中,通过ps –Al命令、ps aux或者top等命令,查看vi进程及其父进程的运行状态,理解每个参数所表达的意义。选择合适的命令参数,对所有进程按照cpu占用率排序。
  1. 首先编写程序:

#include <stdio.h>
#include <unistd.h>
#include <stdib.h>
int main(){
int  pid=fork();
if(pid>0)
{
while(1);
}
else if (pid==0)
{
int ret;
ret=execlp("vi","",NULL);
if(ret==-1){
printf("exerl error\n");
exit(-1);
}
}
else{
printf("fork error\n");
exit(-1);
}
return 0;
       }
  1. 在终端调用程序
    在这里插入图片描述
    在这里插入图片描述

  2. 在另一个终端使用ps -Al命令,来查看现在正在进行的进程信息
    在这里插入图片描述

  3. 在所有进程信息中找到vi
    在这里插入图片描述

  4. 在终端输入ps aux命令,来查看在内存中运行的程序

在这里插入图片描述
6. 继续输入top命令,是所有进程按照cpu占有率来进行排队

在这里插入图片描述
可以发现刚刚运行的程序占了99%的cpu,位列第一

3、使用fork系统调用,创建如下进程树,并使每个进程输出自己的ID和父进程的ID。观察进程的执行顺序和运行状态的变化。

在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
int main(){

 pid_t pid;
 printf("p1:pid %d ppid %d\n",getpid(),getpid());
 pid = fork();
 if(pid > 0){
  pid =fork();
  if(pid == 0){
  printf("p3: pid %d ppid %d\n",getpid(),getppid());
  }
  if(pid > 0){
    pid = fork();
    if(pid == 0){
     ptintf("p2: pid %d ppid %d\n",getpid(),getppid());
     pid = fork();
     if(pid == 0){
         ptintf("p4: pid %d ppid %d\n",getpid(),getppid());
}
     if(pid >0){
       pid = fork();
       if(pid == 0){
           printf("p5: pid %d ppid %d\n",getpid(),getppid());
}
}
}
}      
}
return 0; 
}

在这里插入图片描述

和所要求的进程树相同,P1为P2和P3的父进程,P2位P4和P5的父进程。

修改上述进程树中的进程,使得所有进程都循环输出自己的ID和父进程的ID。然后终止p2进程(分别采用kill -9 、自己正常退出exit()、段错误退出),观察p1、p3、p4、p5进程的运行状态和其他相关参数有何改变。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
	pid_t p1;
	if(p1==0){
		pid_t p3;
		p3 = fork();
		if(p3 == 0){
			int i;
			for(i=0;i<10;i++){
				printf("p3 pid: %d, ppid: %d\n", getpid(),getppid());
				sleep(1);}
			return 0;}
		else if(p3 > 0){
			pid_t p2;
			p2 = fork();
			if(p2==0){
				pid_t p4;
				p4 = fork();
				if(p4 == 0){
					int i;
					for(i=0;i<10;i++){
						printf("p4 pid: %d, ppid: %d\n", getpid(),getppid());		
						sleep(1);}
					return 0;}
				else if(p4 >0){
					pid_t p5;
					p5 = fork();
					if(p5==0){
						int i;
						for(i=0;i<10;i++){
							printf("p5 pid: %d, ppid: %d\n", getpid(),getppid());
							sleep(1);}
						return 0;}
					else{//p2
						int i;
						for(i=0;i<10;i++){
							if(i==5)
								exit(1);
/*段错误
							if(i==5)
							{
								int *p=NULL;
								*p=0;
							}*/
							printf("p2 pid: %d, ppid: %d\n", getpid(),getppid());
							sleep(1);}
						return 0;}}}}
		int i;
		for(i=0;i<10;i++)
		{printf("p1 pid: %d, ppid: %d\n", getpid(),getppid());
			sleep(1);}
		return 0;}
	sleep(1);}

运行截图
在这里插入图片描述

github代码连接 https://github.com/mitsdisy/reimagined-octo-adventure/tree/master

<think>嗯,用户想了解操作系统实验进程控制实验报告内容指导。首先,我需要回忆进程控制实验通常包括哪些内容。根据引用[2],实验内容主要是进程的创建,使用fork()创建两个子进程,父进程进程分别输出不同的字符。这应该是基础部分。 接下来,用户可能需要详细的步骤指导,比如实验目的、环境、步骤代码示例。实验目的可能包括理解进程创建机制、掌握fork()的使用,以及父子进程的执行顺序。实验环境通常是Linux系统,使用C语言gcc编译器。 然后,我需要确保提供的代码正确,能够创建两个子进程,并且每个进程输出对应的字符。要注意fork()的返回值,父进程进程如何区分。代码中可能会有两个fork()调用,或者个fork()之后在父进程进程中再调用次,但这样可能导致更多的进程,需要仔细检查。 另外,用户可能对实验结果分析有疑问,比如为什么输出顺序不确定,这是因为进程调度的问题,引用[3]提到进程调度程序动态分配处理机,所以结果可能有多种顺序。需要提醒用户多次运行观察结果。 可能的问题部分,用户可能会问fork()的返回值,父子进程执行顺序,以及如何避免僵尸进程。这些问题需要简明扼要地回答,引用相关知识点,比如wait()函数的使用。 最后,生成相关问题时要围绕进程控制的其他方面,比如多进程通信、exec函数族的作用,或者进程调度策略,这样可以帮助用户扩展知识。确保所有回答符合格式要求,使用正确的中文,LaTeX格式,并且引用标记正确。</think>### 操作系统实验进程控制实验指导 #### 实验目的 1. 理解进程的创建机制与父子进程关系 2. 掌握`fork()`系统调用的使用 3. 观察进程并发执行特性 4. 学习通过代码控制进程行为(引用[2]中要求父进程与子进程输出不同字符) #### 二、实验环境 - Linux操作系统 - C语言编程环境(gcc编译器) - 终端命令行工具 #### 三、实验原理 1. **进程创建**:通过`fork()`系统调用创建子进程,新进程复制父进程的代码段、数据段等资源 2. **进程标识**: - `fork()`返回0表示子进程 - 返回正数(子进程PID)表示父进程 - 返回-1表示创建失败 3. **并发执行**:父子进程执行顺序由操作系统调度决定(引用[3]中提到的进程调度机制) #### 四、实验步骤与代码示例 ```c #include <stdio.h> #include <unistd.h> int main() { pid_t pid1, pid2; // 第次fork pid1 = fork(); if (pid1 < 0) { fprintf(stderr, "Fork failed"); return 1; } else if (pid1 == 0) { // 子进程1 printf("b\n"); } else { // 父进程 // 第二次fork pid2 = fork(); if (pid2 < 0) { fprintf(stderr, "Fork failed"); return 1; } else if (pid2 == 0) { // 子进程2 printf("c\n"); } else { // 父进程 printf("a\n"); wait(NULL); // 等待子进程结束 wait(NULL); } } return 0; } ``` #### 五、实验结果分析 - 可能出现多种输出顺序(如`a b c`或`b a c`等) - 结果不确定性源于操作系统进程调度策略(引用[3]中提到的动态分配处理机特性) - 建议多次运行观察不同结果 #### 六、常见问题解答 1. **Q: 为什么`fork()`会有多个返回值?** A: 该函数通过返回值区分父子进程,父进程获得子进程PID,子进程得到0[^2] 2. **Q: 如何保证父进程最后结束?** A: 使用`wait()`系统调用让父进程等待子进程终止 3. **Q: 出现僵尸进程怎么办?** A: 正确使用`wait()`/`waitpid()`回收子进程资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值