注:此博客不再更新,所有最新文章将发表在个人独立博客limengting.site。分享技术,记录生活,欢迎大家关注
【实验二】实验要求
设计并实现Unix的“time”命令。“mytime”命令通过命令行参数接受要运行的程序,创建一个独立的进程来运行该程序,并记录程序运行的时间。
在Windows下实现:
使用CreateProcess()来创建进程
使用WaitForSingleObject()在“mytime”命令和新创建的进程之间同步
调用GetSystemTime()来获取时间
在Linux下实现:
使用fork()/vfork /exec()来创建进程运行程序
使用wait()等待新创建的进程结束
调用gettimeofday()来获取时间
mytime的用法:
$ mytime.exe program1
要求输出程序program1运行的时间
$ mytime.exe program2 t
t为时间参数,为program2的输入参数,控制program2的运行时间。最后输出program2的运行时间,应和t基本接近。
显示结果: ××小时××分××秒××毫秒××微秒
1.1~1.8为函数介绍和踩坑分析等废话,想看代码和运行结果请直接划到文末1.9查看。
一、Linux环境下
1.0 程序设计思路
(1)创建子进程之前,先创建time_start和time_end结构体,
struct timeval time_start;
struct timeval time_end;
(2)用fork()函数创建进程,通过返回值来判断是子进程还是父进程。
if (fork() == 0) //子进程
else if (fork() >= 0) //父进程
(3)① 如果第(2)步中是子进程运行,记录子进程开始运行的时间,并在子进程中调用execv()函数,execv()函数从终端传入的参数argv[1]中得知可执行文件的文件名并执行:execv(argv[1],&argv[1])
② 如果第(2)步是父进程在运行,记录(fork后)父进程开始运行的时间gettimeofday(&time_start,NULL);。
如果在终端中是不带时间参数的mytime命令调用,即argc == 2,则先调用wait()等待子进程运行结束,然后再获取进程终止的时间gettimeofday(&time_end,NULL);;如果在终端中是带时间参数的mytime命令调用,即argc == 3,则利用atof()函数将argv[2]中的char*型数据转换为double型数据,父进程调用usleep(sleep_sec * 1000000)函数等待子进程运行sleep_sec秒(即sleep_sec * 1000000微秒)后kill掉子进程,并获取程序终止的时间gettimeofday(&time_end,NULL);
(4)计算程序运行的时间(微秒)并将其转换成××小时××分××秒××毫秒××微秒的格式:
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 * 1000 * 1000);
time_left = time_use % (60 * 60 * 1000 * 1000);
time_min = time_left / (60 * 1000 * 1000);
time_left %= (60 * 1000 * 1000);
time_sec = time_left / (1000 * 1000);
time_left %= (1000 * 1000);
time_ms = time_left / 1000;
time_left %= 1000;
time_us = time_left;
(5)输出程序运行的时间:
printf(“此程序运行的时间为:%ld 小时, %ld 分钟, %ld 秒, %ld 毫秒, %ld 微秒\n”,time_hour, time_min, time_sec, time_ms, time_us);
(6)将c文件编译链接为可执行文件:
gcc exp2_linux.c –o exp2_linux.o
(7)修改.bashrc文件,将exp2_linux.o封装成mytime命令,并用source .bashrc命令使配置生效。
(8)在终端执行mytime program1统计program1的运行时间并输出或者执行mytime program2 t规定program2的执行时间大约为t,运行program2并输出与规定的t差不多的运行时间。
1.1 getpid()和getppid()
getpid()得到本身进程id,getppid()得到父进程进程id,如果已经是父进程,得到系统进程id,如bash环境运行得到bash的id
1.2 fork()
fork()为创建子进程,有三种不同的返回值:
1、在父进程中,fork返回新创建的子进程的PID
2.、在子进程中,fork返回0
3、如果出现错误,fork返回一个负值
另外在子进程被创建之前,只有一个进程在运行,但在fork创建子进程后,两进程同时运行
程序示例:
int main()
{
pid_t pid;
printf("Before fork ...\n");
switch(pid = fork())
{
case -1:
printf("Fork call fail\n");
exit(1);
case 0:
printf("The pid of child is: %d\n", getpid());
printf("The pid of child's parent is: %d\n", getppid());
printf("Child exiting...\n");
exit(0);
default:
printf("The pid of parent is: %d\n", getpid());
printf("the pid of parent's child is: %d\n", pid);
}
printf("After fork, program exiting...\n");
exit(0);
}
输出:
/home/sunnie/CLionProjects/OS_exp2/cmake-build-debug/OS_exp2
Before fork ...
The pid of parent is: 11731
the pid of parent's child is: 11732
After fork, program exiting...
The pid of child is: 11732
The pid of child's parent is: 1
Child exiting...
Process finished with exit code 0
1.3 execv()
1、函数原型
int execv(const char *progname, char *const argv[]); // #include <unistd.h>
2、用法介绍
execv会停止执行当前的进程,并且以progname应用进程替换被停止执行的进程,进程ID没有改变。
progname: 被执行的应用程序。
argv: 传递给应用程序的参数列表, 注意,这个数组的第一个参数应该是应用程序名字本身,并且最后一个参数应该为NULL,不参将多个参数合并为一个参数放入数组。
3、返回值
如果应用程序正常执行完毕,那么execv是永远不会返