操作系统实验一 并发程序设计

前言

实验并不难,仔细看完我的文章后,你应该就会对实验有进一步的理解。希望大家能仔细看完并且真正理解,而不是复制代码。

 1.实验目的


       掌握Linux环境下,多进程之间并发程序设计方法,并通过程序的运行结果来验证分时系统和并发程序设计的优越性。

2.实验要求


熟悉Linux操作系统子进程创建方法以及任务执行时间测量方法

 3.实验内容


       在单进程(单用户、单任务)运行时,系统资源为单进程所独占,当进程在读/写磁盘文件时,CPU是处于等待I/O完成的空闲状态,因此造成较大的资源浪费。
在多进程运行(多任务)时,当某进程在等待I/O结果时,可以自动阻塞(blocked)起来,CPU可选择另一个就绪(ready)进程来执行,从而提高系统的运行效率,增加系统的吞吐量,提高系统交互操作性能。

        本实验包含两个任务:一个任务将一段内容写入磁盘文件中,另一个任务将做求和计算。
        1.    设计两进程顺序执行程序,并测试所需时间
        2.    设计两进程并发执行程序,并测试所需时间
        分别测量出写入磁盘文件任务和计算任务所需时间。
        使用操作系统所学的树结构描述出本实验并发执行程序父子进程之间的关系。

4.实验的内容与过程

阅读代码

进行实验前,我们先将代码大致读懂,才能进行相关实验。

mainPro.c文件:

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

#include "ComputeTask.h"
#include "IOTask.h"

typedef enum OP{

	COMPUTE_TASK=1,
	IO_TASK
}OP_t;

typedef struct task{
	struct task *next;
	OP_t taskType;
}task_t;

int main(int argc, char *argv[])
{
	double computeTaskTimeElapse=0.0;//计算任务花费的总时间
	double IOTaskTimeElapse=0.0;//IO任务花费的总时间
	double totalTimeElapse=0.0;//完成所有任务花费的时间

    //计算任务的起始时间、结束时间和IO任务的起始时间、结束时间
	struct timeval computeTaskStartTime, computeTaskEndTime,IOTaskStartTime, IOTaskEndTime;
	
	pid_t fpid;//进程号
	task_t computeTask, ioTask;//计算任务和IO任务
	task_t* curTask = &computeTask;//当前任务

	computeTask.taskType = COMPUTE_TASK;//计算任务类型
	computeTask.next=&ioTask;//计算任务下一个任务是IO任务
	ioTask.taskType=IO_TASK;//IO任务类型
	ioTask.next=NULL;//IO任务下一个任务为空

	int parentProcess = 1;//父进程
	int childProcessNum = 0;//子进程个数

	while(NULL!=curTask)
	{
		if(curTask->taskType==IO_TASK)//当前任务为IO任务
			gettimeofday(&IOTaskStartTime,NULL);//获取起始时间
		else//计算任务
			gettimeofday(&computeTaskStartTime,NULL);//获取起始时间
		fpid=fork();//生成进程
		if(0==fpid)//fpid为0时该进程为子进程
		{//This is the child process
			parentProcess=0;
			break;
		}
		else if(-1==fpid)//生成进程失败
		{
			printf("Generate child Process error!\n");
			exit(0);
		}

		wait(NULL);  //wait the child process finish execution
		
		if(COMPUTE_TASK==curTask->taskType)//当前任务为计算任务
			gettimeofday(&computeTaskEndTime,NULL);//获取结束时间
		else//当前任务为IO任务
			gettimeofday(&IOTaskEndTime,NULL);//获取结束时间
		printf("Generate child process with pid:%d\n",fpid);

		++childProcessNum;//子进程个数加1
		curTask=curTask->next;//下一个任务

	}

	if(parentProcess==0)//子进程代码块
	{
		if(curTask->taskType==IO_TASK)//IO任务
		{
			executeIOTask();//调用IO任务
			printf("This is a IO task, pid:%d parent pid:%d\n",getpid(),getppid());//Print process ID and parent process ID
		}
		if(curTask->taskType==COMPUTE_TASK)//计算任务
		{
			executeComputeTask();//调用计算任务
			printf("This is a compute task, pid:%d parent pid:%d\n",getpid(),getppid());//Print process ID and parent process ID
		}
	}
	else//父进程代码块
	{
		//Parent Process, we calculate the time for executing computing task and the time fo executing IO task
		computeTaskTimeElapse = (double)(computeTaskEndTime.tv_sec - computeTaskStartTime.tv_sec)*1000.0+(double)(computeTaskEndTime.tv_usec - computeTaskStartTime.tv_usec)/1000.0;
		IOTaskTimeElapse = (double)(IOTaskEndTime.tv_sec - IOTaskStartTime.tv_sec)*1000.0+(double)(IOTaskEndTime.tv_usec - IOTaskStartTime.tv_usec)/1000.0;
		totalTimeElapse = (double)(IOTaskEndTime.tv_sec-computeTaskStartTime.tv_sec)*1000.0+(double)(IOTaskEndTime.tv_usec-computeTaskStartTime.tv_usec)/1000.0;
		printf("Compute Task Time Consume:%fms, IO Task Time Consume: %fms, Total Time Consume:%fms \n", computeTaskTimeElapse,IOTaskTimeElapse,totalTimeElapse);
	}

}

    double computeTaskTimeElapse=0.0;//计算任务花费的总时间
	double IOTaskTimeElapse=0.0;//IO任务花费的总时间
	double totalTimeElapse=0.0;//完成所有任务花费的时间

    //计算任务的起始时间、结束时间和IO任务的起始时间、结束时间
	struct timeval computeTaskStartTime, computeTaskEndTime,IOTaskStartTime, IOTaskEndTime;
	
	pid_t fpid;//进程号
	task_t computeTask, ioTask;//计算任务和IO任务
	task_t* curTask = &computeTask;//当前任务

	computeTask.taskType = COMPUTE_TASK;//计算任务类型
	computeTask.next=&ioTask;//计算任务下一个任务是IO任务
	ioTask.taskType=IO_TASK;//IO任务类型
	ioTask.next=NULL;//IO任务下一个任务为空

	int parentProcess = 1;//父进程
	int childProcessNum = 0;//子进程个数

