cpulimit-3.0/0000755000175000017500000000000014412557013012210 5ustar jessejessecpulimit-3.0/cpulimit.10000664000175000017500000001140514412556714014132 0ustar jessejesse.TH CPULIMIT "1" "June 2012" "cpulimit" "User commands" .SH NAME cpulimit -- limits the CPU usage of a process .SH SYNOPSIS .B cpulimit \fI[TARGET\fR] \fR[\fIOPTIONS\fR...] \fR[ -- \fIPROGRAM\fR] .SH DESCRIPTION .P \fITARGET \fRmust be exactly one of these: .TP \fB\-p\fR, \fB\-\-pid\fR=\fIN\fR pid of the process .TP \fB\-e\fR, \fB\-\-exe\fR=\fIFILE\fR name of the executable program file .TP \fB\-P\fR, \fB\-\-path\fR=\fIPATH\fR absolute path name of the executable program file .P \fIOPTIONS\fR .TP \fB\-b\fR, \fB\-\-background\fR run cpulimit in the background, freeing up the terminal .TP \fB\-f\fR, \fB\-\-foreground\fR run cpulimit in foreground while waiting for launched process to finish .TP \fB\-c\fR, \fB\-\-cpu\fR specify the number of CPU cores available. Usually this is detected for us. .TP \fB\-l\fR, \fB\-\-limit\fR=\fIN\fR percentage of CPU allowed from 1 up. Usually 1 - 100, but can be higher on multi-core CPUs. (mandatory) .TP \fB\-q\fR, \fB\-\-quiet\fR Runs in quiet mode, avoids writing update messages to console. .TP \fB\-k\fR, \fB\-\-kill\fR kill target process instead of throttling its CPU usage .TP \fB\-m\fR, \fB\-\-monitor\-forks\fR watch and throttle child processes of the target process Warning: It is usually a bad idea to use this flag, especially on a shell script. The commands in the script will each spawn a process which will, in turn, spawn more copies of this program to throttle them, bogging down the system. Also, it is possible for a child process to die and for its PID to be assigned to another program. When this happens quickly it can cause cpulimit to target the new, unintended process before the old information has had a chance to be flushed out. Only use the monitor-forks option in specific cases, ideally on machines without a lot of new processes being spawned. .TP \fB\-r\fR, \fB\-\-restore\fR restore a process killed using the \-k flag. .TP \fB\-s\fR, \fB\-\-signal\fR send an alternative signal to the watched process when we exit. Default is SIGCONT. .TP \fB\-v\fR, \fB\-\-verbose\fR show control statistics .TP \fB\-z\fR, \fB\-\-lazy\fR exit if there is no suitable target process, or if it dies .TP \fB\--\fR This is the final CPUlimit option. All following options are for another program we will launch. .TP \fB\-h\fR, \fB\-\-help\fR display this help and exit .SH EXAMPLES Assuming you have started \fB`foo \-\-bar`\fR and you find out with \fItop\fR(1) or \fIps\fR(1) that this process uses all your CPU time you can either .TP \[sh] \fBcpulimit \-e foo \-l 50\fR limits the CPU usage of the process by acting on the executable program file (note: the argument "\-\-bar" is omitted) .TP \[sh] \fBcpulimit \-p 1234 \-l 50\fR limits the CPU usage of the process by acting on its PID, as shown by \fIps\fR(1) .TP \[sh] \fBcpulimit \-P /usr/bin/foo \-l 50\fR same as \fI\-e\fR but uses the absolute path name .TP \[sh] \fB/usr/bin/someapp \& .TP \[sh] \fBcpulimit \-p $! \-l 25 \-b\fR Useful for scripts where you want to throttle the last command run. .TP \[sh] \fBcpulimit \-l 20 firefox\fR Launch Firefox web browser and limit its CPU usage to 20% .TP \[sh] \fBcpulimit \-l 25 \-\- firefox \-private\fR Launch Firefox web browser in private mode and limit its CPU usage to 25% .TP \[sh] \fBcpulimit \-c 2 \-p 12345 \-l 25\fR The \fB\-c\fR flag sets the number of CPU cores the program thinks are available. Usually this is detected for us, but can be over-ridden. .TP \[sh] \fBcpulimit \-l 20 \-k firefox\fr Launch the Firefox program and kill it if the process goes over 20% CPU usage. .TP \[sh] \fBcpulimit \-l 20 \-p 1234 \-s SIGTERM Throttle process 1234 at 20% CPU usage. If cpulimit is forced to exit, it sends the watched process the SIGTERM signal. .SH NOTES .IP \(bu 4 cpulimit always sends the SIGSTOP and SIGCONT signals to a process, both to verify that it can control it and to limit the average amount of CPU it consumes. This can result in misleading (annoying) job control messages that indicate that the job has been stopped (when actually it was, but immediately restarted). This can also cause issues with interactive shells that detect or otherwise depend on SIGSTOP/SIGCONT. For example, you may place a job in the foreground, only to see it immediately stopped and restarted in the background. (See also .) .IP \(bu 4 When invoked with the \fI\-e\fR or \fI\-P\fR options, cpulimit looks for any process under /proc with a name that matches the process name argument given. Furthermore, it uses the first instance of the process found. To control a specific instance of a process, use the \fI\-p\fR option and provide a PID. .IP \(bu 4 The current version of cpulimit assumes the kernel HZ value 100. .SH AUTHOR This manpage was written for the Debian project by gregor herrmann but may be used by others. cpulimit-3.0/LICENSE0000664000175000017500000004325412320362714013225 0ustar jessejesse GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. cpulimit-3.0/cpulimit.c0000644000175000017500000012025114412555160014204 0ustar jessejesse/** * This program is licensed under the GNU General Public License, * version 2. A copy of the license can be found in the accompanying * LICENSE file. * ********************************************************************** * * 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 * * Modifications and updates by: Jesse Smith * Date: May 4, 2011 * Version 1.2 * Date: Jan 29, 2013 * Version 1.2 and newer * * Modifications and updates by: Hasnain Lakhani * Date: Mar 26, 2014 * Version 2.1 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for compatibility #ifdef __APPLE__ #include #include #endif #ifdef FREEBSD #include #include #include #include #include #include #endif #ifdef LINUX #include #define PROC_FILENAME 512 #define LINE_LENGTH 256 #endif //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 //some useful macro #define min(a,b) (ab?a:b) // For platforms without PATH_MAX #ifndef PATH_MAX #define PATH_MAX 4096 #endif #ifndef EXEC_PATH #define EXEC_PATH 32 #endif #define BEST_PRIORITY -10 #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef VERSION #define VERSION 0.0 #endif //pid of the controlled process pid_t pid = 0; pid_t my_pid; // this process's PID //executable file name char *program_name; //verbose mode int verbose = FALSE; //lazy mode int lazy = FALSE; // is higher priority nice possible? int nice_lim; // number of CPUs we detected int NCPU; // quiet mode int quiet = FALSE; // What signal should we send to the watched process // when cpulimit exits? int send_signal = SIGCONT; //reverse byte search // void *memrchr(const void *s, int c, size_t n); #define MAX_SIGNAL 7 const char *SIGNAL_NAME[MAX_SIGNAL] = { "SIGHUP", "SIGINT", "SIGQUIT", "SIGKILL", "SIGTERM", "SIGSTOP", "SIGCONT" }; const int SIGNAL_VALUE[MAX_SIGNAL] = { SIGHUP, SIGINT, SIGQUIT, SIGKILL, SIGTERM, SIGSTOP, SIGCONT }; //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; } #ifdef LINUX #include typedef struct { pid_t child; // the child of our target process pid_t monitor; // the LimitCPU fork monitoring the child void *next; } CHILD; int quitting = FALSE; // Have we receive a quit signal int monitor_children = FALSE; // are we monitoring children of target // Data passed to the monitor thread typedef struct { int limit; // per cent limit to place on a process char *this_program; // copy of argv[0] } PROGRAM_DATA; #endif int Check_Us(pid_t target_pid) { pid_t this_pid; this_pid = getpid(); if (this_pid == target_pid) { fprintf(stderr, "We cannot throttle ourselves.\n"); exit(7); } return TRUE; } int waitforpid(int pid) { //switch to low priority // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) { /* if ( (nice_lim < INT_MAX) && (setpriority(PRIO_PROCESS, my_pid, 19) != 0) ) { printf("Warning: cannot renice\n"); } */ 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; } //read in from /proc and seek for process dirs while ((dit = readdir(dip)) != NULL) { //get pid if (pid==atoi(dit->d_name)) { //pid detected Check_Us(pid); if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) { //process is ok! if (closedir(dip) == -1) { perror("closedir"); return -1; } goto done; } else { fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid); } } } //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 { fprintf(stderr, "Warning: no target process found. Waiting for it...\n"); } } //sleep for a while sleep(2); } done: if (!quiet) printf("Process %d detected\n",pid); //now set high priority, if possible // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) { /* if ( (nice_lim < INT_MAX) && (setpriority(PRIO_PROCESS, my_pid, nice_lim) != 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) { /* if ( (nice_lim < INT_MAX) && (setpriority(PRIO_PROCESS, my_pid, 19) != 0) ) { printf("Warning: cannot renice\n"); } */ char exelink[EXEC_PATH + 1]; 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; } //read in from /proc and seek for process dirs while ((dit = readdir(dip)) != NULL) { //get pid pid=atoi(dit->d_name); if (pid>0) { snprintf(exelink, EXEC_PATH, "/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) { Check_Us(pid); if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) { //process is ok! if (closedir(dip) == -1) { perror("closedir"); return -1; } goto done; } else { fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid); } } } } } //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 { fprintf(stderr, "Warning: no target process found. Waiting for it...\n"); } } //sleep for a while sleep(2); } done: if (!quiet) printf("Process %d detected\n",pid); //now set high priority, if possible // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) { /* if ( (nice_lim < INT_MAX) && (setpriority(PRIO_PROCESS, my_pid, nice_lim) != 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 we are stopped kill(pid, send_signal); #ifdef LINUX if (monitor_children) { quitting = TRUE; printf("Asking children to quit...\n"); sleep(2); // wait for thread clean-up } #endif printf("Exiting...\n"); exit(0); } // Handle a child process quitting void Child_Done(int sig) { pid_t caught_child; caught_child = waitpid(-1, NULL, WNOHANG); if (verbose) { printf("Caught child process: %d\n", (int) caught_child); printf("%d\n", errno); } // If this was the one process we were watching, we can quit now. if (caught_child == pid) { if (verbose) printf("Child process is finished, exiting...\n"); exit(0); } } #ifdef FREEBSD long getjiffies(int pid) { kvm_t *my_kernel = NULL; struct kinfo_proc *process_data = NULL; int processes; long my_jiffies = -1; my_kernel = kvm_open(0, 0, 0, O_RDONLY, "kvm_open"); if (! my_kernel) { fprintf(stderr, "Error opening kernel vm. You should be running as root.\n"); return -1; } process_data = kvm_getprocs(my_kernel, KERN_PROC_PID, pid, &processes); if ( (process_data) && (processes >= 1) ) my_jiffies = process_data->ki_runtime; kvm_close(my_kernel); if (my_jiffies >= 0) my_jiffies /= 1000; return my_jiffies; } #endif #ifdef LINUX //get jiffies count from /proc filesystem long getjiffies(int pid) { static char stat[20]; static char buffer[1024]; char *p; sprintf(stat,"/proc/%d/stat",pid); FILE *f=fopen(stat,"r"); if (f==NULL) return -1; p = fgets(buffer,sizeof(buffer),f); fclose(f); // char *p=buffer; if (p) { p=memchr(p+1,')',sizeof(buffer)-(p-buffer)); int sp=12; while (sp--) p=memchr(p+1,' ',sizeof(buffer)-(p-buffer)); //user mode jiffies long utime=atol(p+1); p=memchr(p+1,' ',sizeof(buffer)-(p-buffer)); //kernel mode jiffies long ktime=atol(p+1); return utime+ktime; } // could not read info return -1; } #endif //process instant photo struct process_screenshot { struct timespec when; //timestamp long 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; long j=getjiffies(pid); if (j>=0) ps[front].jiffies=j; else return -1; //error: pid does not exist #ifdef __APPLE__ // OS X does not have clock_gettime, use clock_get_time clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); ps[front].when.tv_sec = mts.tv_sec; ps[front].when.tv_nsec = mts.tv_nsec; #else // Linux and BSD can use real time clock_gettime(CLOCK_REALTIME,&(ps[front].when)); ps[front].cputime=last_working_quantum; #endif //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); long 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; } #undef MEM_ORDER } void print_caption() { printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n"); } void increase_priority() { //find the best available nice value int old_priority = getpriority(PRIO_PROCESS, 0); int priority = old_priority; while ( (setpriority(PRIO_PROCESS, 0, priority-1) == 0) && (priority > BEST_PRIORITY) ) { priority--; } if (priority != old_priority) { if (verbose) printf("Priority changed to %d\n", priority); } else { if (verbose) printf("Warning: Cannot change priority. Run as root or renice for best results.\n"); } } #ifdef LINUX // This following functions are for detecting and limiting child // processes on Linux. // This function adds a new child process to our list of child processes. CHILD *Add_Child(CHILD *all_children, pid_t new_pid) { CHILD *new_child = (CHILD *) calloc(sizeof(CHILD), 1); CHILD *current; if (! new_child) return all_children; new_child->next = NULL; new_child->child = new_pid; new_child->monitor = 0; if (all_children) { current = all_children; while (current->next) current = current->next; current->next = new_child; return all_children; } else // this is the first node return new_child; } // This function removes a child PID node. CHILD *Remove_Child(CHILD *all_children, pid_t old_pid) { CHILD *current, *previous = NULL; int found = FALSE; current = all_children; while ( (! found) && (current) ) { if (current->child == old_pid) { if (previous) previous->next = current->next; else all_children = current->next; free(current); found = TRUE; } else { previous = current; current = current->next; } } return all_children; } // This function cleans up all remaining child nodes. void Clean_Up_Children(CHILD *all_children) { CHILD *current, *next; current = all_children; while (current) { next = current->next; free(current); current = next; } } // This function searches the linked list for a matching PID. // It returns NULL if no match is found and a pointer to the // node is a match is located. CHILD *Find_Child(CHILD *children, pid_t target) { CHILD *current; int found = FALSE; current = children; while ( (!found) && (current) ) { if (current->child == target) found = TRUE; else current = current->next; } return current; } // This function returns a list of process IDs of children // of the given process (PID). It does this by searching the /proc // file system and looking in the /proc/pid/status file for the PPid field. // A linked list of child PIDs is returned on success or NULL on failure or // if no child PIDs are found. CHILD *Find_Child_PIDs(CHILD *all_children, pid_t parent_pid) { int found = FALSE; DIR *proc; struct dirent *proc_entry; char filename[PROC_FILENAME + 1]; FILE *status_file; char *reading_file; char line[256]; pid_t new_ppid; int current_pid; proc = opendir("/proc"); if (! proc) return all_children; proc_entry = readdir(proc); while (proc_entry) { snprintf(filename, PROC_FILENAME, "/proc/%s/status", proc_entry->d_name); status_file = fopen(filename, "r"); if (status_file) { found = FALSE; reading_file = fgets(line, LINE_LENGTH, status_file); while ( (! found) && (reading_file) ) { if (! strncmp(line, "PPid:", 5) ) { sscanf(&(line[6]), "%d", &new_ppid); if (new_ppid == parent_pid && current_pid != getpid() ) { sscanf(proc_entry->d_name, "%d", ¤t_pid); if (! Find_Child(all_children, current_pid) ) all_children = Add_Child(all_children, current_pid); } found = TRUE; } else reading_file = fgets(line, LINE_LENGTH, status_file); } // done reading status file fclose(status_file); } proc_entry = readdir(proc); } // done reading proc file system closedir(proc); return all_children; } // This function (which should probably be called as a thread) monitors the // system for child processes of the current target process. When a new // child of the target is located, it is added to the CHILD list. // New children result in a new fork of this program being spawned to // monitor the child process and its children. void *Monitor_Children(void *all_data) { CHILD *all_children = NULL; CHILD *current; PROGRAM_DATA *program_data = (PROGRAM_DATA *) all_data; while (! quitting ) { // Check for new child processes all_children = Find_Child_PIDs(all_children, pid); // Find any children without monitors and create a monitoring process // Clean out old processes while we are looking current = all_children; while (current) { // First see if the child process is still running. If not, // we can remote its node. if (current->child) { char filename[PROC_FILENAME + 1]; DIR *child_directory; snprintf(filename, PROC_FILENAME, "/proc/%d", current->child); child_directory = opendir(filename); if (child_directory) closedir(child_directory); else { if (verbose) printf("Child process %d done, cleaning up.\n", (int) current->child); all_children = Remove_Child(all_children, current->child); } } // end of clean up children processes no longer running // The child process is still running, but it might not have // a monitor. Create a new monitoring process. if ( (current->child) && (! current->monitor) ) { pid_t returned_pid; if (verbose) printf("Creating monitoring process for %d\n", (int) current->child); returned_pid = fork(); if (returned_pid > 0) { // parent current->monitor = returned_pid; } else if (returned_pid == 0) { // child char limit_amount[16]; char process_identifier[16]; snprintf(limit_amount, 16, "%d", (int) program_data->limit); snprintf(process_identifier, 16, "%d", current->child); if (verbose) printf("Starting monitor with: %s -l %s -p %s -z -m\n", program_data->this_program, limit_amount, process_identifier); execlp(program_data->this_program, program_data->this_program, "-l", limit_amount, "-p", process_identifier, "-z", "-m", (char *) NULL); // we should not return, report error and bail out if (verbose) printf("Error trying to execute %s\n", program_data->this_program); exit(1); } } // end of creating a new monitor if (verbose) { printf("Watching child: %d with %d\n", (int) current->child, (int) current->monitor); } current = current->next; } sleep(1); } // end LimitCPU is still running pthread_exit(NULL); } #endif // end of monitoring children processes on Linux void print_usage(FILE *stream,int exit_code) { fprintf(stream, "CPUlimit version %1.1f\n", VERSION); fprintf(stream, "Usage: %s TARGET [OPTIONS...] [-- PROGRAM]\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, " The -e option only works when\n"); fprintf(stream, " cpulimit is run with admin rights.\n"); fprintf(stream, " -P, --path=PATH absolute path name of the\n"); fprintf(stream, " executable program file\n"); fprintf(stream, " OPTIONS\n"); fprintf(stream, " -b --background run in background\n"); fprintf(stream, " -f --foreground launch target process in foreground and wait for it to exit\n"); fprintf(stream, " -c --cpu=N override the detection of CPUs on the machine.\n"); fprintf(stream, " -l, --limit=N percentage of cpu allowed from 1 up.\n"); fprintf(stream, " Usually 1 - %d00, but can be higher\n", NCPU); fprintf(stream, " on multi-core CPUs (mandatory)\n"); #ifdef LINUX fprintf(stream, " -m, --monitor-forks Watch children/forks of the target process\n"); #endif fprintf(stream, " -q, --quiet run in quiet mode (only print errors).\n"); fprintf(stream, " -k, --kill kill processes going over their limit\n"); fprintf(stream, " instead of just throttling them.\n"); fprintf(stream, " -r, --restore Restore processes after they have\n"); fprintf(stream, " been killed. Works with the -k flag.\n"); fprintf(stream, " -s, --signal=SIG Send this signal to the watched process when cpulimit exits.\n"); fprintf(stream, " Signal should be specificed as a number or \n"); fprintf(stream, " SIGTERM, SIGCONT, SIGSTOP, etc. SIGCONT is the default.\n"); fprintf(stream, " -v, --verbose show control statistics\n"); fprintf(stream, " -z, --lazy exit if there is no suitable target process,\n"); fprintf(stream, " or if it dies\n"); fprintf(stream, " -- This is the final CPUlimit option. All following\n"); fprintf(stream, " options are for another program we will launch.\n"); fprintf(stream, " -h, --help display this help and exit\n"); exit(exit_code); } // Get the number of CPU cores on this machine. int get_ncpu() { int ncpu = 1; #ifdef _SC_NPROCESSORS_ONLN ncpu = sysconf(_SC_NPROCESSORS_ONLN); #endif return ncpu; } // This function attempts to figure out what signal we should send // target processes based on a command line paramter. First we check // for text such as SIGINT, SIGCONT, SIGSTOP, etc. If no match is found // then we assume the value given is a number and use that. int Translate_Signal(char *my_signal) { int signal_value; int index = 0, found = FALSE; // first check to see if we were passed a string while ( (index < MAX_SIGNAL) && (! found) ) { if (! strcmp(my_signal, SIGNAL_NAME[index]) ) { found = TRUE; signal_value = SIGNAL_VALUE[index]; } else index++; } // no value found, try a number if (! found) signal_value = atoi(my_signal); return signal_value; } 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); program_name = argv[0]; int run_in_background = FALSE; //parse arguments int next_option; /* A string listing valid short options letters. */ #ifdef LINUX const char* short_options="p:e:P:l:c:s:bfqkmrvzh"; PROGRAM_DATA program_data; #else const char* short_options="p:e:P:l:c:s:bfqkrvzh"; #endif /* An array describing valid long options. */ const struct option long_options[] = { { "pid", required_argument, NULL, 'p' }, { "exe", required_argument, NULL, 'e' }, { "path", required_argument, NULL, 'P' }, { "limit", required_argument, NULL, 'l' }, { "background", no_argument, NULL, 'b' }, { "foreground", no_argument, NULL, 'f' }, { "quiet", no_argument, NULL, 'q' }, { "verbose", no_argument, NULL, 'v' }, { "lazy", no_argument, NULL, 'z' }, { "help", no_argument, NULL, 'h' }, { "cpu", required_argument, NULL, 'c'}, { "signal", required_argument, NULL, 's'}, #ifdef LINUX { "monitor-forks", no_argument, NULL, 'm'}, #endif { NULL, 0, NULL, 0 } }; //argument variables const char *exe=NULL; const char *path=NULL; int perclimit=0; int pid_ok = FALSE; int process_ok = FALSE; int limit_ok = FALSE; int last_known_argument = 0; int kill_process = FALSE; // kill process instead of stopping it int restore_process = FALSE; // restore killed process int run_child_in_background = TRUE; // run cpulimit in background when // we launch new process // struct rlimit maxlimit; NCPU = get_ncpu(); opterr = 0; // avoid unwanted error messages for unknown parameters do { next_option = getopt_long (argc, argv, short_options,long_options, NULL); switch(next_option) { case 'b': run_in_background = TRUE; last_known_argument++; break; case 'f': run_child_in_background = FALSE; run_in_background = FALSE; last_known_argument++; break; case 'p': pid=atoi(optarg); if (pid) // valid PID { pid_ok = TRUE; lazy = TRUE; } last_known_argument += 2; break; case 'e': exe=optarg; process_ok = TRUE; last_known_argument += 2; break; case 'P': path=optarg; process_ok = TRUE; last_known_argument += 2; break; case 'l': perclimit=atoi(optarg); limit_ok = TRUE; last_known_argument += 2; break; case 'c': NCPU = atoi(optarg); last_known_argument += 2; break; case 's': send_signal = Translate_Signal(optarg); if ( (send_signal < 1) || (send_signal > 35) ) { fprintf(stderr, "Specified exit signal is not recognized or not within bounds (1-35). Using SIGCONT.\n"); send_signal = SIGCONT; } last_known_argument += 2; case 'k': kill_process = TRUE; last_known_argument++; break; #ifdef LINUX case 'm': monitor_children = TRUE; last_known_argument++; break; #endif case 'r': restore_process = TRUE; last_known_argument++; break; case 'v': verbose = TRUE; last_known_argument++; break; case 'q': quiet = TRUE; last_known_argument++; break; case 'z': lazy = TRUE; last_known_argument++; break; case 'h': print_usage (stdout, 1); last_known_argument++; break; case 'o': last_known_argument++; next_option = -1; break; case '?': print_usage (stderr, 1); last_known_argument++; break; case -1: break; // default: // abort(); } } while(next_option != -1); signal(SIGCHLD, Child_Done); // try to launch a program passed on the command line // But only if we do not already have a PID to watch if ( (last_known_argument + 1 < argc) && (pid_ok == FALSE) ) { last_known_argument++; // if we stopped on "--" jump to the next parameter if ( (last_known_argument + 1 < argc) && (! strcmp(argv[last_known_argument], "--") ) ) last_known_argument++; pid_t forked_pid; // try to launch remaining arguments if (verbose) { int index = last_known_argument; printf("Launching %s", argv[index]); for (index = last_known_argument + 1; index < argc; index++) printf(" %s", argv[index]); printf(" with limit %d\n", perclimit); } forked_pid = fork(); if (forked_pid == -1) // error { printf("Failed to launch specified process.\n"); exit(1); } else if (forked_pid == 0) // target child { execvp(argv[last_known_argument], &(argv[last_known_argument]) ); exit(2); } else // parent who will now fork the throttler { pid_t limit_pid; // if we are planning to kill a process, give it // a running head start to avoid death at start-up if (kill_process) sleep(5); /* The following block assumes we want to run cpulimit in the background. This is the default behaviour. */ if (run_child_in_background) { limit_pid = fork(); if (limit_pid == 0) // child cpulimit process running in background { pid = forked_pid; // the first child, target process lazy = TRUE; pid_ok = TRUE; if (verbose) printf("Throttling process %d\n", (int) pid); } else // parent cpulimit process which can quit exit(0); } // end of running in background else { pid = forked_pid; lazy = TRUE; pid_ok = TRUE; run_in_background = FALSE; } // end of running in foreground } // end of parent that launched target } // end of launching child process if (!process_ok && !pid_ok) { fprintf(stderr,"Error: You must specify a target process\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); exit(1); } if (!limit_ok) { fprintf(stderr,"Error: You must specify a cpu limit\n"); print_usage (stderr, 1); exit(1); } float limit=perclimit/100.0; if ( (limit <= 0.00) || (limit > NCPU) ) { fprintf(stderr,"Error: limit must be in the range of 1 to %d00\n", NCPU); print_usage (stderr, 1); exit(1); } // check to see if we should fork if (run_in_background) { pid_t process_id; process_id = fork(); if (! process_id) exit(0); else { setsid(); process_id = fork(); if (process_id) exit(0); } } //parameters are all ok! signal(SIGINT,quit); signal(SIGTERM,quit); my_pid = getpid(); if (verbose) printf("%d CPUs detected.\n", NCPU); increase_priority(); //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); else waitforpid(pid); //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; // On Linux we can monitor child processes of the target #ifdef LINUX if (monitor_children) { pthread_t my_thread; int thread_status; if (verbose) printf("Starting fork monitoring thread...\n"); program_data.this_program = argv[0]; program_data.limit = perclimit; thread_status = pthread_create(&my_thread, NULL, Monitor_Children, &program_data); if ( (thread_status) && (verbose) ) printf("Creating fork monitoring thread failed.\n"); } #endif //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) { if (!quiet) 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); if (i%200 == 0) print_caption(); } // if (limit<1 && limit>0) { // printf("Comparing %f to %f\n", pcpu, limit); if (pcpu < limit) { // printf("Continue\n"); //resume process if (kill(pid,SIGCONT)!=0) { if (!quiet) fprintf(stderr,"Process %d dead!\n",pid); if (lazy) exit(2); //wait until our process appears goto wait_for_process; } } #ifdef __APPLE_ // OS X does not have clock_gettime, use clock_get_time clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); startwork.tv_sec = mts.tv_sec; startwork.tv_nsec = mts.tv_nsec; #else clock_gettime(CLOCK_REALTIME,&startwork); #endif nanosleep(&twork,NULL); //now process is working #ifdef __APPLE__ // OS X does not have clock_gettime, use clock_get_time // clock_serv_t cclock; // mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); endwork.tv_sec = mts.tv_sec; endwork.tv_nsec = mts.tv_nsec; #else clock_gettime(CLOCK_REALTIME,&endwork); #endif workingtime=timediff(&endwork,&startwork); // if (limit<1) { // printf("Checking %f vs %f\n", pcpu, limit); if (pcpu > limit) { // When over our limit we may run into // situations where we want to kill // the offending process, then restart it if (kill_process) { kill(pid, SIGKILL); if (!quiet) fprintf(stderr, "Process %d killed.\n", pid); if ( (lazy) && (! restore_process) ) exit(2); // restart killed process if (restore_process) { pid_t new_process; new_process = fork(); if (new_process == -1) { fprintf(stderr, "Failed to restore killed process.\n"); } else if (new_process == 0) { // child which becomes new process if (verbose) printf("Relaunching %s\n", argv[last_known_argument]); execvp(argv[last_known_argument], &(argv[last_known_argument]) ); } else // parent { // we need to track new process pid = new_process; // avoid killing child process sleep(5); } } } // do not kill process, just throttle it else { // printf("Stop\n"); //stop process, it has worked enough if (kill(pid,SIGSTOP)!=0) { if (!quiet) fprintf(stderr,"Process %d dead!\n", pid); if (lazy) exit(2); //wait until our process appears goto wait_for_process; } nanosleep(&tsleep,NULL); //now process is sleeping } // end of throttle process } // end of process using too much CPU i++; } return 0; } cpulimit-3.0/test/0000775000175000017500000000000014333501672013173 5ustar jessejessecpulimit-3.0/test/busy.c0000664000175000017500000000106512320363101014306 0ustar jessejesse#include #include #include #include #include void *loop() { while(1); } int main(int argc, char **argv) { int i = 0; int num_threads = 1; if (argc == 2) num_threads = atoi(argv[1]); printf("Process PID: %d\n", getpid() ); for (i=0; i * Command line arguments now available * Now is possible to specify target process by pid cpulimit-3.0/README0000664000175000017500000001463413452465451013111 0ustar jessejesseREADME for LimitCPU ========================== LimitCPU is a program to throttle the CPU cycles used by other applications. LimitCPU will monitor a process and make sure its CPU usage stays at or below a given percentage. This can be used to make sure your system has plenty of CPU cycles available for other tasks. It can also be used to keep laptops cool in the face of CPU-hungry processes and for limiting virtual machines. LimitCPU is the direct child of CPUlimit, a creation of Angelo Marletta, which can be found at http://cpulimit.sourceforge.net. Copying, License and Distribution =================================== LimitCPU is licensed under the GNU General Public License (version 2). A copy of the license should be included with this program in a file named LICENSE. Copyright 2005, Angelo Marletta Copyright 2011-2016, Jesse Smith Where to get LimitCPU ========================== The LimitCPU program can be aquired from http://limitcpu.sourceforge.net How to compile and install =========================== Once you have downloaded a copy of LimitCPU building should be fairly straight forward. First we unpack the source code tar zxf cpulimit-2.6.tar.gz Then we run the makefile. cd cpulimit-2.6 make This should produce the executable file "cpulimit". If you would like to install the program to make it available system-wide, run make install Later should you wish to remove the program from your system, run the following command from the limitcpu directory make deinstall Note on compiling for non-Linux operating systems: LimitCPU can be compiled on FreeBSD and OS X (though at this time LimitCPU has not been tested and may not work properly on OS X). To compile LimitCPU on these platforms, instead of running "make" to build the executable, instead run either make freebsd or make osx in order to build on FreeBSD or OS X respectively. Common usage ========================== The LimitCPU program is generally used to throttle the CPU usage of one process. This can be done with the following command where 12345 is the process ID number of a running program and 25 is the maximum percentage of the CPU we are willing to give that program cpulimit -p 12345 -l 25 The above example will cause LimitCPU to keep an eye on the process with ID number 12345 until the program exits. Should we wish to run LimitCPU in the background we can use cpulimit -p 12345 -l 25 -b We can also limit running processes based on their name instead of their process ID, as in this example: cpulimit --exe /usr/bin/bigexe --limit 50 The above example will keep an eye on "bigexe" and, if the application quits and another program called "bigexe" is run, LimitCPU will monitor the new process too. Should we wish to only track the first program and then exit, we can use cpulimit --exe /usr/bin/bigexe --limit 50 -z The "-z" flag tells LimitCPU to stop running once its target is no longer running itself. The following example is useful for scripts where we want to launch a program and then immediately throttle it without knowing its process ID. The "$!" symbol refers to the last program run and the -b flag tells cpulimit to run in the background, returning control to the shell. /usr/bin/someapp & cpulimit -p $! -l 25 -b Note: As of version 1.7 LimitCPU will attempt to guess the number of CPUs available on the machine and limit usage accordingly. For example, machines with dual-cores will be able to run processes with 1-200% limits. In case the automatic detection does not work, users can over-ride the number of CPUs LimitCPU thinks are available using the -c command line flag. For example cpulimit -c 2 -p 12345 -l 150 Commands can be launched by LimitCPU by appending the command to the end of of LimitCPU's argument list. For example, the following command will launch Firefox and limit it to 10% CPU usage: cpulimit -l 10 firefox Sometimes we wish to launch other programs and pass them parameters. To avoid confusing cpulimit as to which parameters are for it and which ones are for the child process, we can use the -- flag. For example, the following command launches Firefox in Private Browsing mode. To make sure the "-p" in Firefox's "-private" option does not confuse cpulimit, we use the -- flag. cpulimit -l 25 -- firefox -private In some special cases it may be desirable to kill a program instead of simple throttling its CPU usage. In these cases the -k flag can be used. For example, if we want to run a program called "yellow" and kill it when it goes over 50% CPU usage run the following: cpulimit -l 50 -k yellow The -k flag will terminate a process going over its limit, but what if we want to bring back the killed process to let it run again? For that purpose we have the -r flag. The -r flag causes a killed process to be restored. cpulimit -l 50 -k -r yellow The restore flag (-r) only works when the process to launch has been launched by cpulimit. For this reason it is necessary to specify the target program at the end of the command line as shown above. LimitCPU, by default, will tell a watched process to continue running when LimitCPU terminates. This is accomplished by sending the watched process the SIGCONT signal just before LimitCPU exits. The user can specify an alternative signal to send the watched process when LimitCPU terminates. This is accomplished using the -s or --signal command line flag. Here is an example of terminating a watched process cpulimit -l 25 -p 1234 -s SIGTERM And here we accomplish the same thing using a numeric value instead of the written form of the signal. cpulimit -l 25 -p 1234 --signal=15 LimitCPU can be used to watch not only one target process, but also child processes the target spawns. This means new processs the target creates will also be throttled. By default, LimitCPU only watches one target process and ignores children, but monitoring children can be enabled using the "-m" or "--monitor-forks" flag. cpulimit -l 25 -p 1234 -m Accessing the latest code ============================ For developers who would like to test or modify the latest LimitCPU code, the code is available in a Subversion (svn) repository. The code can be checked out using the command svn checkout svn://svn.code.sf.net/p/limitcpu/code/ limitcpu-code Bugs and Feedback ============================= Should you have comments, questions, or bugs to report, please send an e-mail to jessefrgsmith@yahoo.ca with the word "LimitCPU" in the subject line. cpulimit-3.0/Makefile0000664000175000017500000000161014412554600013647 0ustar jessejesseVERSION?=3.0 PREFIX?=/usr CFLAGS?=-Wall -O2 CC?=clang all: cpulimit osx: $(CC) -o cpulimit cpulimit.c -D__APPLE__ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DVERSION=$(VERSION) minix: $(CC) -o cpulimit cpulimit.c $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DVERSION=$(VERSION) freebsd: $(CC) -o cpulimit cpulimit.c -lrt -DFREEBSD $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DVERSION=$(VERSION) cpulimit: cpulimit.c $(CC) -o cpulimit cpulimit.c -pthread -lrt -DLINUX $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -DVERSION=$(VERSION) tests: $(MAKE) -C test install: cpulimit mkdir -p ${PREFIX}/bin mkdir -p ${PREFIX}/share/man/man1 cp cpulimit ${PREFIX}/bin cp cpulimit.1 ${PREFIX}/share/man/man1 deinstall: rm -f ${PREFIX}/bin/cpulimit rm -f ${PREFIX}/share/man/man1/cpulimit.1 clean: rm -f *~ cpulimit $(MAKE) -C test clean tarball: clean cd .. && tar czf cpulimit-$(VERSION).tar.gz --exclude=.svn cpulimit-$(VERSION)