操作系统实验:Linux下的进程控制实验

一、实验目的:

加深对进程概念的理解,明确进程和程序的区别;掌握Linux操作系统的进程创建和终止操作,体会父进程和子进程的关系及进程状态的变化;进一步认识并发执行的实质,编写并发程序。

二、实验平台:

虚拟机:VMWare15.5.1
操作系统:CentOS7 64位
编辑器:Vim
编译器:Gcc

三、实验内容:

1.进程的创建与销毁

编写一段程序,使用系统调用fork()创建两个子进程,当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示“身份信息”:父进程显示“Parent process! PID=xxx1 PPID=xxx2”;子进程显示“Childx process! PID=xxx PPID=xxx”。多运行几次,观察记录屏幕上的显示结果,并分析原因。
说明:
xxx1为进程号,用getpid()函数可获取进程号;
xxx2为父进程号,用getppid()函数可获取父进程号;
Childx中x为1和2,用来区别两个子进程;
wait()函数用来避免父进程在子进程终止之前终止。
实验主要代码:

//print.c
#include<stdio.h>
int main(int argc,char *argv[])
{
	for(int i=0;i<argc;i++){
		printf("argv[%d]=%s\n",i,argv[i]);
	}
	printf("i am print.c,i am executed\n");
	return 0;
}
//example1.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(void)
{
	pid_t pid=fork();
	if(pid==0){   
		printf("Child1 process! PID=%d  PPID=%d \n",getpid(),getppid()); 
		if(execl("./print","print","hello",NULL)==-1) 
			printf("execl failed!");	
		printf("where am I?");
	}else if(pid>0){
		pid_t pid2=fork();
		if(pid2==0){
			printf("Child2 process! PID=%d  PPID=%d \n",getpid(),getppid());
		}else if(pid2>0){
			wait();
			printf("Parent process! PID=%d  PPID=%d \n",getpid(),getppid()); 
			exit(0);
		}else{
			printf("fork faild\n");
			exit(0);
		}
	}else{
		printf("fork faild\n");
		exit(0);
	}
}

实验过程:编辑print.c和example1.c两个文件,输入命令行对.c文件进行编译输出。
编译过程:#gcc print.c –o print
#gcc example1.c –o example1
运行截图如下:
请添加图片描述

进程控制相关函数

1.fork()
用于创建一个新进程(子进程)。
函数原型:
#include <unistd.h>
pid_t fork(void);
返回:子进程中为0,父进程中为子进程ID,出错为-1。
子进程被创建后就进入就绪队列和父进程分别独立地等待调度。子进程继承父进程的程序段代码,子进程被调度执行时,也会和父进程一样从fork()返回。
从共享程序段代码的角度来看,父进程和子进程所执行的程序代码是同一个,在内存中只有一个程序副本
但是从编程的角度来看,为了使子进程和父进程做不同的事,需要在程序中区分父进程和子进程的代码段。这需要借助于fork()的返回值来标志当前进程的身份。从fork()返回后,都会执行语句:pid=frok();
得到返回值pid有三种情况,分别对应出错(小于0)、在父进程中(大于0)、在子进程中(等于0)。
由于父进程和子进程各自独立地进入就绪队列等待调度,所以谁会先得到调度是不确定的,这与系统的调度策略和系统当前的资源状态有关。因此谁会先从fork()返回并继续执行后面的语句也是不确定的。


2.wait()
查询子进程的退出状态(即正常退出的退出码或导致异常终止的信号),并回收子进程的内核资源。
函数原型:
#include <sys/wait.h>
pid_t wait(int * status);
返回值:若成功则返回进程ID,若出错则返回-1。
Wait()函数常用来控制父进程和子进程的同步。在父进程中调用wait()函数,则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,会产生一个终止状态字,系统会向父进程发送SIGCHLD信号。当接到信号后,父进程提取子进程的终止状态字,从wait()函数返回继续执行原程序。


3. exit()
exit()是进程结束最常调用的函数,在main()函数中调用return,最终也是调用exit()函数。这些都是进程的正常终止。在正常终止时,exit()函数返回进程结束状态。
函数原型:
#include<stdio.h>
void exit(int status)
从OS角度来看,进程终止会释放进程用户空间的所有资源,而进程描述符并不释放,它将来由其父进程回收。若父进程先于子进程终止,子进程成为“孤儿进程”,“孤儿进程”会被init进程(进程号为1)领养;若子进程先于父进程终止,子进程成为“僵尸进程”,父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍占用的资源)。
程序可以执行到main()的最后一个“}“结束,也可以运行exit();结束,也可以运行到return 0;结束。


4.execl()
系统调用execl()可以将新程序加载到某一进程的内存空间,该进程会从新程序的main()函数开始执行。
函数原型:
#include <unistd.h>
int execl (const char * pathname, const char * arg0,…/*(char*)0*/)
pathname是要加载程序的全路径名,arg0及以后为参数列表。
进程的用户内存空间被替换,而进程的其他属性基本不改变。进程完整的内存空间:正文区、堆区、栈区、数据区都被替换,原内容不存在了。代码替换完后,在execl()后面的代码毫无意义

实验结果分析

先后四次运行,根据输出分析实验结果:

①每次运行创建的子进程Childx process的PPID均为父进程Parent process的PID,说明两个子进程均为同一父进程创建。

②每次运行的父进程Parent process的PPID均为3105,说明父进程也以子进程的形式存在,其父进程为同一系统进程。

③我们注意第一个if代码段,调用了execl()函数,将当前进程child1替换掉,所以之后的"where am I?”字符串并没有被输出,child1进程从print程序的main()开始执行,输出两个传参以及一段字符串,最终在main()的return 0;后结束。