解析:以上是main函数中定义的各种变量,变量的作用已经用注释说明了,便不再赘述,看上方图片中的注释即可。

while(NULL!=curTask)
	{
		if(curTask->taskType==IO_TASK)//当前任务为IO任务
			gettimeofday(&IOTaskStartTime,NULL);//获取起始时间
		else//计算任务
			gettimeofday(&computeTaskStartTime,NULL);//获取起始时间
		fpid=fork();//生成进程
		if(0==fpid)//fpid为0时该进程为子进程
		{//This is the child process
			parentProcess=0;
			break;
		}
		else if(-1==fpid)//生成进程失败
		{
			printf("Generate child Process error!\n");
			exit(0);
		}

		wait(NULL);  //wait the child process finish execution
		
		if(COMPUTE_TASK==curTask->taskType)//当前任务为计算任务
			gettimeofday(&computeTaskEndTime,NULL);//获取结束时间
		else//当前任务为IO任务
			gettimeofday(&IOTaskEndTime,NULL);//获取结束时间
		printf("Generate child process with pid:%d\n",fpid);

		++childProcessNum;//子进程个数加1
		curTask=curTask->next;//下一个任务

	}

解析:此块代码主要是产生子进程、判断任务类型并获取相应的起始时间和结束时间,代码的主要作用在图片中的注释部分已写出,也不再赘述,接下来看下一个代码块。

