一、实验目的
设计并实现Unix的“time”命令。“mytime”命令通过命令行参数接受要运行的程序,创建一个独立的进程来运行该程序,并记录程序运行的时间。
二、实验内容
在Windows下实现:
• 使用CreateProcess()来创建进程
• 使用WaitForSingleObject()在“mytime”命令和新创建的进程之间同步
• 调用GetSystemTime()来获取时间
在Linux下实现:
• 使用fork()/execv()来创建进程运行程序
• 使用wait()等待新创建的进程结束
• 调用gettimeofday()来获取时间
mytime的用法:
$ mytime.exe program1
三、实验环境
1、Windows环境下使用Dev-c++和命令行窗口!
2、Linux环境是在虚拟机中装Ubuntu15.10;如图1所示
图1
四、实验方法与步骤
1、在Windows下实现
(1)、在创建子进程之前先定义好各个变量,以备后续使用。
(2)、创建子进程之前调用系统函数GetSystemTime()获取当前系统时间。
(3)、调用CreateProcess()函数创建进程。
CreateProcess内的参数设置如下:
CreateProcess
(NULL, //不在此指定可执行文件的文件名
argv[1], //命令行参数
NULL, //默认进程安全性
NULL, //默认线程安全性
FALSE, //当前进程内的句柄不可以被子进程继承
CREATE_NEW_CONSOLE, //为新进程创建一个新的控制台窗口
NULL, //使用本进程的环境变量
NULL, //使用本进程的驱动器和目录
&si, //父进程传给子进程的一些信息
&pi //保存新进程信息的结构
)
注意:
最重要的三个参数,在强调一下:
(1)、由于要使用命令行来创建进程,因此CreateProcess的第一个参数设置为NULL,不在此指定可执行文件的文件名;
(2)、通过第二个参数在命令行输入一个字符串来实现创建进程;
(3)、pi是保存新进程的结构,内部包括四个参数;分别为:新创建进程的句柄,新创建进程的主线程的句柄,新创建进程的标识,新创建进程的主线程的标识。
(4)、使用命令行的形式创建好进程后,调用等待函数来等待所创建函数的死亡;
等待函数为:WaitForSingleObject(pi.hProcess,INFINITE)。
(5)、当子进程死亡后,再次通过GetSystemTime()函数获得系统时间。
(6)、用第(5)步得到的时间减去第(2)步得到的时间的时间即是生成的子进程运行时所花费的时间。
(7)、运行程序,产生mytime.exe文件。
(8)、在命令行中找到文件所放路径,比如在桌面,直接键入如下命令:
cd Desktop
mytime.exe 所要调用的子进程名
2、在Linux下实现
(1)、创建子进程之前,先取得系统时间
struct timevaltime_start;
struct timevaltime_end;
gettimeofday(&time_start,NULL);
(2)、用fork()函数创建进程,fork()函数会返回两个值,通过这两个值来判断是子进程还是父进程。
if (fork() == 0) //子进程
else //为父进程
(3)、①如果第(2)步中是子进程运行,则在子进程中调用execv()函数;在命令行中来运行一个程序;即execv(argv[1],&argv[1])
②如果第(2)步是父进程在运行,则先等待子进程运行结束,然后在获取时间;
即wait(NULL);
gettimeofday(&time_end,NULL);
(4)、计算程序运行的时间(微秒):
time_use= 1000000 * (time_end.tv_sec - time_start.tv_sec) + (time_end.tv_usec -time_start.tv_usec);
(5)、输出程序运行的时间:
printf("此程序运行的时间为:%lf微秒",time_use);
(6)、用命令gcc –o mytimemytime.c将c文件编译为可执行文件。
(7)、在终端打开编译的文件,并在其后跟上要打开的进程名;
如:桌面上的可执行文件fac(求1~20的阶乘之和):./mytime fac
五、实验结果
1、Windows环境下的实验结果
(1)、编译及运行mytime.cpp,产生mytime.exe文件的截图如下(图2所示):
图2
(2)、在命令行调用产生的mytime.exe文件,并在其后跟上要运行的子进程的名字,比如我自己写的GoBang这个小游戏。开始运行截图如下(图3):
图3
(3)GoBang这个子进程被关闭以后的截图如下(图4):
图4
2、Linux环境下的实验结果
(1)、mytime.c编译后的截图(图5所示)
图5
(2)、在终端调用可执行文件mytime,并在其后跟上要运行的子进程名,如(fac);截图如下(图6所示)
图6
六、实验分析与总结
1、在Windows、Linux系统下,分别调用相应的API函数对进程进行创建、同步和获取并记录进程运行的时间,正确利用可执行文件来实现命令行创建进程。这次实验不论是linux还是windows,主要思路只有一个: 利用fork()或者createprocess()函数建立一个子进程,在建立成功之后记录运行时间,然后利用wait()或者waitforsingleobject()函数等待子进程的同步,之后再记录运行时间,用两次时间相减就可以。
2、注意的地方是,windows下的计时函数GetSystemTime()不太好用了,函数返回的是系统的时间,要用两次时间相减,这里要注意可能有负数的情况,所以要考虑借位,其实windows下面有一个好用的函数clock(),这个函数对于计时很在行。
3、收获:加深了对一些API函数的理解和应用。
七.实验源代码
Windows版本
/*题目要求:设计并实现Unix的“time”命令。“mytime”命令通过命令行参数接受要运行的程序,
创建一个独立的进程来运行该程序,并记录程序运行的时间。
在Windows下实现:
使用CreateProcess()来创建进程
使用WaitForSingleObject()在“mytime”命令和新创建的进程之间同步
调用GetSystemTime()来获取时间
*/
// 作者:野狼
// 日期:2017.3.19
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int year, month, day, hour, minutes, seconds, milliseconds;
SYSTEMTIME time_start, time_end;
STARTUPINFO si; //进程启动相关信息的结构体
memset(&si,0,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO); //应用程序必须将cb初始化为sizeof(STARTUPINFO)
si.dwFlags = STARTF_USESHOWWINDOW; //窗口标志
si.wShowWindow = SW_SHOW;
PROCESS_INFORMATION pi; //必备参数设置结束
if (!CreateProcess
(NULL, //不在此指定可执行文件的文件名
argv[1], //命令行参数
NULL, //默认进程安全性
NULL, //默认线程安全性
FALSE, //当前进程内的句柄不可以被子进程继承
CREATE_NEW_CONSOLE, //为新进程创建一个新的控制台窗口
NULL, //使用本进程的环境变量
NULL, //使用本进程的驱动器和目录
&si, //父进程传给子进程的一些信息
&pi //保存新进程信息的结构
))
{
cout <<"Create Fail!"<< endl;
exit(1);
}
else
{
GetSystemTime(&time_start);
printf("Begin Time:%d:%d:%d-%d:%d:%d:%d\n",time_start.wYear,time_start.wMonth,time_start.wDay,time_start.wHour,time_start.wMinute,time_start.wSecond,time_start.wMilliseconds);
cout <<"Create Success!"<< endl;
}
//使用等待函数来等待所创建进程的死亡
WaitForSingleObject(pi.hProcess, INFINITE);
GetSystemTime(&time_end);
printf("End Time: %d:%d:%d-%d:%d:%d:%d",time_start.wYear,time_start.wMonth,time_start.wDay,time_end.wHour,time_end.wMinute,time_end.wSecond,time_end.wMilliseconds);
milliseconds = time_end.wMilliseconds - time_start.wMilliseconds;
seconds = time_end.wSecond - time_start.wSecond;
minutes = time_end.wMinute - time_start.wMinute;
hour = time_end.wHour - time_start.wHour;
day = time_end.wDay - time_start.wDay;
month = time_end.wMonth - time_start.wMonth;
year = time_end.wYear - time_start.wYear;
if (milliseconds < 0)
{
seconds--;
milliseconds += 1000;
}
if (seconds < 0)
{
minutes--;
seconds += 60;
}
if (minutes < 0)
{
hour--;
minutes += 60;
}
if (hour < 0)
{
day--;
hour += 24;
}
if (day < 0)
{
month--;
day += 30;
}
if (month < 0)
{
year--;
month += 12;
}
printf("\nThis program running time is: ");
if (year > 0)
{
printf("%dY:",year);
}
if (month > 0)
{
printf("%dM:", month);
}
if (day > 0)
{
printf("%dD:", day);
}
if (hour > 0)
{
printf("%dH:", hour);
}
if (minutes > 0)
{
printf("%dm:", minutes);
}
if (seconds > 0)
{
printf("%ds:", seconds);
}
if (milliseconds > 0)
{
printf("%dms", milliseconds);
}
printf("\n");
return 0;
}
Linux版本
/********************************************/
/*名称:mytime.c
/*描述:用命令行的形式建立一个新的进程,并保存其运行的时间
/*作者:野狼
/*日期:2017-03-19
/********************************************/
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
int main(int argc, char **argv)
{
//调用系统时间
struct timeval time_start;
struct timeval time_end;
//用以记录进程运行的时间
float time_use = 0;
pid_t pid;
pid = fork();
if (pid < 0) //如果出错
{
printf("Create Fail!");
exit(0);
}
else if (pid == 0) //如果是子进程
{
printf("Create Child\n");
gettimeofday(&time_start,NULL);
printf("111time_start.tv_sec:%d\n",time_start.tv_sec);
printf("111time_start.tv_usec:%d\n\n",time_start.tv_usec);
//在子进程中调用execv函数在命令行中来运行一个程序
execv(argv[1],&argv[1]);
}
else
{
gettimeofday(&time_start,NULL);
printf("time_start.tv_sec:%d\n",time_start.tv_sec);
printf("time_start.tv_usec:%d\n\n",time_start.tv_usec);
wait(NULL); //等待子进程结束
gettimeofday(&time_end,NULL);
printf("time_end.tv_sec:%d\n",time_end.tv_sec);
printf("time_end.tv_usec:%d\n",time_end.tv_usec);
time_use = (time_end.tv_sec - time_start.tv_sec)*1000000 + (time_end.tv_usec - time_start.tv_usec);
printf("此程序运行的时间为:%lf微秒",time_use);
}
return 0;
}