④进程创建后由于父进程和子进程各自独立地进入就绪队列等待调度,所以谁会先得到调度是不确定,实验数据可以看出child1进程先运行和child2进程先运行各出现两次。

⑤父进程代码段因为调用了wait()函数,等待子进程结束后函数返回继续执行原程序,输出相应信息,所以父进程字符串最后才输出。

2.多进程并发执行

fork()和exec()系列函数能同时运行多个程序,利用上述函数将下面单进程顺序执行的程序single.c改造成可并发执行3个进程的程序multi_process.c;并用time命令获取程序的执行时间,比较单进程和多进程运行时间,并分析原因。

//single.c
#include <stdio.h> 
#define NUM 5 
int main(void) 
{ 
	void print_msg(char *m); 
	print_msg("Good "); 
	print_msg("Morning  "); 
	print_msg("007\n");  //将007替换为本人学号
	return 0; 
} 
void print_msg(char *m) 
{ 
	int i; 
	for(i = 0; i<NUM; i++){ 
		printf("%s",m); 
		fflush(stdout); 
		sleep(1);
	}
}

编译过程:#gcc single.c –o single
运行截图如下:
在这里插入图片描述
结果分析:单进程执行按次序依次打印五次三个字符串。
创建多个进程代码:

//print1.c
#include<stdio.h>
#define NUM 5
int main(int argc,char *argv[])
{
	for(int i=0;i<NUM;i++){
		printf("%s\n",argv[1]);
		fflush(stdout);
		sleep(1);
	}
	return 0;
}
//multi_process.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(void){
	pid_t pid[3];
	int i;
	for(int i=0;i<3;i++){
		pid[i]=fork();
		if(pid[i]==0){
			break;//防止子进程再次创建子进程
		}
	}
	if(pid[0]==0){
		execl("print1","print","Good",NULL);	
	}else{
		if(pid[1]==0){
			execl("print1","print","Hello",NULL);		
		}else{
			if(pid[2]==0) execl("print1","print","201919044217",NULL);
			wait();
			wait();
			wait();//调用一次,只能回收一个子进程
			exit(0);
		}
	}
	return 0;
}

编译过程:#gcc print1.c –o print1
#gcc multi_process.c –o multi
运行截图如下:
在这里插入图片描述

time命令

time命令
linux下time命令可以获取到一个程序的执行时间,包括程序的实际运行时间(real time),以及程序运行在用户态的时间(user time)和内核态的时间(sys time)。命令行执行结束时在标准输出中打印执行该命令行的时间统计结果,其统计结果包含以下数据:
1)实际时间(real time): 从command命令行开始执行到运行终止的消逝时间;
2)用户CPU时间(user CPU time): 命令执行完成花费的用户CPU时间,即命令在用户态中执行时间总和;
3)系统CPU时间(system CPU time): 命令执行完成花费的系统CPU时间,即命令在核心态中执行时间总和。
其中,用户CPU时间和系统CPU时间之和为CPU时间,即命令占用CPU执行的时间总和。实际时间要大于CPU时间,因为Linux是多任务操作系统,往往在执行一条命令时,系统还要处理其它任务。
用法:#time ./example

实验结果分析

多进程实验中因为是三个进程并发执行,所以时间上几乎是单个进程的1/3;因为是并发执行的,所以打印结果无序。

四、总结分析

通过两天的学习了解,从Linux系统虚拟机环境搭建,到vim编辑保存文本文件(其实我用的文本编辑器),到用gcc编译.c文件,运行查看结果这一套流程,更加深刻了解了Linux系统下进程控制方式,以及多进程并发执行效果。

  • 23
    点赞
  • 165
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
嵌入式系统通常对于资源和执行效率有严格的约束,导致了应用于嵌入式系统中的操作系统通常都提供非常有限的编程接口和系统支持。因此对于传统的嵌入式系统开发人员来说,开发难度相对较大,开发效率也相对较低。Java的出现缓解了这一情况。由于Java的跨平台特性,大批应用程序开发人员可以脱离硬件平台,在桌面的虚拟环境中开发嵌入式应用程序。 与传统的Java虚拟机不同,YJVM(Yet another JVM)是运行在嵌入式Linux内核中的Java虚拟机。实现了解释执行Java字节码、线程管理、执行环境管理、集成Classpath核心类库等基本功能。它可以使Linux在内核层次上支持Java的字节码,从而减少了软件层次、为提高效率留下空间。另外,YJVM中还构造了Java的进程管理,为在嵌入式环境中运行Java程序提供了更安全的环境。不同于纯粹用Java来实现的操作系统的尝试,这个方案更充分利用了现有的操作系统的实践成果,在不牺牲Java跨平台特性的前提下,更有实用价值。 本文从对嵌入式操作系统的讨论入手,结合Java虚拟机的原理和研究状况阐述了YJVM的设计和实现要点,体现了YJVM相比现有的嵌入式Java解决方案和Java操作系统的优势。本文的主要研究内容及成果包括: 1、作为一种嵌入式操作系统,嵌入式Linux的基本结构和与传统嵌入式操作系统相比的特点和优势。 2、嵌入式Java的传统解决方案和Java操作系统的基本结构。以及他们对于YJVM设计的影响和异同。 3、Java虚拟机的工作原理和结构。在YJVM中Java虚拟机的结构和实现方案之间的具体映射。 4、YJVM中程序装载、解释器、进程管理、垃圾收集和对Classpath核心类库集成的具体实现要点. 5、测试方案的制定的依据和结果;测试例程的构造和选择;测试结果的分析和在此基础上提出的改进和发展方向。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新西兰做的饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值