if(parentProcess==0)//子进程代码块
	{
		if(curTask->taskType==IO_TASK)//IO任务
		{
			executeIOTask();//调用IO任务
			printf("This is a IO task, pid:%d parent pid:%d\n",getpid(),getppid());//Print process ID and parent process ID
		}
		if(curTask->taskType==COMPUTE_TASK)//计算任务
		{
			executeComputeTask();//调用计算任务
			printf("This is a compute task, pid:%d parent pid:%d\n",getpid(),getppid());//Print process ID and parent process ID
		}
	}
	else//父进程代码块
	{
		//Parent Process, we calculate the time for executing computing task and the time fo executing IO task
		computeTaskTimeElapse = (double)(computeTaskEndTime.tv_sec - computeTaskStartTime.tv_sec)*1000.0+(double)(computeTaskEndTime.tv_usec - computeTaskStartTime.tv_usec)/1000.0;
		IOTaskTimeElapse = (double)(IOTaskEndTime.tv_sec - IOTaskStartTime.tv_sec)*1000.0+(double)(IOTaskEndTime.tv_usec - IOTaskStartTime.tv_usec)/1000.0;
		totalTimeElapse = (double)(IOTaskEndTime.tv_sec-computeTaskStartTime.tv_sec)*1000.0+(double)(IOTaskEndTime.tv_usec-computeTaskStartTime.tv_usec)/1000.0;
		printf("Compute Task Time Consume:%fms, IO Task Time Consume: %fms, Total Time Consume:%fms \n", computeTaskTimeElapse,IOTaskTimeElapse,totalTimeElapse);
	}

解析:这块代码主要是判断当前进程是子进程还是父进程。是子进程的话,就调用相应的任务,打印出相应的信息。如果是父进程的话,就计算两个任务的完成时间和总的完成时间。接下来就看一下两个调用的任务代码。

IOTask.c文件:

#include "IOTask.h"
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE (1024*1024*1024)

const char fileName[]="IOFile";//文件名
void executeIOTask()
{
	char* fileBuf=(char*)malloc(BUFFER_SIZE);
	int fileFd=open(fileName,O_RDWR|O_CREAT,0644);//文件不存在的话就新创一个文件
	if(-1==fileFd)
	{
		perror("Open File Error");
	}
	memset(fileBuf,97,BUFFER_SIZE);//写入小写字母a
	size_t writeSize = write(fileFd,fileBuf,BUFFER_SIZE);
	printf("write:%lu bytes to file:%s\n",writeSize,fileName);
	free(fileBuf);
	close(fileFd);
}

解析:该文件内主要是IO任务的定义。代码的大致意思是先定义一个文件名,如果文件存在就打开,否则就创建一个文件,然后往该文件中写入我们指定的内容。这就是IO任务,我们可以通过改变写入数据的大小从而改变进程的进行时间。这部分代码块也实现了将一段内容写进磁盘的任务:

ComputeTask.c文件:

#include "ComputeTask.h"
#include <stdio.h>
#include <stdint.h>

#define LOOP_SIZE (1024*1024*1024)

void executeComputeTask()
{
	size_t idx=0;
	size_t loopSize=LOOP_SIZE;
	
	for(idx=0;idx<loopSize;++idx)
	{
		//sum
	}
}

解析:该代码部分为我们的计算任务。通过阅读可知,该任务不过是进行循环罢了。我们也可以通过改变循环次数来改变进程进行的时间。

前期的代码理解部分我们至此已经完成了,接下来就开始我们的实验。

任务1:设计两进程顺序执行程序,并测试所需时间。

因为原代码已经实现了顺序执行,所以我们直接跑原代码就行了。

在跑之前,我们先把我们的文件和脚本放到共享文件夹中:

makefile文件内容:(这是一个编译代码的脚本,注意部分位置要用tab)

CC=gcc
CFLAGS = -g -Wall
OBJS = ComputeTask.o IOTask.o mainProg.o
TARGET = mainProg

$(TARGET): $(OBJS)
	$(CC) -o $(TARGET) $(OBJS) $(CFLAGS)

clean:
	rm  -rf  *.o  $(TARGET)

在文件夹中右键打开终端并在终端运行: 

解析:我们先用make进行编译,然后用./mainProg运行程序。最后我们可以看到计算任务所需时间为1610.051000ms,IO任务所需时间为11331.561000ms,总的任务所需时间为12941.678000ms。至此,任务一我们便完成了。 

任务二:设计两进程并发执行程序,并测试所需时间。

进行实验前,我们先了解并发是啥。

并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。

