一、實驗目的
設計并實作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;
}