/**
- * Copyright (c) 2005, by: Angelo Marletta <marlonx80@hotmail.com>
*
- * This file may be used subject to the terms and conditions of the
- * GNU Library General Public License Version 2, or any later version
- * at your option, as published by the Free Software Foundation.
+ * cpulimit - a cpu limiter for Linux
+ *
+ * Copyright (C) 2005-2008, by: Angelo Marletta <marlonx80@hotmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
*
- * Simple program to limit the cpu usage of a process
+ * This is a simple program to limit the cpu usage of a process
* If you modify this code, send me a copy please
*
- * Author: Angelo Marletta
- * Date: 26/06/2005
- * Version: 1.1
- * Last version at: http://marlon80.interfree.it/cpulimit/index.html
+ * Date: 15/2/2008
+ * Version: 1.2 alpha
+ * Get the latest version at: http://cpulimit.sourceforge.net
*
* Changelog:
- * - Fixed a segmentation fault if controlled process exited in particular circumstances
- * - Better CPU usage estimate
- * - Fixed a <0 %CPU usage reporting in rare cases
- * - Replaced MAX_PATH_SIZE with PATH_MAX already defined in <limits.h>
- * - Command line arguments now available
- * - Now is possible to specify target process by pid
+ * - reorganization of the code, splitted in more source files
+ * - control function process_monitor() optimized by eliminating an unnecessary loop
+ * - experimental support for multiple control of children processes and threads
+ * children detection algorithm seems heavy because of the amount of code,
+ * but it's designed to be scalable when there are a lot of children processes
+ * - cpu count detection, i.e. if you have 4 cpu, it is possible to limit up to 400%
+ * - in order to avoid deadlock, cpulimit prevents to limit itself
+ * - option --path eliminated, use --exe instead both for absolute path and file name
+ * - deleted almost every setpriority(), just set it once at startup
+ * - minor enhancements and bugfixes
+ *
*/
#include <getopt.h>
#include <stdio.h>
+#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
-//kernel time resolution (inverse of one jiffy interval) in Hertz
-//i don't know how to detect it, then define to the default (not very clean!)
-#define HZ 100
+#include "process.h"
+#include "procutils.h"
+#include "list.h"
//some useful macro
-#define min(a,b) (a<b?a:b)
-#define max(a,b) (a>b?a:b)
-
-//pid of the controlled process
-int pid=0;
-//executable file name
+#define MIN(a,b) (a<b?a:b)
+#define MAX(a,b) (a>b?a:b)
+#define print_caption() printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n")
+//control time slot in microseconds
+//each slot is splitted in a working slice and a sleeping slice
+#define CONTROL_SLOT 100000
+
+#define MAX_PRIORITY -10
+
+//the "family"
+struct process_family pf;
+//pid of cpulimit
+int cpulimit_pid;
+//name of this program (maybe cpulimit...)
char *program_name;
+
+/* CONFIGURATION VARIABLES */
+
//verbose mode
-int verbose=0;
-//lazy mode
-int lazy=0;
+int verbose = 0;
+//lazy mode (exits if there is no process)
+int lazy = 0;
+
+//how many cpu do we have?
+int get_cpu_count()
+{
+ FILE *fd;
+ int cpu_count = 0;
+ char line[100];
+ fd = fopen("/proc/stat", "r");
+ if (fd < 0)
+ return 0; //are we running Linux??
+ while (fgets(line,sizeof(line),fd)!=NULL) {
+ if (strncmp(line, "cpu", 3) != 0) break;
+ cpu_count++;
+ }
+ fclose(fd);
+ return cpu_count - 1;
+}
-//reverse byte search
-void *memrchr(const void *s, int c, size_t n);
+//return t1-t2 in microseconds (no overflow checks, so better watch out!)
+inline unsigned long timediff(const struct timespec *t1,const struct timespec *t2)
+{
+ return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_nsec/1000 - t2->tv_nsec/1000);
+}
-//return ta-tb in microseconds (no overflow checks!)
-inline long timediff(const struct timespec *ta,const struct timespec *tb) {
- unsigned long us = (ta->tv_sec-tb->tv_sec)*1000000 + (ta->tv_nsec/1000 - tb->tv_nsec/1000);
- return us;
+//returns t1-t2 in microseconds
+inline unsigned long long tv_diff(struct timeval *t1, struct timeval *t2)
+{
+ return ((unsigned long long)(t1->tv_sec - t2->tv_sec)) * 1000000ULL + t1->tv_usec - t2->tv_usec;
}
-int waitforpid(int pid) {
- //switch to low priority
- if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
- printf("Warning: cannot renice\n");
+//SIGINT and SIGTERM signal handler
+void quit(int sig)
+{
+ //let all the processes continue if stopped
+ struct list_node *node = NULL;
+ for (node=pf.members.first; node!= NULL; node=node->next) {
+ struct process *p = (struct process*)(node->data);
+ process_close(p->history);
+ kill(p->pid, SIGCONT);
}
+ //free all the memory
+ cleanup_process_family(&pf);
+ exit(0);
+}
- int i=0;
-
- while(1) {
+void print_usage(FILE *stream, int exit_code)
+{
+ fprintf(stream, "Usage: %s TARGET [OPTIONS...]\n",program_name);
+ fprintf(stream, " TARGET must be exactly one of these:\n");
+ fprintf(stream, " -p, --pid=N pid of the process (implies -z)\n");
+ fprintf(stream, " -e, --exe=FILE name of the executable program file or absolute path name\n");
+ fprintf(stream, " OPTIONS\n");
+ fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to 100 (required)\n");
+ fprintf(stream, " -v, --verbose show control statistics\n");
+ fprintf(stream, " -z, --lazy exit if there is no suitable target process, or if it dies\n");
+ fprintf(stream, " -h, --help display this help and exit\n");
+ exit(exit_code);
+}
- DIR *dip;
- struct dirent *dit;
+void limit_process(int pid, float limit)
+{
+ //slice of the slot in which the process is allowed to run
+ struct timespec twork;
+ //slice of the slot in which the process is stopped
+ struct timespec tsleep;
+ //when the last twork has started
+ struct timespec startwork;
+ //when the last twork has finished
+ struct timespec endwork;
+ //initialization
+ memset(&twork, 0, sizeof(struct timespec));
+ memset(&tsleep, 0, sizeof(struct timespec));
+ memset(&startwork, 0, sizeof(struct timespec));
+ memset(&endwork, 0, sizeof(struct timespec));
+ //last working time in microseconds
+ unsigned long workingtime = 0;
+ int i = 0;
+
+ //build the family
+ create_process_family(&pf, pid);
+ struct list_node *node;
+
+ if (verbose) printf("Members in the family owned by %d: %d\n", pf.father, pf.members.count);
- //open a directory stream to /proc directory
- if ((dip = opendir("/proc")) == NULL) {
- perror("opendir");
- return -1;
- }
+ while(1) {
- //read in from /proc and seek for process dirs
- while ((dit = readdir(dip)) != NULL) {
- //get pid
- if (pid==atoi(dit->d_name)) {
- //pid detected
- if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) {
- //process is ok!
- goto done;
- }
- else {
- fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid);
+ if (i%100==0 && verbose) print_caption();
+
+ if (i%10==0) {
+ //update the process family (checks only for new members)
+ int newborn = check_new_members(&pf);
+ if (newborn) {
+ printf("%d new children processes detected (", newborn);
+ int j;
+ node = pf.members.last;
+ for (j=0; j<newborn; j++) {
+ printf("%d", ((struct process*)(node->data))->pid);
+ if (j<newborn-1) printf(" ");
+ node = node->previous;
}
+ printf(")\n");
}
}
- //close the dir stream and check for errors
- if (closedir(dip) == -1) {
- perror("closedir");
- return -1;
- }
-
- //no suitable target found
- if (i++==0) {
- if (lazy) {
- fprintf(stderr,"No process found\n");
- exit(2);
- }
- else {
- printf("Warning: no target process found. Waiting for it...\n");
+ //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;
+
+ //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) {
+ //process is dead, remove it from family
+ remove_process_from_family(&pf, proc->pid);
+ fprintf(stderr,"Process %d dead!\n", proc->pid);
}
+ pcpu += proc->history->usage.pcpu;
+ workingrate += proc->history->usage.workingrate;
}
+ //average value
+ workingrate /= pf.members.count;
- //sleep for a while
- sleep(2);
- }
+ //TODO: make workingtime customized for each process, now it's equal for all
-done:
- printf("Process %d detected\n",pid);
- //now set high priority, if possible
- if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
- printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
- }
- return 0;
-
-}
-
-//this function periodically scans process list and looks for executable path names
-//it should be executed in a low priority context, since precise timing does not matter
-//if a process is found then its pid is returned
-//process: the name of the wanted process, can be an absolute path name to the executable file
-// or simply its name
-//return: pid of the found process
-int getpidof(const char *process) {
-
- //set low priority
- if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
- printf("Warning: cannot renice\n");
- }
-
- char exelink[20];
- char exepath[PATH_MAX+1];
- int pid=0;
- int i=0;
-
- while(1) {
-
- DIR *dip;
- struct dirent *dit;
-
- //open a directory stream to /proc directory
- if ((dip = opendir("/proc")) == NULL) {
- perror("opendir");
- return -1;
+ //adjust work and sleep time slices
+ if (pcpu>0) {
+ twork.tv_nsec = MIN(CONTROL_SLOT*limit*1000/pcpu*workingrate,CONTROL_SLOT*1000);
}
-
- //read in from /proc and seek for process dirs
- while ((dit = readdir(dip)) != NULL) {
- //get pid
- pid=atoi(dit->d_name);
- if (pid>0) {
- sprintf(exelink,"/proc/%d/exe",pid);
- int size=readlink(exelink,exepath,sizeof(exepath));
- if (size>0) {
- int found=0;
- if (process[0]=='/' && strncmp(exepath,process,size)==0 && size==strlen(process)) {
- //process starts with / then it's an absolute path
- found=1;
- }
- else {
- //process is the name of the executable file
- if (strncmp(exepath+size-strlen(process),process,strlen(process))==0) {
- found=1;
- }
- }
- if (found==1) {
- if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) {
- //process is ok!
- goto done;
- }
- else {
- fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid);
- }
- }
- }
- }
+ else if (pcpu==0) {
+ twork.tv_nsec = CONTROL_SLOT*1000;
}
+ else if (pcpu==-1) {
+ //not yet a valid idea of cpu usage
+ pcpu = limit;
+ workingrate = limit;
+ twork.tv_nsec = MIN(CONTROL_SLOT*limit*1000,CONTROL_SLOT*1000);
+ }
+ tsleep.tv_nsec = CONTROL_SLOT*1000-twork.tv_nsec;
- //close the dir stream and check for errors
- if (closedir(dip) == -1) {
- perror("closedir");
- return -1;
+ 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);
}
- //no suitable target found
- if (i++==0) {
- if (lazy) {
- fprintf(stderr,"No process found\n");
- exit(2);
- }
- else {
- printf("Warning: no target process found. Waiting for it...\n");
+ //resume processes
+ for (node=pf.members.first; node!=NULL; node=node->next) {
+ struct process *proc = (struct process*)(node->data);
+ if (kill(proc->pid,SIGCONT)!=0) {
+ //process is dead, remove it from family
+ fprintf(stderr,"Process %d dead!\n", proc->pid);
+ remove_process_from_family(&pf, proc->pid);
}
}
- //sleep for a while
- sleep(2);
- }
-
-done:
- printf("Process %d detected\n",pid);
- //now set high priority, if possible
- if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
- printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
- }
- return pid;
-
-}
-
-//SIGINT and SIGTERM signal handler
-void quit(int sig) {
- //let the process continue if it's stopped
- kill(pid,SIGCONT);
- printf("Exiting...\n");
- exit(0);
-}
-
-//get jiffies count from /proc filesystem
-int getjiffies(int pid) {
- static char stat[20];
- static char buffer[1024];
- sprintf(stat,"/proc/%d/stat",pid);
- FILE *f=fopen(stat,"r");
- if (f==NULL) return -1;
- fgets(buffer,sizeof(buffer),f);
- fclose(f);
- char *p=buffer;
- p=memchr(p+1,')',sizeof(buffer)-(p-buffer));
- int sp=12;
- while (sp--)
- p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
- //user mode jiffies
- int utime=atoi(p+1);
- p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
- //kernel mode jiffies
- int ktime=atoi(p+1);
- return utime+ktime;
-}
-
-//process instant photo
-struct process_screenshot {
- struct timespec when; //timestamp
- int jiffies; //jiffies count of the process
- int cputime; //microseconds of work from previous screenshot to current
-};
-
-//extracted process statistics
-struct cpu_usage {
- float pcpu;
- float workingrate;
-};
-
-//this function is an autonomous dynamic system
-//it works with static variables (state variables of the system), that keep memory of recent past
-//its aim is to estimate the cpu usage of the process
-//to work properly it should be called in a fixed periodic way
-//perhaps i will put it in a separate thread...
-int compute_cpu_usage(int pid,int last_working_quantum,struct cpu_usage *pusage) {
- #define MEM_ORDER 10
- //circular buffer containing last MEM_ORDER process screenshots
- static struct process_screenshot ps[MEM_ORDER];
- //the last screenshot recorded in the buffer
- static int front=-1;
- //the oldest screenshot recorded in the buffer
- static int tail=0;
-
- if (pusage==NULL) {
- //reinit static variables
- front=-1;
- tail=0;
- return 0;
- }
-
- //let's advance front index and save the screenshot
- front=(front+1)%MEM_ORDER;
- int j=getjiffies(pid);
- if (j>=0) ps[front].jiffies=j;
- else return -1; //error: pid does not exist
- clock_gettime(CLOCK_REALTIME,&(ps[front].when));
- ps[front].cputime=last_working_quantum;
-
- //buffer actual size is: (front-tail+MEM_ORDER)%MEM_ORDER+1
- int size=(front-tail+MEM_ORDER)%MEM_ORDER+1;
-
- if (size==1) {
- //not enough samples taken (it's the first one!), return -1
- pusage->pcpu=-1;
- pusage->workingrate=1;
- return 0;
- }
- else {
- //now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds
- long dt=timediff(&(ps[front].when),&(ps[tail].when));
- long dtwork=0;
- int i=(tail+1)%MEM_ORDER;
- int max=(front+1)%MEM_ORDER;
- do {
- dtwork+=ps[i].cputime;
- i=(i+1)%MEM_ORDER;
- } while (i!=max);
- int used=ps[front].jiffies-ps[tail].jiffies;
- float usage=(used*1000000.0/HZ)/dtwork;
- pusage->workingrate=1.0*dtwork/dt;
- pusage->pcpu=usage*pusage->workingrate;
- if (size==MEM_ORDER)
- tail=(tail+1)%MEM_ORDER;
- return 0;
+ //now processes are free to run (same working slice for all)
+ clock_gettime(CLOCK_REALTIME,&startwork);
+ nanosleep(&twork,NULL);
+ clock_gettime(CLOCK_REALTIME,&endwork);
+ workingtime = timediff(&endwork,&startwork);
+
+ //stop processes, they have worked enough
+ //resume processes
+ for (node=pf.members.first; node!=NULL; node=node->next) {
+ struct process *proc = (struct process*)(node->data);
+ if (kill(proc->pid,SIGSTOP)!=0) {
+ //process is dead, remove it from family
+ fprintf(stderr,"Process %d dead!\n", proc->pid);
+ remove_process_from_family(&pf, proc->pid);
+ }
+ }
+ //now the process is forced to sleep
+ nanosleep(&tsleep,NULL);
+ i++;
}
- #undef MEM_ORDER
-}
-
-void print_caption() {
- printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n");
-}
-
-void print_usage(FILE *stream,int exit_code) {
- fprintf(stream, "Usage: %s TARGET [OPTIONS...]\n",program_name);
- fprintf(stream, " TARGET must be exactly one of these:\n");
- fprintf(stream, " -p, --pid=N pid of the process\n");
- fprintf(stream, " -e, --exe=FILE name of the executable program file\n");
- fprintf(stream, " -P, --path=PATH absolute path name of the executable program file\n");
- fprintf(stream, " OPTIONS\n");
- fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to 100 (mandatory)\n");
- fprintf(stream, " -v, --verbose show control statistics\n");
- fprintf(stream, " -z, --lazy exit if there is no suitable target process, or if it dies\n");
- fprintf(stream, " -h, --help display this help and exit\n");
- exit(exit_code);
+ cleanup_process_family(&pf);
}
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);
+ cpulimit_pid = getpid();
+
+ //argument variables
+ const char *exe = NULL;
+ int perclimit = 0;
+ int pid_ok = 0;
+ int process_ok = 0;
+ int limit_ok = 0;
+ int pid = 0;
+
//parse arguments
int next_option;
- /* A string listing valid short options letters. */
- const char* short_options="p:e:P:l:vzh";
- /* An array describing valid long options. */
+ int option_index = 0;
+ //A string listing valid short options letters
+ const char* short_options = "p:e:l:vzh";
+ //An array describing valid long options
const struct option long_options[] = {
- { "pid", 0, NULL, 'p' },
- { "exe", 1, NULL, 'e' },
- { "path", 0, NULL, 'P' },
- { "limit", 0, NULL, 'l' },
- { "verbose", 0, NULL, 'v' },
- { "lazy", 0, NULL, 'z' },
- { "help", 0, NULL, 'h' },
- { NULL, 0, NULL, 0 }
+ { "pid", required_argument, NULL, 'p' },
+ { "exe", required_argument, NULL, 'e' },
+ { "limit", required_argument, NULL, 'l' },
+ { "verbose", no_argument, &verbose, 'v' },
+ { "lazy", no_argument, &lazy, 'z' },
+ { "help", no_argument, NULL, 'h' },
+ { 0, 0, 0, 0 }
};
- //argument variables
- const char *exe=NULL;
- const char *path=NULL;
- int perclimit=0;
- int pid_ok=0;
- int process_ok=0;
- int limit_ok=0;
do {
- next_option = getopt_long (argc, argv, short_options,long_options, NULL);
+ next_option = getopt_long(argc, argv, short_options,long_options, &option_index);
switch(next_option) {
case 'p':
- pid=atoi(optarg);
- pid_ok=1;
+ pid = atoi(optarg);
+ //todo: verify pid is valid
+ pid_ok = 1;
+ process_ok = 1;
break;
case 'e':
- exe=optarg;
- process_ok=1;
- break;
- case 'P':
- path=optarg;
- process_ok=1;
+ exe = optarg;
+ process_ok = 1;
break;
case 'l':
- perclimit=atoi(optarg);
- limit_ok=1;
+ perclimit = atoi(optarg);
+ limit_ok = 1;
break;
case 'v':
- verbose=1;
+ verbose = 1;
break;
case 'z':
- lazy=1;
+ lazy = 1;
break;
case 'h':
- print_usage (stdout, 1);
+ print_usage(stdout, 1);
break;
case '?':
- print_usage (stderr, 1);
+ print_usage(stderr, 1);
break;
case -1:
break;
}
} while(next_option != -1);
- if (!process_ok && !pid_ok) {
- fprintf(stderr,"Error: You must specify a target process\n");
- print_usage (stderr, 1);
+ if (pid!=0) {
+ lazy = 1;
+ }
+
+ if (!process_ok) {
+ fprintf(stderr,"Error: You must specify a target process, by name or by PID\n");
+ print_usage(stderr, 1);
exit(1);
}
- if ((exe!=NULL && path!=NULL) || (pid_ok && (exe!=NULL || path!=NULL))) {
- fprintf(stderr,"Error: You must specify exactly one target process\n");
- print_usage (stderr, 1);
+ if (pid_ok && exe!=NULL) {
+ fprintf(stderr, "Error: You must specify exactly one process, by name or by PID\n");
+ print_usage(stderr, 1);
exit(1);
}
if (!limit_ok) {
- fprintf(stderr,"Error: You must specify a cpu limit\n");
- print_usage (stderr, 1);
+ fprintf(stderr,"Error: You must specify a cpu limit percentage\n");
+ print_usage(stderr, 1);
exit(1);
}
- float limit=perclimit/100.0;
- if (limit<0 || limit >1) {
- fprintf(stderr,"Error: limit must be in the range 0-100\n");
- print_usage (stderr, 1);
+ float 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);
+ print_usage(stderr, 1);
exit(1);
}
//parameters are all ok!
- signal(SIGINT,quit);
- signal(SIGTERM,quit);
-
- //time quantum in microseconds. it's splitted in a working period and a sleeping one
- int period=100000;
- struct timespec twork,tsleep; //working and sleeping intervals
- memset(&twork,0,sizeof(struct timespec));
- memset(&tsleep,0,sizeof(struct timespec));
-
-wait_for_process:
-
- //look for the target process..or wait for it
- if (exe!=NULL)
- pid=getpidof(exe);
- else if (path!=NULL)
- pid=getpidof(path);
+ signal(SIGINT, quit);
+ signal(SIGTERM, quit);
+
+ //try to renice with the best value
+ int old_priority = getpriority(PRIO_PROCESS, 0);
+ int priority = old_priority;
+ while (setpriority(PRIO_PROCESS, 0, priority-1) == 0 && priority>MAX_PRIORITY) {
+ priority--;
+ }
+ if (priority != old_priority) {
+ printf("Priority changed to %d\n", priority);
+ }
else {
- waitforpid(pid);
+ printf("Cannot change priority\n");
}
- //process detected...let's play
-
- //init compute_cpu_usage internal stuff
- compute_cpu_usage(0,0,NULL);
- //main loop counter
- int i=0;
-
- struct timespec startwork,endwork;
- long workingtime=0; //last working time in microseconds
-
- if (verbose) print_caption();
-
- float pcpu_avg=0;
-
- //here we should already have high priority, for time precision
+
while(1) {
-
- //estimate how much the controlled process is using the cpu in its working interval
- struct cpu_usage cu;
- if (compute_cpu_usage(pid,workingtime,&cu)==-1) {
- fprintf(stderr,"Process %d dead!\n",pid);
- if (lazy) exit(2);
- //wait until our process appears
- goto wait_for_process;
- }
-
- //cpu actual usage of process (range 0-1)
- float pcpu=cu.pcpu;
- //rate at which we are keeping active the process (range 0-1)
- float workingrate=cu.workingrate;
-
- //adjust work and sleep time slices
- if (pcpu>0) {
- twork.tv_nsec=min(period*limit*1000/pcpu*workingrate,period*1000);
- }
- else if (pcpu==0) {
- twork.tv_nsec=period*1000;
- }
- else if (pcpu==-1) {
- //not yet a valid idea of cpu usage
- pcpu=limit;
- workingrate=limit;
- twork.tv_nsec=min(period*limit*1000,period*1000);
- }
- tsleep.tv_nsec=period*1000-twork.tv_nsec;
-
- //update average usage
- pcpu_avg=(pcpu_avg*i+pcpu)/(i+1);
-
- 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);
+ //look for the target process..or wait for it
+ int ret = 0;
+ if (pid_ok) {
+ //search by pid
+ ret = look_for_process_by_pid(pid);
+ if (ret == 0) {
+ printf("No process found\n");
+ }
+ else if (ret < 0) {
+ printf("Process found but you aren't allowed to control it\n");
+ }
}
-
- if (limit<1 && limit>0) {
- //resume process
- if (kill(pid,SIGCONT)!=0) {
- fprintf(stderr,"Process %d dead!\n",pid);
- if (lazy) exit(2);
- //wait until our process appears
- goto wait_for_process;
+ else {
+ //search by file or path name
+ ret = look_for_process_by_name(exe);
+ if (ret == 0) {
+ printf("No process found\n");
+ }
+ else if (ret < 0) {
+ printf("Process found but you aren't allowed to control it\n");
+ }
+ else {
+ pid = ret;
}
}
-
- clock_gettime(CLOCK_REALTIME,&startwork);
- nanosleep(&twork,NULL); //now process is working
- clock_gettime(CLOCK_REALTIME,&endwork);
- workingtime=timediff(&endwork,&startwork);
-
- if (limit<1) {
- //stop process, it has worked enough
- if (kill(pid,SIGSTOP)!=0) {
- fprintf(stderr,"Process %d dead!\n",pid);
- if (lazy) exit(2);
- //wait until our process appears
- goto wait_for_process;
+ if (ret > 0) {
+ if (ret == cpulimit_pid) {
+ printf("Process %d is cpulimit itself! Aborting to avoid deadlock\n", ret);
+ exit(1);
}
- nanosleep(&tsleep,NULL); //now process is sleeping
+ printf("Process %d found\n", pid);
+ //control
+ limit_process(pid, limit);
}
- i++;
+ if (lazy) {
+ printf("Giving up...\n");
+ break;
+ }
+ sleep(2);
}
-
+
+ return 0;
}
+