行程管理控制
這里實作的是一個自定義timer用于統計子行程運行的時間,使用方式主要是
timer [-t seconds] command arguments
例如要統計ls的運行時間可以直接輸入timer ls,其后的arguments是指所要運行的程式的引數,如:timer ls -al,如果要指定程式運行多少時間,如5秒鐘,可以輸入timer -t 5 ls -al,需要注意的是,該程式對輸入沒有做例外檢測,所以要確保程式輸入正確,
Linux
程式思路
-
獲取時間
時間獲取函式使用
gettimeofday,精度可以達到微秒struct timeval{ long tv_sec;*//秒* long tv_usec;*//微秒* } -
子行程創建
-
fork()函式#include <sys/types.h> #include <unistd.h> pid_t fork(void);fork呼叫失敗則回傳-1,呼叫成功則:fork函式會有兩種回傳值,一是為0,一是為正整數,若為0,則說明當前行程為子行程;若為正整數,則該行程為父行程且該值為子行程pid,關于行程控制的詳細說明請參考:行程控制
-
exec函式用fork創建子行程后執行的是和父行程相同的程式(但有可能執行不同的代碼分支),子行程往往要呼叫一種exec函式以執行另一個程式,當行程呼叫一種exec函式時,該行程的用戶空間代碼和資料完全被新程式替換,從新程式的啟動例程開始執行,呼叫exec并不創建新行程,所以呼叫exec前后該行程的id并未改變,
其實有六種以exec開頭的函式,統稱exec函式:#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時可能會:
-
阻塞(如果它的所有子行程都還在運行
-
帶子行程的終止資訊立即回傳(如果一個子行程已終止,正等待父行程讀取其終止資訊)
-
出錯立即回傳(如果它沒有任何子行程)
這兩個函式的區別是:
- 如果父行程的所有子行程都還在運行,呼叫wait將使父行程阻塞,而呼叫waitpid時如果在options引數中指定WNOHANG可以使父行程不阻塞而立即回傳0
- wait等待第一個終止的子行程,而waitpid可以通過pid引數指定等待哪一個子行程
-
?
源代碼
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
在Windows下進行父子行程的創建和管理在api呼叫上相較Linux有一定難度,但實際上在使用管理上比Linux容易的多,
CreateProcess
#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);
//建議大家不要單獨傳入lpApplicationName,而是將程式名放入cWinDir中
//這樣會自動搜索PATH
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;
}
測驗
-
自行撰寫程式測驗

-
系統程式測驗

-
添加至環境變數

參考資料
Windows
- CreateProcess
Linux
-
行程控制
-
行程控制,linux環境
-
Linux父行程創建子行程的方法
-
Linux創建子行程執行任務
-
execv Linux man page
-
execv函式的應用
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/84883.html
標籤:C++
