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

news2024/11/15 5:50:09

 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文件内容:(这是一个编译代码的脚本)

 

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

解析:我们先用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);
	}
}

至此,我们的实验大功告成!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/443083.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MYSQL索引失效的场景有哪些】

创建一张表&#xff1a;id为主键&#xff08;primary key&#xff09;name为普通建&#xff08;index&#xff09; 插入数据&#xff1a; 用主键索引查询&#xff1a; 用普通建索引查询 对于执行计划&#xff0c;参数有&#xff1a; possible_keys 字段表示可能用到的索引&am…

KMM初探与编译过程详解

本文字数&#xff1a;22817字 预计阅读时间&#xff1a;58分钟 简介 KMM&#xff0c; 即Kotlin Multiplatform Mobile&#xff0c;是由Kotlin发布的移动端跨平台框架。相比于其他跨平台框架&#xff0c;KMM是原生UI逻辑共享的理念&#xff0c;共享重复逻辑性的工作来提升开发效…

Maven配置国内源以及jar下载失败处理详解

目录 1&#xff0c;配置Idea的Maven xml文件不存在&#xff1a; xml文件存在&#xff1a; 2&#xff0c;重新下载jar包 3&#xff0c;注意事项总结 1&#xff0c;配置Idea的Maven 需要配置的项目有两个&#xff0c;一个是当前项目&#xff0c;一个是新项目&#xff1a; 打…

抖音账号矩阵搭建管理获客系统

抖音矩阵号管理系统是一款企业矩阵运营管理工具&#xff0c;能够有效地帮助企业管理多个矩阵账号&#xff0c;并实现批量管理。在短视频矩阵系统中&#xff0c;自动获客工具和智能AI的帮助下&#xff0c;一个人也能轻松地管理多个账号。 一、矩阵账号管理&#xff1a; 首先&a…

leetCode算法第三天

继续练习leetcode中关于字符串的算法题&#xff0c;越练越觉得自己编码思想还很欠缺&#xff0c;继续努力。 文章目录 有效的括号括号生成串联所有单词的子串最长有效括号 有效的括号 leetcode链接&#xff1a;https://leetcode.cn/problems/valid-parentheses/ 解题思路&…

SPI协议

SPI数据接口 SPI&#xff08;Serial Peripheral Interface&#xff09;串行外设接口的简称&#xff0c;它是一种同步全双工通信协议。有 3根或者 4根数据线组成&#xff0c;包括 CLK、SOMI、SIMO、STE&#xff1a; CLK为时钟线&#xff0c;由主机控制输出。 SOMI…

国产数字温度传感芯片M117 Pin to Pin替代PT100和PT1000

高精度数字温度传感芯片 - M117&#xff0c;可Pin to Pin替代PT100/PT1000&#xff0c;且具功能差异化优势&#xff0c;支持行业应用的定制化需求。高测温精度0.1℃&#xff0c;用户无需进行校准。芯片感温原理基于CMOS半导体PN节温度与带隙电压的特性关系&#xff0c;经过小信…

电脑开机进不了系统卡在加载界面怎么办?

电脑开机进不了系统卡在加载界面怎么办&#xff1f;有用户电脑弹出需要进行系统更新&#xff0c;不小心点到了系统更新的选项。因为自己不想进行系统更新&#xff0c;所以马上将电脑关机了。但是关机之后却发现系统一直卡在开机的界面中&#xff0c;无法进入桌面中了。那么这个…

如何在Anaconda下安装pytorch(conda安装和pip安装)

前言 文字说明 本文中标红的&#xff0c;代表的是我认为比较重要的。 版本说明 python环境配置&#xff1a;jupyter的base环境下的python是3.10版本。CUDA配置是&#xff1a;CUDA11.6。目前pytorch官网提示支持的版本是3.7-3.9 本文主要用来记录自己在安装pytorch中…

乙肝80%以上由妈妈传给孩子 5岁以下治愈率超六成

中国是乙肝大国。目前&#xff0c;乙肝病毒感染人数仍超过7000万。通过医务人员多年的努力&#xff0c;母婴传播感染率明显下降。到目前为止&#xff0c;已降至0.3%左右。每年仍有5万名儿童感染乙肝病毒。目前&#xff0c;儿童慢性乙肝仍在180万左右&#xff0c;绝对数仍是世界…