观察实验一的代码,我们发现:

分析:wait在mainProg.c文件中的红框位置。思考一下,父进程在调用了fork产生了子进程后,再往下走时便会遇到wait函数,此时父进程就会等待子进程全部代码走完才会继续运行,继续下一个IO任务,这便实现了两个进程的顺序执行(先完成计算任务再完成IO任务)。那我们要完成实验二的并发执行程序,是不是应该将wait函数移到别的地方,这样才能让父进程不断地产生子任务呢。举个例子,在本题中,我们先是计算任务,然后计算任务调用了一个子进程。由于wait函数被移走了,我们就继续执行如下代码:

即继续下一个任务(IO任务),然后while判断,重新从第一行开始执行,IO任务继续生成新的子进程,继续下一个任务。因为IO任务下一个任务为空,所以就不继续生成了。这样我们就能实现并发了。那么问题来了:我们的wait应该放在哪里呢?

观察实验一的wait函数部分:

解析:在wait函数后面还跟着一堆代码,那这些代码是干啥的?前面已经解释了,这堆代码获取对应任务的结束时间的。因为题目要求我们计算IO任务和计算任务所需的时间,那么我们是不是要等子进程结束了才能计算时间呢。所以我们要把计算时间的代码放在wait后面,即子进程结束,父进程继续执行来获取相应的结束时间呢。有了这个思路,我们就知道了wait函数应该放在父进程代码块中,后面接着获取结束时间的代码,这样我们便可以获取到相应任务的结束时间了。

观察接下来的代码部分:

if(parentProcess==0)//子进程代码块
	{
		if(curTask->taskType==IO_TASK)//IO任务
		{
			executeIOTask();//调用IO任务
			printf("This is a IO task, pid:%d parent pid:%d\n",getpid(),getppid());//Print process ID and parent process ID
		}
		if(curTask->taskType==COMPUTE_TASK)//计算任务
		{
			executeComputeTask();//调用计算任务
			printf("This is a compute task, pid:%d parent pid:%d\n",getpid(),getppid());//Print process ID and parent process ID
		}
	}
	else//父进程代码块
	{
		//Parent Process, we calculate the time for executing computing task and the time fo executing IO task
		computeTaskTimeElapse = (double)(computeTaskEndTime.tv_sec - computeTaskStartTime.tv_sec)*1000.0+(double)(computeTaskEndTime.tv_usec - computeTaskStartTime.tv_usec)/1000.0;
		IOTaskTimeElapse = (double)(IOTaskEndTime.tv_sec - IOTaskStartTime.tv_sec)*1000.0+(double)(IOTaskEndTime.tv_usec - IOTaskStartTime.tv_usec)/1000.0;
		totalTimeElapse = (double)(IOTaskEndTime.tv_sec-computeTaskStartTime.tv_sec)*1000.0+(double)(IOTaskEndTime.tv_usec-computeTaskStartTime.tv_usec)/1000.0;
		printf("Compute Task Time Consume:%fms, IO Task Time Consume: %fms, Total Time Consume:%fms \n", computeTaskTimeElapse,IOTaskTimeElapse,totalTimeElapse);
	}

 解析:if函数中是我们子进程部分,else是父进程代码块,所以我们的wait应该放在else中。但是,我们获取结束时间的代码应该怎么写呢。同时要注意!因为我们两个子进程是一起并发执行的,所以我们是不知道哪个子进程先结束的,所以我们的total时间应该取最后一个完成的子进程的结束时间。

示例代码:

解析:前面的childProcess用来计算子进程个数,我们有几个子进程,就要进行几次wait。我们用wait获取到子进程的进程号,然后用我前面定义的一个数组(用来记录子进程进程号的)来判断改进程号是哪项任务,然后再获取相应任务的结束时间。在两个子任务都结束后,我再获取total的结束时间(TotalEndTime是我自己定义的变量)。这样,我们就完成了实验二。接下来在终端跑一下代码,看看实验结果。        

 解析:可以看到计算任务所需时间为4930.70400ms,IO任务所需时间为11801.941000ms,总的任务所需时间为11801.969000ms。(可以发现total结束时间和最后结束的IO子进程结束时间差不多,说明我们上面的分析是正确的),至此,我们的实验二便完成了。

