這裏實現的是一個自定義timer用於統計子進程運行的時間。使用方式主要是html
timer [-t seconds] command arguments
例如要統計ls
的運行時間能夠直接輸入timer ls
,其後的arguments
是指所要運行的程序的參數。如:timer ls -al
。若是要指定程序運行多少時間,如5秒鐘,能夠輸入timer -t 5 ls -al
。須要注意的是,該程序對輸入沒有作異常檢測,因此要確保程序輸入正確。linux
獲取時間ios
時間獲取函數使用gettimeofday
,精度能夠達到微秒windows
struct timeval{ long tv_sec;*//秒* long tv_usec;*//微秒* }
子進程建立api
fork()
函數安全
#include <sys/types.h> #include <unistd.h> pid_t fork(void);
fork
調用失敗則返回-1,調用成功則:函數
fork函數會有兩種返回值,一是爲0,一是爲正整數。若爲0,則說明當前進程爲子進程;若爲正整數,則該進程爲父進程且該值爲子進程pid。關於進程控制的詳細說明請參考:進程控制測試
exec
函數spa
用fork建立子進程後執行的是和父進程相同的程序(但有可能執行不一樣的代碼分支),子進程每每要調用一種exec函數以執行另外一個程序。當進程調用一種exec函數時,該進程的用戶空間代碼和數據徹底被新程序替換,重新程序的啓動例程開始執行。調用exec並不建立新進程,因此調用exec先後該進程的id並未改變。
其實有六種以exec開頭的函數,統稱exec函數:.net
#include <unistd.h> int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);
這些函數若是調用成功則加載新的程序從啓動代碼開始執行,再也不返回,若是調用出錯則返回-1,因此exec函數只有出錯的返回值而沒有成功的返回值。
wait
與waitpid
一個進程在終止時會關閉全部文件描述符,釋放在用戶空間分配的內存,但它的PCB還保留着,內核在其中保存了一些信息:若是是正常終止則保存着退出狀態,若是是異常終止則保存着致使該進程終止的信號是哪一個。這個進程的父進程能夠調用wait或waitpid獲取這些信息,而後完全清除掉這個進程。咱們知道一個進程的退出狀態能夠在Shell中用特殊變量$?查看,由於Shell是它的父進程,當它終止時Shell調用wait或waitpid獲得它的退出狀態同時完全清除掉這個進程。
若是一個進程已經終止,可是它的父進程還沒有調用wait或waitpid對它進行清理,這時的進程狀態稱爲殭屍(Zombie)進程。任何進程在剛終止時都是殭屍進程,正常狀況下,殭屍進程都馬上被父進程清理了。
殭屍進程是不能用kill命令清除掉的,由於kill命令只是用來終止進程的,而殭屍進程已經終止了。
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);
若調用成功則返回清理掉的子進程id,若調用出錯則返回-1。父進程調用wait或waitpid時可能會:
阻塞(若是它的全部子進程都還在運行
出錯當即返回(若是它沒有任何子進程)
這兩個函數的區別是:
timer源代碼
#include <math.h> #include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <unistd.h> #include <wait.h> #include <ctime> #include <iostream> #include <cstring> //程序假定輸入徹底正確,沒有作異常處理 //mytime [-t number] 程序 using namespace std; //調用系統時間 struct timeval time_start; struct timeval time_end; void printTime(); void newProcess(const char *child_process, char *argv[], double duration); int main(int argc, char const *argv[]) { double duration = 0; char **arg; int step = 2; if (argc > 3 && (strcmp((char *)"-t", argv[1]) == 0)) //若是指定了運行時間 { step = 4; duration = atof(argv[2]); //沒有作異常處理 } arg = new char *[argc - step + 1]; for (int i = 0; i < argc - step; i++) { arg[i] = new char[100]; strcpy(arg[i], argv[i + step]); } arg[argc - step] = NULL; newProcess(argv[step - 1], arg, duration); return 0; } void printTime() { //用以記錄進程運行的時間 int time_use = 0; // us int time_left = 0; // us int time_hour = 0, time_min = 0, time_sec = 0, time_ms = 0, time_us = 0; gettimeofday(&time_end, NULL); time_use = (time_end.tv_sec - time_start.tv_sec) * 1000000 + (time_end.tv_usec - time_start.tv_usec); time_hour = time_use / (60 * 60 * (int)pow(10, 6)); time_left = time_use % (60 * 60 * (int)pow(10, 6)); time_min = time_left / (60 * (int)pow(10, 6)); time_left %= (60 * (int)pow(10, 6)); time_sec = time_left / ((int)pow(10, 6)); time_left %= ((int)pow(10, 6)); time_ms = time_left / 1000; time_left %= 1000; time_us = time_left; printf("此程序運行的時間爲:%d 小時, %d 分鐘, %d 秒, %d 毫秒, %d 微秒\n", time_hour, time_min, time_sec, time_ms, time_us); } void newProcess(const char* child_process, char **argv, double duration) { pid_t pid = fork(); if (pid < 0) //出錯 { printf("建立子進程失敗!"); exit(1); } if (pid == 0) //子進程 { execvp(child_process, argv); } else { if (abs(duration - 0) < 1e-6) { gettimeofday(&time_start, NULL); wait(NULL); //等待子進程結束 printTime(); } else { gettimeofday(&time_start, NULL); // printf("sleep: %lf\n", duration); waitpid(pid, NULL, WNOHANG); usleep(duration * 1000000); // sec to usec int kill_ret_val = kill(pid, SIGKILL); if (kill_ret_val == -1) // return -1, fail { printf("kill failed.\n"); perror("kill"); } else if (kill_ret_val == 0) // return 0, success { printf("process %d has been killed\n", pid); } printTime(); } } }
測試源代碼
#include <iostream> #include <ctime> #include <unistd.h> using namespace std; int main(int argc, char const *argv[]) { for(int n = 0; n < argc; n++) { printf("arg[%d]:%s\n",n, argv[n]); } sleep(5); return 0; }
自行編寫程序測試
系統程序測試
將timer加入環境變量
這裏僅進行了臨時變量修改。
在Windows下進行父子進程的建立和管理在api調用上相較Linux有必定難度,但實際上在使用管理上比Linux容易的多。
#include <Windows.h> BOOL CreateProcessA( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation );
timer程序
// 進程管理.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。 // #include <iostream> #include <wchar.h> #include <Windows.h> #include <tchar.h> using namespace std; void printTime(SYSTEMTIME* start, SYSTEMTIME* end); void newProcess(TCHAR* cWinDir, double duration); int _tmain(int argc, TCHAR *argv[]) { TCHAR* cWinDir = new TCHAR[MAX_PATH]; memset(cWinDir, sizeof(TCHAR) * MAX_PATH, 0); printf("argc: %d\n", argc); int step = 1; double duration = 0; if (argc > 1) { if (argv[1][0] == TCHAR('-') && argv[1][1] == TCHAR('t') && argv[1][2] == TCHAR('\0')) { step = 3; duration = atof((char*)argv[2]); } } //printf("printf content start: %ls\n", argv[1]); int j = 0; for (int i = 0, h = 0; i < argc - step; i++) { wcscpy_s(cWinDir + j, MAX_PATH - j, argv[i + step]); for (h = 0; argv[i + step][h] != TCHAR('\0'); h++); j += h; cWinDir[j++] = ' '; //printf("%d : %d\n", i, j); //printf("printf content start: %ls\n", cWinDir); } cWinDir[j - 2] = TCHAR('\0'); //printf("printf content start: %ls\n", cWinDir); newProcess(cWinDir,duration); return 0; } void printTime(SYSTEMTIME* start, SYSTEMTIME* end) { int hours = end->wHour - start->wHour; int minutes = end->wMinute - start->wMinute; int seconds = end->wSecond - start->wSecond; int ms = end->wMilliseconds - start->wMilliseconds; if (ms < 0) { ms += 1000; seconds -= 1; } if (seconds < 0) { seconds += 60; minutes -= 1; } if (minutes < 0) { minutes += 60; hours -= 1; } //因爲僅考慮在一天以內,不考慮小時會變成負數的狀況 printf("runtime: %02dhours %02dminutes %02dseconds %02dmilliseconds\n", hours, minutes, seconds, ms); } void newProcess(TCHAR* cWinDir, double duration) { PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); SYSTEMTIME start_time, end_time; memset(&start_time, sizeof(SYSTEMTIME), 0); memset(&end_time, sizeof(SYSTEMTIME), 0); GetSystemTime(&start_time); if (CreateProcess( NULL, //lpApplicationName.若爲空,則lpCommandLine必須指定可執行程序 //若路徑中存在空格,必須使用引號框定 cWinDir, //lpCommandLine //若lpApplicationName爲空,lpCommandLine長度不超過MAX_PATH NULL, //指向一個SECURITY_ATTRIBUTES結構體,這個結構體決定是否返回的句柄能夠被子進程繼承,進程安全性 NULL, // 若是lpProcessAttributes參數爲空(NULL),那麼句柄不能被繼承。<同上>,線程安全性 false, // 指示新進程是否從調用進程處繼承了句柄。句柄可繼承性 0, // 指定附加的、用來控制優先類和進程的建立的標識符(優先級) // CREATE_NEW_CONSOLE 新控制檯打開子進程 // CREATE_SUSPENDED 子進程建立後掛起,直到調用ResumeThread函數 NULL, // 指向一個新進程的環境塊。若是此參數爲空,新進程使用調用進程的環境。指向環境字符串 NULL, // 指定子進程的工做路徑 &si, // 決定新進程的主窗體如何顯示的STARTUPINFO結構體 &pi // 接收新進程的識別信息的PROCESS_INFORMATION結構體。進程線程以及句柄 )) { } else { printf("CreateProcess failed (%d).\n", GetLastError()); return; } //wait untill the child process exits if (abs(duration - 0) < 1e-6) WaitForSingleObject(pi.hProcess, INFINITE);//這裏指定運行時間,單位毫秒 else WaitForSingleObject(pi.hProcess, duration * 1000); GetSystemTime(&end_time); printTime(&start_time, &end_time); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); }
測試程序
#include <iostream> #include <Windows.h> using namespace std; int main(int argc, char* argv[]) { for (int n = 0; n < argc; n++) { printf("arg[%d]:%s\n", n, argv[n]); } Sleep(5*1000); return 0; }
自行編寫程序測試
系統程序測試
添加至環境變量