diff options
author | bhackerozzo <bhackerozzo@3b445381-d045-0410-9223-e17ecdb95f4b> | 2008-08-28 00:32:21 +0000 |
---|---|---|
committer | bhackerozzo <bhackerozzo@3b445381-d045-0410-9223-e17ecdb95f4b> | 2008-08-28 00:32:21 +0000 |
commit | 20cad7b4efc241a438c7134cc2bfff5f9a8db2f6 (patch) | |
tree | 15ae3cfccb5fb88353916dff377cd1cd5ca82911 | |
parent | 0343c0946a0d4dba4bf161fd992ee6abb49fa955 (diff) | |
download | cpulimit-20cad7b4efc241a438c7134cc2bfff5f9a8db2f6.tar.gz |
simplified and lightweight control algorithm
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | cpulimit.c | 65 | ||||
-rw-r--r-- | process.c | 82 | ||||
-rw-r--r-- | process.h | 40 | ||||
-rw-r--r-- | ptest.c | 31 |
5 files changed, 105 insertions, 119 deletions
@@ -8,9 +8,6 @@ all:: $(TARGETS) cpulimit: cpulimit.c $(LIBS) $(CC) -o cpulimit cpulimit.c $(LIBS) -lrt $(CFLAGS) -ptest: ptest.c process.o procutils.o list.o - $(CC) -o ptest ptest.c process.o procutils.o list.o -lrt $(CFLAGS) - process.o: process.c process.h $(CC) -c process.c $(CFLAGS) @@ -20,6 +17,9 @@ procutils.o: procutils.c procutils.h list.o: list.c list.h $(CC) -c list.c $(CFLAGS) +ptest: ptest.c + $(CC) -o ptest ptest.c -lrt $(CFLAGS) + clean: rm -f *~ *.o $(TARGETS) @@ -146,7 +146,7 @@ void print_usage(FILE *stream, int exit_code) exit(exit_code); } -void limit_process(int pid, float limit) +void limit_process(int pid, double limit) { //slice of the slot in which the process is allowed to run struct timespec twork; @@ -171,9 +171,13 @@ void limit_process(int pid, float limit) if (verbose) printf("Members in the family owned by %d: %d\n", pf.father, pf.members.count); + //rate at which we are keeping active the processes (range 0-1) + //1 means that the process are using all the twork slice + double workingrate = -1; + while(1) { - if (i%100==0 && verbose) print_caption(); + if (i%200==0 && verbose) print_caption(); if (i%10==0) { //update the process family (checks only for new members) @@ -193,45 +197,46 @@ void limit_process(int pid, float limit) //total cpu actual usage (range 0-1) //1 means that the processes are using 100% cpu - float pcpu = 0; - //rate at which we are keeping active the processes (range 0-1) - //1 means that the process are using all the twork slice - float workingrate = 0; - + double pcpu = -1; + //number of processes in the family + int pcount = 0; + //estimate how much the controlled processes are using the cpu in the working interval for (node=pf.members.first; node!=NULL; node=node->next) { struct process *proc = (struct process*)(node->data); - if (process_monitor(proc->history, workingtime, &(proc->history->usage))==-1) { + if (process_monitor(proc->history)==-1) { //process is dead, remove it from family fprintf(stderr,"Process %d dead!\n", proc->pid); remove_process_from_family(&pf, proc->pid); continue; } - pcpu += proc->history->usage.pcpu; - workingrate += proc->history->usage.workingrate; +//printf("pid %d limit %f pcpu %f wrate %f\n", proc->pid, limit, proc->history->usage.pcpu, proc->history->usage.workingrate); + if (proc->history->cpu_usage<0) { + continue; + } + if (pcpu<0) pcpu = 0; + pcpu += proc->history->cpu_usage; + pcount++; } - //average value - workingrate /= pf.members.count; - - //TODO: make workingtime customized for each process, now it's equal for all //adjust work and sleep time slices - if (pcpu>0) { - twork.tv_nsec = MIN(TIME_SLOT*limit*1000/pcpu*workingrate,TIME_SLOT*1000); - } - else if (pcpu==0) { - twork.tv_nsec = TIME_SLOT*1000; - } - else if (pcpu==-1) { - //not yet a valid idea of cpu usage + if (pcpu < 0) { + //it's the 1st cycle, initialize workingrate pcpu = limit; workingrate = limit; - twork.tv_nsec = MIN(TIME_SLOT*limit*1000,TIME_SLOT*1000); + twork.tv_nsec = TIME_SLOT*limit*1000; + } + else { + //adjust workingrate + workingrate = MIN(workingrate / pcpu * limit, 1); + twork.tv_nsec = TIME_SLOT*1000*workingrate; } tsleep.tv_nsec = TIME_SLOT*1000-twork.tv_nsec; +//printf("%lf %lf\n", workingrate, pcpu); + if (verbose && i%10==0 && i>0) { - printf("%0.2f%%\t%6ld us\t%6ld us\t%0.2f%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100); + printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100); } //resume processes @@ -251,8 +256,7 @@ void limit_process(int pid, float limit) workingtime = timediff(&endwork,&startwork); if (tsleep.tv_nsec>0) { - //stop only if the process is active - //stop processes, they have worked enough + //stop only if tsleep>0, instead it's useless for (node=pf.members.first; node!=NULL; node=node->next) { struct process *proc = (struct process*)(node->data); if (kill(proc->pid,SIGSTOP)!=0) { @@ -270,7 +274,6 @@ void limit_process(int pid, float limit) } int main(int argc, char **argv) { - //get program name char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0])); program_name = p==NULL?argv[0]:(p+1); @@ -347,12 +350,12 @@ int main(int argc, char **argv) { } if (!process_ok) { - fprintf(stderr,"Error: You must specify a target process, by name or by PID\n"); + fprintf(stderr,"Error: You must specify a target process, either by name or by PID\n"); print_usage(stderr, 1); exit(1); } if (pid_ok && exe!=NULL) { - fprintf(stderr, "Error: You must specify exactly one process, by name or by PID\n"); + fprintf(stderr, "Error: You must specify exactly one process, either by name or by PID\n"); print_usage(stderr, 1); exit(1); } @@ -361,7 +364,7 @@ int main(int argc, char **argv) { print_usage(stderr, 1); exit(1); } - float limit = perclimit/100.0; + double limit = perclimit/100.0; int cpu_count = get_cpu_count(); if (limit<0 || limit >cpu_count) { fprintf(stderr,"Error: limit must be in the range 0-%d00\n", cpu_count); @@ -382,7 +385,7 @@ int main(int argc, char **argv) { printf("Priority changed to %d\n", priority); } else { - printf("Cannot change priority\n"); + printf("Warning: Cannot change priority. Run as root for best results.\n"); } while(1) { @@ -25,20 +25,17 @@ int process_init(struct process_history *proc, int pid) { - proc->pid = pid; - //init circular queue - proc->front_index = -1; - proc->tail_index = 0; - proc->actual_history_size = 0; - memset(proc->history, 0, sizeof(proc->history)); - proc->total_workingtime = 0; //test /proc file descriptor for reading - sprintf(proc->stat_file, "/proc/%d/stat", proc->pid); + sprintf(proc->stat_file, "/proc/%d/stat", pid); FILE *fd = fopen(proc->stat_file, "r"); + if (fd == NULL) return 1; fclose(fd); - proc->usage.pcpu = 0; - proc->usage.workingrate = 0; - return (fd == NULL); + //init properties + proc->pid = pid; + proc->cpu_usage = 0; + memset(&(proc->last_sample), 0, sizeof(struct timespec)); + proc->last_jiffies = -1; + return 0; } //return t1-t2 in microseconds (no overflow checks, so better watch out!) @@ -65,46 +62,43 @@ static int get_jiffies(struct process_history *proc) { return utime+ktime; } -int process_monitor(struct process_history *proc, int last_working_quantum, struct cpu_usage *usage) -{ - //increment front index - proc->front_index = (proc->front_index+1) % HISTORY_SIZE; - - //actual history size is: (front-tail+HISTORY_SIZE)%HISTORY_SIZE+1 - proc->actual_history_size = (proc->front_index - proc->tail_index + HISTORY_SIZE) % HISTORY_SIZE + 1; - - //actual front and tail of the queue - struct process_screenshot *front = &(proc->history[proc->front_index]); - struct process_screenshot *tail = &(proc->history[proc->tail_index]); +#define ALFA 0.08 - //take a shot and save in front +int process_monitor(struct process_history *proc) +{ int j = get_jiffies(proc); if (j<0) return -1; //error retrieving jiffies count (maybe the process is dead) - front->jiffies = j; - clock_gettime(CLOCK_REALTIME, &(front->when)); - front->cputime = last_working_quantum; - - if (proc->actual_history_size==1) { - //not enough elements taken (it's the first one!), return 0 - usage->pcpu = -1; - usage->workingrate = 1; + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + if (proc->last_jiffies==-1) { + //store current time + proc->last_sample = now; + //store current jiffies + proc->last_jiffies = j; + //it's the first sample, cannot figure out the cpu usage + proc->cpu_usage = -1; return 0; } + //time from previous sample (in ns) + long dt = timediff(&now, &(proc->last_sample)); + //how many jiffies in dt? + double max_jiffies = dt * HZ / 1000000.0; + double sample = (j - proc->last_jiffies) / max_jiffies; + if (proc->cpu_usage == -1) { + //initialization + proc->cpu_usage = sample; + } else { - //queue has enough elements - //now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds - long dt = timediff(&(front->when), &(tail->when)); - //the total time between tail and front in which the process was allowed to run - proc->total_workingtime += front->cputime - tail->cputime; - int used_jiffies = front->jiffies - tail->jiffies; - float usage_ratio = (used_jiffies*1000000.0/HZ) / proc->total_workingtime; - usage->workingrate = 1.0 * proc->total_workingtime / dt; - usage->pcpu = usage_ratio * usage->workingrate; - //increment tail index if the queue is full - if (proc->actual_history_size==HISTORY_SIZE) - proc->tail_index = (proc->tail_index+1) % HISTORY_SIZE; - return 0; + //adaptative adjustment + proc->cpu_usage = (1-ALFA) * proc->cpu_usage + ALFA * sample; } + + //store current time + proc->last_sample = now; + //store current jiffies + proc->last_jiffies = j; + + return 0; } int process_close(struct process_history *proc) @@ -32,26 +32,8 @@ #include <sys/time.h> #include <unistd.h> -//kernel time resolution (inverse of one jiffy interval) in Hertz +//kernel time resolution timer interrupt frequency in Hertz #define HZ sysconf(_SC_CLK_TCK) -//size of the history circular queue -#define HISTORY_SIZE 10 - -//extracted process statistics -struct cpu_usage { - float pcpu; - float workingrate; -}; - -//process instant photo -struct process_screenshot { - //timestamp - struct timespec when; - //total jiffies used by the process - int jiffies; - //cpu time used since previous screenshot expressed in microseconds - int cputime; -}; //process descriptor struct process_history { @@ -61,23 +43,17 @@ struct process_history { char stat_file[20]; //read buffer for /proc filesystem char buffer[1024]; - //recent history of the process (fixed circular queue) - struct process_screenshot history[HISTORY_SIZE]; - //the index of the last screenshot made to the process - int front_index; - //the index of the oldest screenshot made to the process - int tail_index; - //how many screenshots we have in the queue (max HISTORY_SIZE) - int actual_history_size; - //total cputime saved in the history - long total_workingtime; - //current usage - struct cpu_usage usage; + //timestamp when last_j and cpu_usage was calculated + struct timespec last_sample; + //total number of jiffies used by the process at time last_sample + int last_jiffies; + //cpu usage estimation (value in range 0-1) + double cpu_usage; }; int process_init(struct process_history *proc, int pid); -int process_monitor(struct process_history *proc, int last_working_quantum, struct cpu_usage *usage); +int process_monitor(struct process_history *proc); int process_close(struct process_history *proc); @@ -3,15 +3,18 @@ #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> -#include "procutils.h" +#include <sys/time.h> +#include <stdlib.h> +#include <unistd.h> -#define N 10 +#define N 8 //simple program to test cpulimit int main() { printf("Parent: PID %d\n", getpid()); getchar(); + sleep(1); int i; int children[N]; for (i=0;i<N;i++) { @@ -22,14 +25,24 @@ int main() printf("Child %d created\n", pid); } else if (pid==0) { + while(1); //child code - struct timeval t; - gettimeofday(&t, NULL); - srandom(t.tv_usec + getpid()); - int time = random() % 2000000; -// printf("Child %d wait %d\n", getpid(), time); - usleep(time); - printf("Child %d terminated\n", getpid()); +// while(1) { + //random generator initialization + struct timeval t; + gettimeofday(&t, NULL); + srandom(t.tv_sec + t.tv_usec + getpid()); + int loop = random() % 1000000; + printf("start\n"); + int i,j; + for (i=0;i<1000;i++) + for (j=0;j<loop;j++); + printf("stop\n"); + int time = random() % 10000000; + // printf("Child %d wait %d\n", getpid(), time); + usleep(time); + printf("Child %d terminated\n", getpid()); +// } exit(0); } else { |