linux与Windows进程控制
进程管理控制
这里实现的是一个自定义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); 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; }
测试
-
自行编写程序测试
-
系统程序测试
-
添加至环境变量