操作系统实验——进程创建与进程间通信

操作系统实验——进程创建与进程间通信

实验报告在和代码在文章底部


实验目的

  1. 理解PCB以及系统调用的概念。
  2. 理解进程创建以及进程并发执行的过程。
  3. 掌握fork系统调用和exec系统调用的用法。
  4. 掌握使用fork创建多个子进程以及进程树的方法。
  5. 了解进程间通信的常用方法。

实验内容


一、创建进程树

编写程序创建进程树如图1和图2所示,在每个进程中显示当前进程标识getpid()和父进程标识getppid()。

在这里插入图片描述
实验分析:

创建流程是先由a创建b进程,b进程创建c进程,c进程创建d进程

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include<sys/wait.h>
#include<sys/types.h>

int main()
{
  pid_t pida,pidb,pidc,pidd;
  while((pidb=fork())==-1);
  if(pidb>0) 
    {
    	wait(0); 
      	printf("Apid=%d,ppid=%d\n",getpid(),getppid());
    }
  else  //p
   {
     while((pidc=fork())==-1);
     if(pidc>0)   
       	{	
       	 wait(0); 
         printf("Bpid=%d,pidd=%d\n",getpid(),getppid());
		}
     else //p
       {
		while((pidd=fork())==-1);
	     	if(pidd >0)   //p2
	      	{
	      		wait(0); 
				printf("Cpid=%d,pidd=%d\n",getpid(),getppid());
			}
	     	else //p
	        {	
				printf("Dpid=%d,pidd=%d\n",getpid(),getppid());
			}   
	    }   
    }

}

运行结果:

在这里插入图片描述


同样的思路,图二中的创建流程为:
由父进程a同时建立b,c子进程,再由b进程创建d子进程,c子进程再创建e、f子进程

运行结果如下:

在这里插入图片描述


二、vfork以及execl的使用

实验要求一:

统计创建的子进程的数量,并在主进程中正确显示

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
int main(){
	signal(SIGCHLD,SIG_IGN);
	int num = 0; 
	pid_t pid1,pid2,pid3,pid4,pid5;
	while((pid1 = vfork()) == -1);
	if(pid1 == 0)
	{
		while((pid2=vfork())==-1);
		if(pid2 == 0)
		{
			wait(0);
			num++;
			printf("Dpid is %d , Dppid is %d\n",getpid(),getppid());
			_exit(0);
		}
		else
		{
			wait(0);
			num++;
			printf("Bpid is %d , Bppid is %d\n",getpid(),getppid());
			_exit(0);
		}
	}
	else
	{
		while((pid3=vfork())==-1);
		if(pid3 == 0)
		{
			while((pid4=vfork())==-1);
			if(pid4 == 0)
			{
				wait(0);
				num++;
				printf("Fpid is %d , Fppid is %d\n",getpid(),getppid());
				_exit(0); 
			}
			else
			{
				while((pid5=vfork())==-1);
				if(pid5 == 0)
				{
					wait(0);
					num++;
					printf("Epid is %d , Eppid is %d\n",getpid(),getppid());
					_exit(0);
				}
				else
				{
					wait(0);
					num++;
					printf("Cpid is %d , Cppid is %d\n",getpid(),getppid());
					_exit(0);
				}	
			}			
		}
		else
		{
			wait(0);
			printf("Apid is %d , Appid is %d\n",getpid(),getppid());
			printf("childnum is %d\n",num);			
		}
	}
}

实验结果:

在这里插入图片描述


实验要求二:

创建一个子进程,并在后台运行上述任意一个可执行文件,观察程序的运行结果。

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
int main(){
	pid_t pid1;
	while((pid1=fork())==-1);
	if(pid1==0)
	{
		wait(0);
		printf("This is child\n");
	}
	else
	{
		printf("This is father\n");
		execlp("./test3","5a",NULL);
	}
}

实验结果:

在这里插入图片描述


三、创建进程以及进程间通信(管道)的综合运用


实验要求:

主进程输入一个字符串,然后创建两个子进程:一个是发送进程,通过管道向另一个子进程发送一个字符串;另一个是接收进程,该子进程接收字符串后,启动一个后台加密程序对字符串进行加密并输出密文。

提示:需要使用系统调用fork(), execl(),pipe(),wait()/waitpid()


加密代码:

//encrypt.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char * argv[])
{
   int i=0;
   int len;
   if(argc<=1) {printf("%d\n",argc);return 0;}
   char * s =argv[1];
   printf("启动后台加密进程,进程号:%d\n",getpid());
   len = strlen(s);//取长度
   for(i=0;i<len;i++){
         if((s[i]>64 && s[i]<91) || (s[i]>96 && s[i]<123)){ //字符串加密
             if(s[i]=='z') s[i]='A';
             else if(s[i]=='Z') s[i]='a';
             else s[i] = s[i] + 1;             
         }
         else s[i] = s[i];
    }
    printf("加密后:%s\n",s);   
    return 1;  
}


具体代码:

见底部文件


实验结果:

在这里插入图片描述



实验代码和报告

文件下载地址

操作系统 进程创建实验报告 调用fork( )创建进程 实验原理: 一) 进程 UNIX中,进程既是一个独立拥有资源的基本单位,又是一个独立调度的基本单位。一个进程实体由若干个区(段)组成,包括程序区、数据区、栈区、共享存储区等。每个区又分为若干页,每个进程配置有唯一的进程控制块PCB,用于控制和管理进程。 PCB的数据结构如下: 1、进程表项(Process Table Entry)。包括一些最常用的核心数据: 进程标识符PID、用户标识符UID、进程状态、事件描述符、进程和U区在内存或外存的地址、软中断信号、计时域、进程的大小、偏置值nice、指向就绪队列中下一个PCB的指针P_Link、指向U区进程正文、数据及栈在内存区域的指针。 2、U区(U Area)。用于存放进程表项的一些扩充信息。 每一个进程都有一个私用的U区,其中含有:进程表项指针、真正用户标识符u-ruid(read user ID)、有效用户标识符u-euid(effective user ID)、用户文件描述符表、计时器、内部I/O参数、限制字段、差错字段、返回值、信号处理数组。 由于UNIX系统采用段页式存储管理,为了把段的起始虚地址变换为段在系统中的物理地址,便于实现区的共享,所以还有: 3、系统区表项。以存放各个段在物理存储器中的位置等信息。 系统把一个进程的虚地址空间划分为若干个连续的逻辑区,有正文区、数据区、栈区等。这些区是可被共享和保护的独立实体,多个进程可共享一个区。为了对区进行管理,核心中设置一个系统区表,各表项中记录了以下有关描述活动区的信息: 区的类型和大小、区的状态、区在物理存储器中的位置、引用计数、指向文件索引结点的指针。 4、进程区表 系统为每个进程配置了一张进程区表。表中,每一项记录一个区的起始虚地址及指向系统区表中对应的区表项。核心通过查找进程区表和系统区表,便可将区的逻辑地址变换为物理地址。 二) 进程映像 UNIX系统中,进程进程映像的执行过程,也就是正在执行的进程实体。它由三部分组成: 1、用户级上、下文。主要成分是用户程序; 2、寄存器上、下文。由CPU中的一些寄存器的内容组成,如PC,PSW,SP及通用寄存器等; 3、系统级上、下文。包括OS为管理进程所用的信息,有静态和动态之分。 三) 所涉及的系统调用 1、fork( ) 创建一个新进程。 系统调用格式: pid = fork( ) 参数定义: int fork( ) fork( )返回值意义如下: 0:在子进程中,pid变量保存的fork( )返回值为0,表示当前进程是子进程。 >0:在父进程中,pid变量保存的fork( )返回值为子进程的id值(进程唯一标识符)。 -1:创建失败。 如果fork( )调用成功,它向父进程返回子进程的PID,并向子进程返回0,即fork( )被调用了一次,但返回了两次。此时OS在内存中建立一个新进程,所建的新进程是调用fork( )父进程(parent process)的副本,称为子进程(child process)。子进程继承了父进程的许多特性,并具有进程完全相同的用户级上下文。父进程进程并发执行。 核心为fork( )完成以下操作: (1)为新进程分配一进程表项和进程标识符 进入fork( )后,核心检查系统是否有足够的资源来建立一个新进程。若资源不足,则fork( )系统调用失败;否则,核心为新进程分配一进程表项和唯一的进程标识符。 (2)检查同时运行的进程数目 超过预先规定的最大数目时,fork( )系统调用失败。 (3)拷贝进程表项中的数据 将父进程的当前目录和所有已打开的数据拷贝到子进程表项中,并置进程的状态为“创建”状态。 (4)子进程继承父进程的所有文件 对父进程当前目录和所有已打开的文件表项中的引用计数加1。 (5)为子进程创建进程上、下文 进程创建结束,设子进程状态为“内存中就绪”并返回子进程的标识符。 (6)子进程执行 虽然父进程进程程序完全相同,但每个进程都有自己的程序计数器PC(注意子进程的PC开始位置),然后根据pid变量保存的fork( )返回值的不同,执行了不同的分支语句。
实验进程管理   Windows所创建的每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每一进程都以调用ExitProcess() 或TerminateProcess() API函数终止。通常应用程序的框架负责调用 ExitProcess() 函数。对于C++ 运行库来说,这一调用发生在应用程序的main() 函数返回之后。 1. 创建进程 CreateProcess() 调用的核心参数是可执行文件运行时的文件名及其命令行。表 2-1详细地列出了每个参数的类型和名称。   表2-1 CreateProcess() 函数的参数 参数名称 使用目的 LPCTSTR lpApplivationName 全部或部分地指明包括可执行代码的EXE文件的文件名 LPCTSTR lpCommandLine 向可执行文件发送的参数 LPSECURIITY_ATTRIBUTES lpProcessAttributes 返回进程句柄的安全属性。主要指明这一句柄是否应该由其他子进程所继承 LPSECURIITY_ATTRIBUTES lpThreadAttributes 返回进程的主线程的句柄的安全属性 BOOL bInheritHandle 一种标志,告诉系统允许新进程继承创建进程的句柄 DWORD dwCreationFlage 特殊的创建标志 (如CREATE_SUSPENDED) 的位标记 LPVOID lpEnvironment 向新进程发送的一套环境变量;如为null值则发送调用者环境 LPCTSTR lpCurrentDirectory 新进程的启动目录 STARTUPINFO lpStartupInfo STARTUPINFO结构,包括新进程的输入和输出配置的详情 LPPROCESS_INFORMATION lpProcessInformation 调用的结果块;发送新应用程序的进程和主线程的句柄和ID   可以指定第一个参数,即应用程序的名称,其中包括相对于当前进程的当前目录的全路径或者利用搜索方法找到的路径;lpCommandLine参数允许调用者向新应用程序发送数据;接下来的三个参数进程和它的主线程以及返回的指向该对象的句柄的安全性有关。 然后是标志参数,用以在dwCreationFlags参数中指明系统应该给予新进程什么行为。经常使用的标志是CREATE_SUSPNDED,告诉主线程立刻暂停。当准备好时,应该使用ResumeThread() API来启动进程。另一个常用的标志是CREATE_NEW_CONSOLE,告诉新进程启动自己的控制台窗口,而不是利用父窗口。这一参数还允许设置进程的优先级,用以向系统指明,相对于系统中所有其他的活动进程来说,给此进程多少CPU时间。 接着是CreateProcess() 函数调用所需要的三个通常使用缺省值的参数。第一个参数是lpEnvironment参数,指明为新进程提供的环境;第二个参数是lpCurrentDirectory,可用于向主创进程发送缺省目录不同的新进程使用的特殊的当前目录;第三个参数是STARTUPINFO数据结构所必需的,用于在必要时指明新应用程序的主窗口的外观。 CreateProcess() 的最后一个参数是用于新进程对象及其主线程的句柄和ID的返回值缓冲区。以PROCESS_INFORMATION结构中返回的句柄调用CloseHandle() API函数是重要的,因为如果不将这些句柄关闭的话,有可能危及主创进程终止之前的任何未释放的资源。 2. 正在运行的进程 如果一个进程拥有至少一个执行线程,则为正在系统中运行的进程。通常,这种进程使用主线程来指示它的存在。当主线程结束时,调用ExitProcess() API函数,通知系统终止它所拥有的所有正在运行、准备运行或正在挂起的其他线程。当进程正在运行时,可以查看它的许多特性,其中少数特性也允许加以修改。 首先可查看的进程特性是系统进程标识符 (PID) ,可利用GetCurrentProcessId() API函数来查看,GetCurrentProcess() 相似,对该函数的调用不能失败,但返回的PID在整个系统中都可使用。其他的可显示当前进程信息的API函数还有GetStartupInfo()和GetProcessShutdownParameters() ,可给出进程存活期内的配置详情。 通常,一个进程需要它的运行期环境的信息。例如API函数GetModuleFileName() 和GetCommandLine() ,可以给出用在CreateProcess() 中的参数以启动应用程序。在创建应用程序时可使用的另一个
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

失散多年的哥哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值