其他的版本:

版本一:

终端运行结果:

版本二:

解析:在结构体增加一个变量来存储进程号

解析:给两个任务的进程号赋值。

解析:结束时间赋值部分

终端运行结果:

5.最终答案

有三个版本,大家可以自信感受,其实效果是一样的。如果你们有更好的想法可以提出来讨论一下。

版本一:

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

#include "ComputeTask.h"
#include "IOTask.h"

#define CHILD_PROCESS_SIZE 2

typedef enum OP
{

	COMPUTE_TASK = 1,
	IO_TASK
} OP_t;

typedef struct task
{
	struct task *next; //指向下一个任务
	OP_t taskType;	   //任务类型
} task_t;

int main(int argc, char *argv[])
{
	double computeTaskTimeElapse = 0.0;
	double IOTaskTimeElapse = 0.0;
	double totalTimeElapse = 0.0;

	//计算任务起始和结束时间、IO任务的起始和结束时间
	struct timeval computeTaskStartTime, computeTaskEndTime, IOTaskStartTime, IOTaskEndTime, TotalEndTime;

	pid_t fpid;						//进程号
	task_t computeTask, ioTask;		//创建计算和IO任务
	task_t *curTask = &computeTask; //当前任务为计算任务

	computeTask.taskType = COMPUTE_TASK; //任务类型
	computeTask.next = &ioTask;			 //计算任务的下一个任务
	ioTask.taskType = IO_TASK;			 //任务类型
	ioTask.next = NULL;					 //下一个为空

	int parentProcess = 1;
	int childProcessNum = 0;

	pid_t pids[CHILD_PROCESS_SIZE] = {0};//coputeTask:0 IOTask:1

	while (NULL != curTask) //
	{
		if (curTask->taskType == IO_TASK)
			gettimeofday(&IOTaskStartTime, NULL); // IO任务起始时间
		else
			gettimeofday(&computeTaskStartTime, NULL); //计算任务起始时间
		fpid = fork();
		if (0 == fpid) //子进程
		{			   // This is the child process
			parentProcess = 0;
			break;
		}
		else if (-1 == fpid) //生成子进程失败
		{
			printf("Generate child Process error!\n");
			exit(0);
		}

		pids[childProcessNum] = fpid;

		++childProcessNum;
		curTask = curTask->next;
	}

	if (parentProcess == 0) //当前进程为子进程
	{
		if (curTask->taskType == IO_TASK)
		{
			executeIOTask();
			printf("This is a IO task, pid:%d parent pid:%d\n", getpid(), getppid()); // Print process ID and parent process ID
		}
		if (curTask->taskType == COMPUTE_TASK)
		{
			executeComputeTask();
			printf("This is a compute task, pid:%d parent pid:%d\n", getpid(), getppid()); // Print process ID and parent process ID
		}
	}
	else
	{
		while (childProcessNum--)
		{
			pid_t pid = wait(NULL);
			if (pid == pids[0])
			{
				gettimeofday(&computeTaskEndTime, NULL);
			}
			else
			{
				gettimeofday(&IOTaskEndTime, NULL);
			}
		}
		gettimeofday(&TotalEndTime, NULL);
		// Parent Process, we calculate the time for executing computing task and the time fo executing IO task
		computeTaskTimeElapse = (double)(computeTaskEndTime.tv_sec - computeTaskStartTime.tv_sec) * 1000.0 + (double)(computeTaskEndTime.tv_usec - computeTaskStartTime.tv_usec) / 1000.0;
		IOTaskTimeElapse = (double)(IOTaskEndTime.tv_sec - IOTaskStartTime.tv_sec) * 1000.0 + (double)(IOTaskEndTime.tv_usec - IOTaskStartTime.tv_usec) / 1000.0;
		totalTimeElapse = (double)(TotalEndTime.tv_sec - computeTaskStartTime.tv_sec) * 1000.0 + (double)(TotalEndTime.tv_usec - computeTaskStartTime.tv_usec) / 1000.0;
		printf("Compute Task Time Consume:%fms, IO Task Time Consume: %fms, Total Time Consume:%fms \n", computeTaskTimeElapse, IOTaskTimeElapse, totalTimeElapse);
	}
}

