2 * This program is licensed under the GNU General Public License,
3 * version 2. A copy of the license can be found in the accompanying
6 **********************************************************************
8 * Simple program to limit the cpu usage of a process
9 * If you modify this code, send me a copy please
11 * Author: Angelo Marletta
15 * Modifications and updates by: Jesse Smith
19 * Version 1.2 and newer
29 #include <sys/types.h>
32 #include <sys/types.h>
34 #include <sys/resource.h>
39 #include <limits.h> // for compatibility
42 #include <mach/clock.h>
43 #include <mach/mach.h>
51 #include <sys/param.h>
52 #include <sys/sysctl.h>
57 //kernel time resolution (inverse of one jiffy interval) in Hertz
58 //i don't know how to detect it, then define to the default (not very clean!)
62 #define min(a,b) (a<b?a:b)
63 #define max(a,b) (a>b?a:b)
65 // For platforms without PATH_MAX
70 #define BEST_PRIORITY -10
83 //pid of the controlled process
85 pid_t my_pid; // this process's PID
87 //executable file name
93 // is higher priority nice possible?
96 // number of CPUs we detected
100 // void *memrchr(const void *s, int c, size_t n);
104 //return ta-tb in microseconds (no overflow checks!)
105 inline long timediff(const struct timespec *ta,const struct timespec *tb) {
106 unsigned long us = (ta->tv_sec-tb->tv_sec)*1000000 + (ta->tv_nsec/1000 - tb->tv_nsec/1000);
112 int Check_Us(pid_t target_pid)
117 if (this_pid == target_pid)
119 printf("We cannot throttle ourselves.\n");
126 int waitforpid(int pid) {
127 //switch to low priority
128 // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
130 if ( (nice_lim < INT_MAX) &&
131 (setpriority(PRIO_PROCESS, my_pid, 19) != 0) ) {
132 printf("Warning: cannot renice\n");
142 //open a directory stream to /proc directory
143 if ((dip = opendir("/proc")) == NULL) {
148 //read in from /proc and seek for process dirs
149 while ((dit = readdir(dip)) != NULL) {
151 if (pid==atoi(dit->d_name)) {
154 if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) {
156 if (closedir(dip) == -1) {
163 fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid);
168 //close the dir stream and check for errors
169 if (closedir(dip) == -1) {
174 //no suitable target found
177 fprintf(stderr,"No process found\n");
181 printf("Warning: no target process found. Waiting for it...\n");
190 printf("Process %d detected\n",pid);
191 //now set high priority, if possible
192 // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
194 if ( (nice_lim < INT_MAX) &&
195 (setpriority(PRIO_PROCESS, my_pid, nice_lim) != 0) ) {
196 printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
203 //this function periodically scans process list and looks for executable path names
204 //it should be executed in a low priority context, since precise timing does not matter
205 //if a process is found then its pid is returned
206 //process: the name of the wanted process, can be an absolute path name to the executable file
207 // or simply its name
208 //return: pid of the found process
209 int getpidof(const char *process) {
212 // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
214 if ( (nice_lim < INT_MAX) &&
215 (setpriority(PRIO_PROCESS, my_pid, 19) != 0) ) {
216 printf("Warning: cannot renice\n");
220 char exepath[PATH_MAX+1];
229 //open a directory stream to /proc directory
230 if ((dip = opendir("/proc")) == NULL) {
235 //read in from /proc and seek for process dirs
236 while ((dit = readdir(dip)) != NULL) {
238 pid=atoi(dit->d_name);
240 sprintf(exelink,"/proc/%d/exe",pid);
241 int size=readlink(exelink,exepath,sizeof(exepath));
244 if (process[0]=='/' && strncmp(exepath,process,size)==0 && size==strlen(process)) {
245 //process starts with / then it's an absolute path
249 //process is the name of the executable file
250 if (strncmp(exepath+size-strlen(process),process,strlen(process))==0) {
256 if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) {
258 if (closedir(dip) == -1) {
265 fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid);
272 //close the dir stream and check for errors
273 if (closedir(dip) == -1) {
278 //no suitable target found
281 fprintf(stderr,"No process found\n");
285 printf("Warning: no target process found. Waiting for it...\n");
294 printf("Process %d detected\n",pid);
295 //now set high priority, if possible
296 // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
298 if ( (nice_lim < INT_MAX) &&
299 (setpriority(PRIO_PROCESS, my_pid, nice_lim) != 0) ) {
300 printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
307 //SIGINT and SIGTERM signal handler
309 //let the process continue if it's stopped
311 printf("Exiting...\n");
317 //get jiffies count from /proc filesystem
318 int getjiffies(int pid)
320 kvm_t *my_kernel = NULL;
321 struct kinfo_proc *process_data = NULL;
325 my_kernel = kvm_open(0, 0, 0, O_RDONLY, "kvm_open");
328 printf("Error opening kernel vm. You should be running as root.\n");
332 process_data = kvm_getprocs(my_kernel, KERN_PROC_PID, pid, &processes);
333 if ( (process_data) && (processes >= 1) )
334 my_jiffies = process_data->ki_runtime;
336 kvm_close(my_kernel);
345 int getjiffies(int pid) {
346 static char stat[20];
347 static char buffer[1024];
349 sprintf(stat,"/proc/%d/stat",pid);
350 FILE *f=fopen(stat,"r");
351 if (f==NULL) return -1;
352 p = fgets(buffer,sizeof(buffer),f);
357 p=memchr(p+1,')',sizeof(buffer)-(p-buffer));
360 p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
363 p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
364 //kernel mode jiffies
368 // could not read info
374 //process instant photo
375 struct process_screenshot {
376 struct timespec when; //timestamp
377 int jiffies; //jiffies count of the process
378 int cputime; //microseconds of work from previous screenshot to current
381 //extracted process statistics
387 //this function is an autonomous dynamic system
388 //it works with static variables (state variables of the system), that keep memory of recent past
389 //its aim is to estimate the cpu usage of the process
390 //to work properly it should be called in a fixed periodic way
391 //perhaps i will put it in a separate thread...
392 int compute_cpu_usage(int pid,int last_working_quantum,struct cpu_usage *pusage) {
394 //circular buffer containing last MEM_ORDER process screenshots
395 static struct process_screenshot ps[MEM_ORDER];
396 //the last screenshot recorded in the buffer
398 //the oldest screenshot recorded in the buffer
402 //reinit static variables
408 //let's advance front index and save the screenshot
409 front=(front+1)%MEM_ORDER;
410 int j=getjiffies(pid);
411 if (j>=0) ps[front].jiffies=j;
412 else return -1; //error: pid does not exist
415 // OS X does not have clock_gettime, use clock_get_time
418 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
419 clock_get_time(cclock, &mts);
420 mach_port_deallocate(mach_task_self(), cclock);
421 ps[front].when.tv_sec = mts.tv_sec;
422 ps[front].when.tv_nsec = mts.tv_nsec;
425 // Linux and BSD can use real time
426 clock_gettime(CLOCK_REALTIME,&(ps[front].when));
427 ps[front].cputime=last_working_quantum;
429 //buffer actual size is: (front-tail+MEM_ORDER)%MEM_ORDER+1
430 int size=(front-tail+MEM_ORDER)%MEM_ORDER+1;
433 //not enough samples taken (it's the first one!), return -1
435 pusage->workingrate=1;
439 //now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds
440 long dt=timediff(&(ps[front].when),&(ps[tail].when));
442 int i=(tail+1)%MEM_ORDER;
443 int max=(front+1)%MEM_ORDER;
445 dtwork+=ps[i].cputime;
448 int used=ps[front].jiffies-ps[tail].jiffies;
449 float usage=(used*1000000.0/HZ)/dtwork;
450 pusage->workingrate=1.0*dtwork/dt;
451 pusage->pcpu=usage*pusage->workingrate;
453 tail=(tail+1)%MEM_ORDER;
459 void print_caption() {
460 printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n");
464 void increase_priority()
466 //find the best available nice value
467 int old_priority = getpriority(PRIO_PROCESS, 0);
468 int priority = old_priority;
469 while ( (setpriority(PRIO_PROCESS, 0, priority-1) == 0) &&
470 (priority > BEST_PRIORITY) )
474 if (priority != old_priority) {
475 if (verbose) printf("Priority changed to %d\n", priority);
478 if (verbose) printf("Warning: Cannot change priority. Run as root or renice for best results.\n");
486 void print_usage(FILE *stream,int exit_code) {
487 fprintf(stream, "CPUlimit version %1.1f\n", VERSION);
488 fprintf(stream, "Usage: %s TARGET [OPTIONS...] [-- PROGRAM]\n",program_name);
489 fprintf(stream, " TARGET must be exactly one of these:\n");
490 fprintf(stream, " -p, --pid=N pid of the process\n");
491 fprintf(stream, " -e, --exe=FILE name of the executable program file\n");
492 fprintf(stream, " The -e option only works when\n");
493 fprintf(stream, " cpulimit is run with admin rights.\n");
494 fprintf(stream, " -P, --path=PATH absolute path name of the\n");
495 fprintf(stream, " executable program file\n");
496 fprintf(stream, " OPTIONS\n");
497 fprintf(stream, " -b --background run in background\n");
498 fprintf(stream, " -c --cpu=N override the detection of CPUs on the machine.\n");
499 fprintf(stream, " -l, --limit=N percentage of cpu allowed from 1 up.\n");
500 fprintf(stream, " Usually 1 - %d00, but can be higher\n", NCPU);
501 fprintf(stream, " on multi-core CPUs (mandatory)\n");
502 fprintf(stream, " -k, --kill kill processes going over their limit\n");
503 fprintf(stream, " instead of just throttling them.\n");
504 fprintf(stream, " -r, --restore Restore processes after they have\n");
505 fprintf(stream, " been killed. Works with the -k flag.\n");
507 fprintf(stream, " -v, --verbose show control statistics\n");
508 fprintf(stream, " -z, --lazy exit if there is no suitable target process,\n");
509 fprintf(stream, " or if it dies\n");
510 fprintf(stream, " -- This is the final CPUlimit option. All following\n");
511 fprintf(stream, " options are for another program we will launch.\n");
512 fprintf(stream, " -h, --help display this help and exit\n");
518 // Get the number of CPU cores on this machine.
522 #ifdef _SC_NPROCESSORS_ONLN
523 ncpu = sysconf(_SC_NPROCESSORS_ONLN);
531 int main(int argc, char **argv) {
534 // char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0]));
535 // program_name = p==NULL?argv[0]:(p+1);
536 program_name = argv[0];
537 int run_in_background = FALSE;
540 /* A string listing valid short options letters. */
541 const char* short_options="p:e:P:l:c:bkrvzh";
542 /* An array describing valid long options. */
543 const struct option long_options[] = {
544 { "pid", required_argument, NULL, 'p' },
545 { "exe", required_argument, NULL, 'e' },
546 { "path", required_argument, NULL, 'P' },
547 { "limit", required_argument, NULL, 'l' },
548 { "background", no_argument, NULL, 'b' },
549 { "verbose", no_argument, NULL, 'v' },
550 { "lazy", no_argument, NULL, 'z' },
551 { "help", no_argument, NULL, 'h' },
552 { "cpu", required_argument, NULL, 'c'},
556 const char *exe=NULL;
557 const char *path=NULL;
560 int process_ok = FALSE;
561 int limit_ok = FALSE;
562 int last_known_argument = 0;
563 int kill_process = FALSE; // kill process instead of stopping it
564 int restore_process = FALSE; // restore killed process
565 // struct rlimit maxlimit;
569 opterr = 0; // avoid unwanted error messages for unknown parameters
571 next_option = getopt_long (argc, argv, short_options,long_options, NULL);
572 switch(next_option) {
574 run_in_background = TRUE;
575 last_known_argument++;
579 if (pid) // valid PID
584 last_known_argument += 2;
589 last_known_argument += 2;
594 last_known_argument += 2;
597 perclimit=atoi(optarg);
599 last_known_argument += 2;
603 last_known_argument += 2;
607 last_known_argument++;
610 restore_process = TRUE;
611 last_known_argument++;
616 last_known_argument++;
620 last_known_argument++;
623 print_usage (stdout, 1);
624 last_known_argument++;
627 last_known_argument++;
631 print_usage (stderr, 1);
632 last_known_argument++;
639 } while(next_option != -1);
642 // try to launch a program passed on the command line
643 // But only if we do not already have a PID to watch
644 if ( (last_known_argument + 1 < argc) && (pid_ok == FALSE) )
646 last_known_argument++;
647 // if we stopped on "--" jump to the next parameter
648 if ( (last_known_argument + 1 < argc) && (! strcmp(argv[last_known_argument], "--") ) )
649 last_known_argument++;
651 // try to launch remaining arguments
654 int index = last_known_argument;
655 printf("Launching %s", argv[index]);
656 for (index = last_known_argument + 1; index < argc; index++)
657 printf(" %s", argv[index]);
658 printf(" with limit %d\n", perclimit);
661 if (forked_pid == -1) // error
663 printf("Failed to launch specified process.\n");
666 else if (forked_pid == 0) // target child
668 execvp(argv[last_known_argument],
669 &(argv[last_known_argument]) );
672 else // parent who will now fork the throttler
675 // if we are planning to kill a process, give it
676 // a running head start to avoid death at start-up
681 if (limit_pid == 0) // child
683 pid = forked_pid; // the first child
687 printf("Throttling process %d\n", (int) pid);
694 else if (forked_pid == 0) // child
700 printf("Throttling process %d\n", (int) pid);
704 execvp(argv[last_known_argument],
705 &(argv[last_known_argument]));
707 // we should never return
712 } // end of launching child process
714 if (!process_ok && !pid_ok) {
715 fprintf(stderr,"Error: You must specify a target process\n");
716 print_usage (stderr, 1);
719 if ((exe!=NULL && path!=NULL) || (pid_ok && (exe!=NULL || path!=NULL))) {
720 fprintf(stderr,"Error: You must specify exactly one target process\n");
721 print_usage (stderr, 1);
725 fprintf(stderr,"Error: You must specify a cpu limit\n");
726 print_usage (stderr, 1);
729 float limit=perclimit/100.0;
730 if ( (limit <= 0.00) || (limit > NCPU) )
732 fprintf(stderr,"Error: limit must be in the range of 1 to %d00\n", NCPU);
733 print_usage (stderr, 1);
737 // check to see if we should fork
738 if (run_in_background)
753 //parameters are all ok!
755 signal(SIGTERM,quit);
759 printf("%d CPUs detected.\n", NCPU);
763 Instead of all this big block of code to detect and change
764 priority settings, let us just use the increase_priority()
765 function. It is a little more simple and takes a more
766 gradual approach, rather than "all or nothing".
769 if (setpriority(PRIO_PROCESS, my_pid,-20)!=0) {
770 //if that failed, check if we have a limit
771 // by how much we can raise the priority
773 //check if non-root can even make changes
774 // (ifdef because it's only available in linux >= 2.6.13)
775 nice_lim=getpriority(PRIO_PROCESS, my_pid);
776 getrlimit(RLIMIT_NICE, &maxlimit);
778 //if we can do better then current
779 if( (20 - (signed)maxlimit.rlim_cur) < nice_lim &&
780 setpriority(PRIO_PROCESS, my_pid,
781 20 - (signed)maxlimit.rlim_cur)==0 //and it actually works
784 //if we can do better, but not by much, warn about it
785 if( (nice_lim - (20 - (signed)maxlimit.rlim_cur)) < 9)
787 printf("Warning, can only increase priority by %d.\n", nice_lim - (20 - (signed)maxlimit.rlim_cur));
790 nice_lim = 20 - (signed)maxlimit.rlim_cur;
793 // otherwise don't try to change priority.
794 // The below will also run if it's not possible
795 // for non-root to change priority
798 printf("Warning: cannot renice.\nTo work better you should run this program as root, or adjust RLIMIT_NICE.\nFor example in /etc/security/limits.conf add a line with: * - nice -10\n\n");
807 //time quantum in microseconds. it's splitted in a working period and a sleeping one
809 struct timespec twork,tsleep; //working and sleeping intervals
810 memset(&twork,0,sizeof(struct timespec));
811 memset(&tsleep,0,sizeof(struct timespec));
815 //look for the target process..or wait for it
818 else if (path != NULL)
824 //process detected...let's play
826 //init compute_cpu_usage internal stuff
827 compute_cpu_usage(0,0,NULL);
831 struct timespec startwork,endwork;
832 long workingtime=0; //last working time in microseconds
834 if (verbose) print_caption();
838 //here we should already have high priority, for time precision
841 //estimate how much the controlled process is using the cpu in its working interval
843 if (compute_cpu_usage(pid,workingtime,&cu)==-1) {
844 fprintf(stderr,"Process %d dead!\n",pid);
846 //wait until our process appears
847 goto wait_for_process;
850 //cpu actual usage of process (range 0-1)
852 //rate at which we are keeping active the process (range 0-1)
853 float workingrate=cu.workingrate;
855 //adjust work and sleep time slices
857 twork.tv_nsec=min(period*limit*1000/pcpu*workingrate,period*1000);
860 twork.tv_nsec=period*1000;
863 //not yet a valid idea of cpu usage
866 twork.tv_nsec=min(period*limit*1000,period*1000);
868 tsleep.tv_nsec=period*1000-twork.tv_nsec;
870 //update average usage
871 pcpu_avg=(pcpu_avg*i+pcpu)/(i+1);
873 if (verbose && i%10==0 && i>0) {
874 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);
879 // if (limit<1 && limit>0) {
880 // printf("Comparing %f to %f\n", pcpu, limit);
883 // printf("Continue\n");
885 if (kill(pid,SIGCONT)!=0) {
886 fprintf(stderr,"Process %d dead!\n",pid);
888 //wait until our process appears
889 goto wait_for_process;
894 // OS X does not have clock_gettime, use clock_get_time
897 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
898 clock_get_time(cclock, &mts);
899 mach_port_deallocate(mach_task_self(), cclock);
900 startwork.tv_sec = mts.tv_sec;
901 startwork.tv_nsec = mts.tv_nsec;
904 clock_gettime(CLOCK_REALTIME,&startwork);
907 nanosleep(&twork,NULL); //now process is working
909 // OS X does not have clock_gettime, use clock_get_time
910 // clock_serv_t cclock;
911 // mach_timespec_t mts;
912 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
913 clock_get_time(cclock, &mts);
914 mach_port_deallocate(mach_task_self(), cclock);
915 endwork.tv_sec = mts.tv_sec;
916 endwork.tv_nsec = mts.tv_nsec;
919 clock_gettime(CLOCK_REALTIME,&endwork);
921 workingtime=timediff(&endwork,&startwork);
924 // printf("Checking %f vs %f\n", pcpu, limit);
927 // When over our limit we may run into
928 // situations where we want to kill
929 // the offending process, then restart it
933 fprintf(stderr, "Process %d killed.\n", pid);
934 if ( (lazy) && (! restore_process) )
936 // restart killed process
940 new_process = fork();
941 if (new_process == -1)
943 fprintf(stderr, "Failed to restore killed process.\n");
945 else if (new_process == 0)
947 // child which becomes new process
949 printf("Relaunching %s\n",
950 argv[last_known_argument]);
951 execvp(argv[last_known_argument],
952 &(argv[last_known_argument]) );
956 // we need to track new process
958 // avoid killing child process
963 // do not kll process, just throttle it
968 //stop process, it has worked enough
969 if (kill(pid,SIGSTOP)!=0) {
970 fprintf(stderr,"Process %d dead!\n", pid);
972 //wait until our process appears
973 goto wait_for_process;
975 nanosleep(&tsleep,NULL); //now process is sleeping
976 } // end of throttle process
977 } // end of process using too much CPU