一、软件需求
在同时运行一个或多个一味消耗 CPU 时间执行处理的进程时,采集以下统计信息。
・在某一时间点运行在逻辑 CPU 上的进程是哪一个
・每个进程的运行进度通过分析这些信息,来确认本章开头对调度器的描述是否正确。实验程序的设计如下。
1) 命令行参数
・第 1 个参数(n):同时运行的进程数量
・第 2 个参数(total):程序运行的总时长(单位:毫秒)
・第 3 个参数(resol):采集统计信息的间隔(单位:毫秒)
2) 令 n 个进程同时运行,各个进程按照以下要求运行
・在消耗 total 毫秒的 CPU 时间后结束运行
・每 resol 毫秒记录一次以下 3 个数值:
①每个进程的唯一 ID(0~n−1 的各个进程独有的编号);
②从程序开始运行到记录的时间点为止经过的时间(单位:毫秒);
③进程的进度(单位:%),在结束运行后,把所有统计信息用制表符分隔并逐行输出。
在同时运行多个 CPU 密集型进程时,需采集以下统计信息:
- 当前运行在逻辑 CPU 上的进程 ID
- 每个进程的运行进度百分比
实验程序设计要求:
1. 命令行参数
参数 | 说明 | 示例值 |
n | 并发进程数量 | 3 |
total | 总运行时长(毫秒) | 50 |
resol | 统计间隔(毫秒) | 1 |
2. 进程行为规范
- 每个进程消耗total毫秒 CPU 时间后结束
- 每resol毫秒记录:
- 进程 ID(0~n-1)
- 累计运行时间(ms)
- 进度百分比((i+1)*100/nrecord,nrecord=total/resol)
二、代码实现
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#define NLOOP_FOR_ESTIMETION 1000000000UL
#define NSEC_PER_SEC 1000000000UL
#define NSEC_PER_MSEC 1000000UL
/* 获取纳秒差 */
static inline long diff_nsec(struct timespec before, struct timespec after)
{
return ((after.tv_sec * NSEC_PER_SEC + after.tv_nsec)
- (before.tv_sec * NSEC_PER_SEC + before.tv_nsec));
}
/* 每微秒消耗的空循环的次数 */
static unsigned long loops_per_msec(void)
{
struct timespec before, after;
clock_gettime(CLOCK_MONOTONIC, &before);
unsigned long i;
for(i = 0; i < NLOOP_FOR_ESTIMETION; i++)
;
clock_gettime(CLOCK_MONOTONIC, &after);
return ((NLOOP_FOR_ESTIMETION * NSEC_PER_MSEC) / diff_nsec(before, after));
}
static inline void load(unsigned long nloops)
{
unsigned long i;
for(i = 0; i < nloops; i++ )
;
}
static void chidld_fn(int nproc, struct timespec *buf, int nrecord,
unsigned long nloop_per_resol, struct timespec start)
{
int i;
struct timespec ts;
for(i = 0; i < nrecord; i++)
{
load(nloop_per_resol);
clock_gettime(CLOCK_MONOTONIC, &ts);
buf[i] = ts;
}
printf("nproc\tms\t step\n");
printf("---------------------\n");
for(i = 0; i < nrecord; i++)
{
printf("%d\t%ld\t%d\n", nproc,
diff_nsec(start, buf[i]) / NSEC_PER_MSEC,
((i + 1) * 100) / nrecord );
}
printf("---------------------\n");
exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[])
{
int ret = EXIT_FAILURE;
if( 4 > argc)
{
fprintf(stderr, "usage: %s <nproc> <total[ms]> <resolution[ms]>\n", argv[0]);
exit(EXIT_FAILURE);
}
int nproc = atoi(argv[1]); //同时运行的进程的数量
int total = atoi(argv[2]); //程序运行的总时长,ms为单位
int resol = atoi(argv[3]); //采集统计信息的间隔
if(1 > nproc) //对进程数量参数过滤非法值
{
fprintf(stderr, "<nproc> (%d) should be >= 1\n", nproc);
exit(EXIT_FAILURE);
}
if(1 > total) //对程序时长参数过滤非法值
{
fprintf(stderr, "<total> (%d) should be >= 1\n", total);
exit(EXIT_FAILURE);
}
if(1 > resol) //对采集统计信息参数过滤非法值
{
fprintf(stderr, "<resol> (%d) should be >= 1\n", resol);
exit(EXIT_FAILURE);
}
if(total % resol)
{
fprintf(stderr, "<total> (%d) should be mutliple of <resol> (%d) \n",total, resol);
exit(EXIT_FAILURE);
}
int nrecord = total / resol;
struct timespec* logbug = malloc(nrecord * sizeof(struct timespec)); //堆中开辟内存
if(!logbug)
{
err(EXIT_FAILURE, "malloc(nrecord) failed");
}
puts("estimeting workload which just take one millsecond");
unsigned long nloops_per_resol = loops_per_msec() * resol;
puts("end estimeting");
fflush(stdout); // fflush log
pid_t* pids = malloc(nrecord * sizeof(pid_t));
if(!pids)
{
warn("malloc (pids) failed");
goto free_logbuf;
}
int i, ncreated;
struct timespec start;
clock_gettime(CLOCK_MONOTONIC, &start);
for(i = 0, ncreated = 0; i < nproc; i++, ncreated++)
{
pids[i] = fork();
if( 0 > pids[i])
{
goto wait_children;
}
else if( 0 == pids[i])
{//子进程执行空间
chidld_fn(i, logbug, nrecord, nloops_per_resol, start);
}
}
ret = EXIT_SUCCESS;
wait_children:
if(EXIT_FAILURE == ret) //杀死所有已创建的子进程.
{
for(i = 0; i < ncreated; i++)
{
if(0 > kill(pids[i], SIGINT))
{
warn("kill(%d) failed", pids[i]);
}
}
}
for(i = 0; i < ncreated; i++) //等待子进程结束
{
if(0 > wait(NULL))
{
warn("wait() failed");
}
}
free_pids:
free(pids);
free_logbuf:
free(logbug);
exit(ret);
}
三、编译配置(Makefile)
TARGET := app
#src file #g++ -std=c++11 $(CPPFLAGS) $^ -I. -o $@
SRC := sched.c
#cppflags setting
CPPFLAGS := -pthread
all:$(TARGET)
@echo "make successfull"
$(TARGET): $(SRC)
@echo $(SRC)
gcc $(CPPFLAGS) $^ -I. -o $@
clean:
rm $(TARGET)
.PHONY:all,clean
四、实验结果
$ taskset -c 0 ./app 3 50 1
estimeting workload which just take one millsecond
end estimeting
nproc ms step
---------------------
2 0 2
2 1 4
2 2 6
2 2 8
2 3 10
2 3 12
2 4 14
2 5 16
2 23 18
2 24 20
2 25 22
2 26 24
2 28 26
2 37 28
2 38 30
2 40 32
2 50 34
2 52 36
2 53 38
2 55 40
2 56 42
2 74 44
2 76 46
2 85 48
2 86 50
2 87 52
2 88 54
2 89 56
2 90 58
2 90 60
2 91 62
2 92 64
2 109 66
2 110 68
2 112 70
2 113 72
2 131 74
2 132 76
2 133 78
2 135 80
2 142 82
2 143 84
2 144 86
2 157 88
2 159 90
2 167 92
2 168 94
2 169 96
2 170 98
2 172 100
---------------------
nproc ms step
---------------------
0 10 2
0 11 4
0 12 6
0 13 8
0 14 10
0 15 12
0 16 14
0 34 16
0 35 18
0 45 20
0 46 22
0 49 24
0 60 26
0 61 28
0 63 30
0 64 32
0 78 34
0 78 36
0 79 38
0 80 40
0 81 42
0 93 44
0 94 46
0 95 48
0 95 50
0 96 52
0 97 54
0 97 56
0 98 58
0 99 60
0 101 62
0 115 64
0 116 66
0 126 68
0 128 70
0 129 72
0 138 74
0 139 76
0 140 78
0 150 80
0 152 82
0 153 84
0 154 86
0 154 88
0 155 90
0 156 92
0 173 94
0 174 96
0 175 98
0 176 100
---------------------
nproc ms step
---------------------
1 6 2
1 7 4
1 8 6
1 9 8
1 19 10
1 20 12
1 30 14
1 31 16
1 33 18
1 43 20
1 44 22
1 58 24
1 59 26
1 67 28
1 68 30
1 69 32
1 71 34
1 72 36
1 82 38
1 83 40
1 84 42
1 101 44
1 102 46
1 103 48
1 105 50
1 106 52
1 109 54
1 118 56
1 120 58
1 121 60
1 124 62
1 125 64
1 146 66
1 148 68
1 149 70
1 161 72
1 162 74
1 164 76
1 165 78
1 178 80
1 179 82
1 180 84
1 182 86
1 183 88
1 185 90
1 200 92
1 201 94
1 202 96
1 202 98
1 203 100
---------------------