版本二:

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

#include "ComputeTask.h"
#include "IOTask.h"

#define CHILD_PROCESS_SIZE 2

typedef enum OP
{

	COMPUTE_TASK = 1,
	IO_TASK
} OP_t;

typedef struct task
{
	struct task *next; //指向下一个任务
	OP_t taskType;	   //任务类型
} task_t;

int main(int argc, char *argv[])
{
	double computeTaskTimeElapse = 0.0;
	double IOTaskTimeElapse = 0.0;
	double totalTimeElapse = 0.0;

	//计算任务起始和结束时间、IO任务的起始和结束时间
	struct timeval computeTaskStartTime, computeTaskEndTime, IOTaskStartTime, IOTaskEndTime, TotalEndTime;

	pid_t fpid;						//进程号
	task_t computeTask, ioTask;		//创建计算和IO任务
	task_t *curTask = &computeTask; //当前任务为计算任务

	computeTask.taskType = COMPUTE_TASK; //任务类型
	computeTask.next = &ioTask;			 //计算任务的下一个任务
	ioTask.taskType = IO_TASK;			 //任务类型
	ioTask.next = NULL;					 //下一个为空

	int parentProcess = 1;
	int childProcessNum = 0;

	pid_t pids[CHILD_PROCESS_SIZE] = {0};//computeTask:0 IOTask:1

	while (NULL != curTask) //
	{
		if (curTask->taskType == IO_TASK)
			gettimeofday(&IOTaskStartTime, NULL); // IO任务起始时间
		else
			gettimeofday(&computeTaskStartTime, NULL); //计算任务起始时间
		fpid = fork();
		if (0 == fpid) //子进程
		{			   // This is the child process
			parentProcess = 0;
			break;
		}
		else if (-1 == fpid) //生成子进程失败
		{
			printf("Generate child Process error!\n");
			exit(0);
		}

		pids[childProcessNum] = fpid;

		++childProcessNum;
		curTask = curTask->next;
	}

	if (parentProcess == 0) //当前进程为子进程
	{
		if (curTask->taskType == IO_TASK)
		{
			executeIOTask();
			printf("This is a IO task, pid:%d parent pid:%d\n", getpid(), getppid()); // Print process ID and parent process ID
		}
		if (curTask->taskType == COMPUTE_TASK)
		{
			executeComputeTask();
			printf("This is a compute task, pid:%d parent pid:%d\n", getpid(), getppid()); // Print process ID and parent process ID
		}
	}
	else
	{
		fpid = wait(NULL);
		while (fpid > 0)
		{
			if (fpid == pids[0])
			{
				gettimeofday(&computeTaskEndTime, NULL);
			}
			else
			{
				gettimeofday(&IOTaskEndTime, NULL);
			}
			fpid = wait(NULL);
		}
		gettimeofday(&TotalEndTime, NULL);
		// Parent Process, we calculate the time for executing computing task and the time fo executing IO task
		computeTaskTimeElapse = (double)(computeTaskEndTime.tv_sec - computeTaskStartTime.tv_sec) * 1000.0 + (double)(computeTaskEndTime.tv_usec - computeTaskStartTime.tv_usec) / 1000.0;
		IOTaskTimeElapse = (double)(IOTaskEndTime.tv_sec - IOTaskStartTime.tv_sec) * 1000.0 + (double)(IOTaskEndTime.tv_usec - IOTaskStartTime.tv_usec) / 1000.0;
		totalTimeElapse = (double)(TotalEndTime.tv_sec - computeTaskStartTime.tv_sec) * 1000.0 + (double)(TotalEndTime.tv_usec - computeTaskStartTime.tv_usec) / 1000.0;
		printf("Compute Task Time Consume:%fms, IO Task Time Consume: %fms, Total Time Consume:%fms \n", computeTaskTimeElapse, IOTaskTimeElapse, totalTimeElapse);
	}
}