Node【模块系统】

文章目录 &#x1f31f;前言&#x1f31f;Nodejs模块系统&#x1f31f;为什么需要模块化&#x1f31f;什么是Nodejs模块&#x1f31f;Nodejs模块分类&#x1f31f;文件模块的分类&#x1f31f;调用内置模块&#x1f31f;调用文件模块 &#x1f31f;Nodejs模块使用&#x1f31f;…

2023年网络安全的发展趋势是怎样的?

数据安全越来越重要。 我国《数据安全法》提出“建立健全数据安全治理体系”&#xff0c;各地区部门均在探索和简历数据分类分级、重要数据识别与重点保护制度。 数据安全治理不仅是一系列技术应用或产品&#xff0c;更是包括组织构建、规范制定、技术支撑等要素共同完成数据…

PACS/RIS影像管理系统源码,支持图像后处理与重建

PACS/RIS影像管理系统源码&#xff0c;功能强大&#xff0c;文档齐全&#xff0c;有演示。 文末获取联系&#xff01; 系统特点&#xff1a; 符合国内医院影像中心/放射科的典型工作管理流程。 开放式体系结构&#xff0c;完全符合DICOM3.0标准&#xff0c;提供HL7标准接口&a…

MyBatis(十四)MyBatis的逆向工程

前言、 所谓的逆向工程是&#xff1a;根据数据库表逆向生成Java的pojo类&#xff0c;SqlMapper.xml文件&#xff0c;以及Mapper接口类等。 要完成这个工作&#xff0c;需要借助别人写好的逆向工程插件。 思考&#xff1a;使用这个插件的话&#xff0c;需要给这个插件配置哪些…

2023年淮阴工学院五年一贯制专转本退役士兵大学语文考试大纲

2023年淮阴工学院五年一贯制专转本退役士兵大学语文考试大纲 一、考试目标 淮阴工学院五年一贯制高职专转本入学考试秘书学专业《大学语文》考试是我校为招收五年一贯制高职专转本学生设置的具有选拔性质的考试科目。其目的是科学、公平、有效地测试考生是否具备攻读秘书学本…

【论文总结】V-Shuttle:可扩展和语义感知的 Hypervisor 虚拟设备模糊测试

介绍 这是来自2021 CCS的一篇论文&#xff0c;作者有GaoningPan, Xingwei Lin, Xuhong Zhang, Yongkang Jia, Shouling Ji, Chunming Wu, Xinlei Ying, Jiashui Wang, Yanjun Wu。该论文提出V-shuttle的新框架来执行管控程序的模糊测试&#xff0c;该框架执行可扩展和语义感知…

LDR6328 PD诱骗(取电)芯片概述,支持定制化取电

PD充电需要在供电端&#xff08;充电器&#xff09;和受电端&#xff08;产品上&#xff09;都要有协议通信&#xff0c;一般充电器内有PD供电协议芯片&#xff0c;产品上有PD协议受电芯片&#xff0c;两者连接后会进行通信握手&#xff0c;连接成功后充电器才会输出需要的电压…

银行数字化转型导师坚鹏:商业银行对公业务数字化风控

商业银行对公业务数字化风控 课程背景&#xff1a; 数字化背景下&#xff0c;很多银行存在以下问题&#xff1a; 不清楚商业银行数字化风控发展现状&#xff1f; 不清楚对公业务数字化风控工作如何开展&#xff1f; 不知道零售业务数字化风控工作如何开展&#xff1f; …

探索五大机器学习技术及其应用

没有一种机器学习算法可以解决所有类型的机器学习问题。机器学习任务可能千差万别&#xff0c;算法的选择将取决于数据的大小、维数和稀疏性等因素。目标变量、数据的质量以及特征内部以及特征与目标变量之间存在的相互作用和统计关系。 在本文中&#xff0c;我将提供机器学习…

【NPM】npm上传包

必须使用npm镜像&#xff0c;不能使用淘宝镜像 查看当前的镜像 npm config get registry切换镜像为npm &#xff08;不能使用淘宝镜像&#xff09; npm config set registry https://registry.npmjs.org在要发布的包文件目录执行 发布为公共包 npm publish --access publi…