版本三:

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

#include "ComputeTask.h"
#include "IOTask.h"

typedef enum OP
{

	COMPUTE_TASK = 1,
	IO_TASK
} OP_t;

typedef struct task
{
	struct task *next; //指向下一个任务
	OP_t taskType;	   //任务类型
	pid_t id;		   //进程号
} task_t;

int main(int argc, char *argv[])
{
	double computeTaskTimeElapse = 0.0;
	double IOTaskTimeElapse = 0.0;
	double totalTimeElapse = 0.0;

	//计算任务起始和结束时间、IO任务的起始和结束时间
	struct timeval computeTaskStartTime, computeTaskEndTime, IOTaskStartTime, IOTaskEndTime, TotalEndTime;

	pid_t fpid;						//进程号
	task_t computeTask, ioTask;		//创建计算和IO任务
	task_t *curTask = &computeTask; //当前任务为计算任务

	computeTask.taskType = COMPUTE_TASK; //任务类型
	computeTask.next = &ioTask;			 //计算任务的下一个任务
	ioTask.taskType = IO_TASK;			 //任务类型
	ioTask.next = NULL;					 //下一个为空

	int parentProcess = 1;
	int childProcessNum = 0;

	while (NULL != curTask) //
	{
		if (curTask->taskType == IO_TASK)
			gettimeofday(&IOTaskStartTime, NULL); // IO任务起始时间
		else
			gettimeofday(&computeTaskStartTime, NULL); //计算任务起始时间
		fpid = fork();
		if (0 == fpid) //子进程
		{			   // This is the child process
			parentProcess = 0;
			break;
		}
		else if (-1 == fpid) //生成子进程失败
		{
			printf("Generate child Process error!\n");
			exit(0);
		}

		++childProcessNum;
		if (childProcessNum == 1)
		{
			computeTask.id = fpid;
		}
		else
		{
			ioTask.id = fpid;
		}
		curTask = curTask->next;
	}

	if (parentProcess == 0) //当前进程为子进程
	{
		if (curTask->taskType == IO_TASK)
		{
			executeIOTask();
			printf("This is a IO task, pid:%d parent pid:%d\n", getpid(), getppid()); // Print process ID and parent process ID
		}
		if (curTask->taskType == COMPUTE_TASK)
		{
			executeComputeTask();
			printf("This is a compute task, pid:%d parent pid:%d\n", getpid(), getppid()); // Print process ID and parent process ID
		}
	}
	else
	{
		while (childProcessNum--)
		{
			fpid = wait(NULL);
			if (computeTask.id == fpid)
				gettimeofday(&computeTaskEndTime, NULL); //获取结束时间
			else if (ioTask.id == fpid)
				gettimeofday(&IOTaskEndTime, NULL); //获取结束时间
		}
		gettimeofday(&TotalEndTime, NULL);
		// Parent Process, we calculate the time for executing computing task and the time fo executing IO task
		computeTaskTimeElapse = (double)(computeTaskEndTime.tv_sec - computeTaskStartTime.tv_sec) * 1000.0 + (double)(computeTaskEndTime.tv_usec - computeTaskStartTime.tv_usec) / 1000.0;
		IOTaskTimeElapse = (double)(IOTaskEndTime.tv_sec - IOTaskStartTime.tv_sec) * 1000.0 + (double)(IOTaskEndTime.tv_usec - IOTaskStartTime.tv_usec) / 1000.0;
		totalTimeElapse = (double)(TotalEndTime.tv_sec - computeTaskStartTime.tv_sec) * 1000.0 + (double)(TotalEndTime.tv_usec - computeTaskStartTime.tv_usec) / 1000.0;
		printf("Compute Task Time Consume:%fms, IO Task Time Consume: %fms, Total Time Consume:%fms \n", computeTaskTimeElapse, IOTaskTimeElapse, totalTimeElapse);
	}
}

至此,我们的实验大功告成!如果大家有什么想法,可以在评论区提出,一起交流。

  • 23
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值