aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile22
-rw-r--r--README35
-rw-r--r--cpulimit.c183
-rw-r--r--loop.c18
-rw-r--r--process.c30
-rw-r--r--process.h12
-rw-r--r--processtest.c14
-rw-r--r--procutils.c589
-rw-r--r--ptest.c19
9 files changed, 484 insertions, 438 deletions
diff --git a/Makefile b/Makefile
index f4cbb9f..261a76a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,24 @@
-CFLAGS=-Wall -O2 -D_GNU_SOURCE
-TARGETS=cpulimit processtest procutils.o list.o loop
+CC=gcc
+CFLAGS=-Wall -D_GNU_SOURCE -O2
+TARGETS=cpulimit ptest
LIBS=process.o procutils.o list.o
all:: $(TARGETS)
cpulimit: cpulimit.c $(LIBS)
- gcc -o cpulimit cpulimit.c $(LIBS) -lrt $(CFLAGS)
+ $(CC) -o cpulimit cpulimit.c $(LIBS) -lrt $(CFLAGS)
-processtest: processtest.c process.o
- gcc -o processtest processtest.c process.o -lrt $(CFLAGS)
-
-loop: loop.c
- gcc -o loop loop.c -lpthread $(CFLAGS)
+ptest: ptest.c process.o procutils.o list.o
+ $(CC) -o ptest ptest.c process.o procutils.o list.o -lrt $(CFLAGS)
process.o: process.c process.h
- gcc -c process.c $(CFLAGS)
+ $(CC) -c process.c $(CFLAGS)
-procutils.o: procutils.c procutils.h list.o
- gcc -c procutils.c $(CFLAGS)
+procutils.o: procutils.c procutils.h
+ $(CC) -c procutils.c $(CFLAGS)
list.o: list.c list.h
- gcc -c list.c $(CFLAGS)
+ $(CC) -c list.c $(CFLAGS)
clean:
rm -f *~ *.o $(TARGETS)
diff --git a/README b/README
new file mode 100644
index 0000000..d25fee5
--- /dev/null
+++ b/README
@@ -0,0 +1,35 @@
+Cpulimit 1.2
+
+===========
+Changelog:
+===========
+- 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 every setpriority(), (todo: set it just once)
+- minor enhancements and bugfixes
+
+
+==============
+Installation:
+==============
+
+Run 'make' and place the executable file 'cpulimit' wherever you want.
+Type 'cpulimit --help' to get documentation on available options.
+
+
+=======
+About:
+=======
+
+cpulimit is a program that attempts to limit the cpu usage of a process (expressed in percentage, not in cpu time). This is useful to control batch jobs, when you don't want they eat too much cpu. It does not act on the nice value or other scheduling priority stuff, but on the real cpu usage. Also, it is able to adapt itself to the overall system load, dynamically and quickly.
+The control of the used cpu amount is done sending continue and stop POSIX signals to processes.
+All the children processes and threads of the specified process will share the same percent of cpu.
+
+Developed by Angelo Marletta.
+Please send your feedback, bug reports, feature requests to marlonx80 at hotmail dot com.
diff --git a/cpulimit.c b/cpulimit.c
index 122cdd8..c6cdb8b 100644
--- a/cpulimit.c
+++ b/cpulimit.c
@@ -23,17 +23,20 @@
* This is a simple program to limit the cpu usage of a process
* If you modify this code, send me a copy please
*
- * Date: 9/2/2008
- * Version: 1.2
- * Get the latest version at: http://cpulimit.sourceforge.net/
+ * Date: 15/2/2008
+ * Version: 1.2 alpha
+ * Get the latest version at: http://cpulimit.sourceforge.net
*
* Changelog:
* - 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
+ * - option --path eliminated, use --exe instead both for absolute path and file name
+ * - deleted every setpriority(), (todo: set it just once)
* - minor enhancements and bugfixes
*
*/
@@ -53,6 +56,7 @@
#include <dirent.h>
#include <errno.h>
#include <string.h>
+
#include "process.h"
#include "procutils.h"
#include "list.h"
@@ -60,27 +64,24 @@
//some useful macro
#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
-//the process pool, containing the main process and all the processes directly
-//or indirectly generated by the main process
-struct list process_pool;
-//pid of the main process
-int pid;
+//the "family"
+struct process_family pf;
//pid of cpulimit
int cpulimit_pid;
+//name of this program (maybe cpulimit...)
+char *program_name;
+
+/* CONFIGURATION VARIABLES */
-//CONFIGURATION VARIABLES
//verbose mode
int verbose = 0;
//lazy mode (exits if there is no process)
int lazy = 0;
-//priority values of the daemon (nice)
-int high_priority_value = -10;
-int low_priority_value = 10;
-int default_priority_value;
-
-//name of this program (cpulimit)
-char *program_name;
//how many cpu do we have?
int get_cpu_count()
@@ -99,36 +100,33 @@ int get_cpu_count()
return cpu_count - 1;
}
-
-//return ta-tb in microseconds (no overflow checks, so better watch out!)
-inline unsigned long timediff(const struct timespec *ta,const struct timespec *tb)
+//return t1-t2 in microseconds (no overflow checks, so better watch out!)
+inline unsigned long timediff(const struct timespec *t1,const struct timespec *t2)
{
- unsigned long us = (ta->tv_sec-tb->tv_sec)*1000000 + (ta->tv_nsec/1000 - tb->tv_nsec/1000);
- return us;
+ return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_nsec/1000 - t2->tv_nsec/1000);
}
-inline unsigned long long tv_diff(struct timeval *tv1, struct timeval *tv2)
+//returns t1-t2 in microseconds
+inline unsigned long long tv_diff(struct timeval *t1, struct timeval *t2)
{
- unsigned long long ret;
- ret = ((unsigned long long)(tv2->tv_sec - tv1->tv_sec)) * 1000000ULL;
- ret += tv2->tv_usec - tv1->tv_usec;
- return ret;
+ return ((unsigned long long)(t1->tv_sec - t2->tv_sec)) * 1000000ULL + t1->tv_usec - t2->tv_usec;
}
//SIGINT and SIGTERM signal handler
void quit(int sig)
{
//let all the processes continue if stopped
- //todo...
-// kill(target_proc.pid, SIGCONT);
+ 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);
}
-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);
@@ -145,9 +143,6 @@ void print_usage(FILE *stream, int exit_code)
void limit_process(int pid, float limit)
{
- //control time slot in microseconds
- //each slot is splitted in a working slice and a sleeping slice
- int slot=100000;
//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
@@ -160,96 +155,108 @@ void limit_process(int pid, float limit)
memset(&twork, 0, sizeof(struct timespec));
memset(&tsleep, 0, sizeof(struct timespec));
memset(&startwork, 0, sizeof(struct timespec));
- memset(&endwork, 0, sizeof(struct timespec));
-
+ memset(&endwork, 0, sizeof(struct timespec));
//last working time in microseconds
unsigned long workingtime = 0;
-
- if (verbose) print_caption();
-
-// float pcpu_avg=0;
int i = 0;
-//TODO: SPOSTARE NEL WHILE?
-//FARE ALGORITMO DI CONTROLLO MULTIPLO E HO FINITO!
- //ok, the main process was found
- struct process *proc = malloc(sizeof(struct process));
- //process initialization
- process_init(proc, pid);
- //add the main process to the process pool
- add_elem(&process_pool, proc);
+ //build the family
+ create_process_family(&pf, pid);
+ struct list_node *node;
+
+ printf("Members in the family owned by %d: %d\n", pf.father, pf.members.count);
- //here we should have high priority, for time precision
while(1) {
+ if (i%100==0 && verbose) print_caption();
+
if (i%10==0) {
- int *ch=NULL;
- get_sub_processes(pid, &ch);
- exit(0);
-//TODO: update process pool
+ //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");
+ }
}
- //estimate how much the controlled process is using the cpu in the working interval
- struct cpu_usage cu;
- if (process_monitor((struct process*)first_elem(&process_pool), workingtime, &cu)==-1) {
- fprintf(stderr,"Process %d dead!\n", proc->pid);
- return;
+ //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;
}
-
- //cpu actual usage of process (range 0-1)
- //1 means that the process is using 100% cpu
- float pcpu = cu.pcpu;
- //rate at which we are keeping active the process (range 0-1)
- //1 means that the process is using all the twork slice
- float workingrate = cu.workingrate;
-
+ //average value
+ workingrate /= pf.members.count;
+
//adjust work and sleep time slices
if (pcpu>0) {
- twork.tv_nsec = MIN(slot*limit*1000/pcpu*workingrate,slot*1000);
+ twork.tv_nsec = MIN(CONTROL_SLOT*limit*1000/pcpu*workingrate,CONTROL_SLOT*1000);
}
else if (pcpu==0) {
- twork.tv_nsec = slot*1000;
+ 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(slot*limit*1000,slot*1000);
+ twork.tv_nsec = MIN(CONTROL_SLOT*limit*1000,CONTROL_SLOT*1000);
}
- tsleep.tv_nsec = slot*1000-twork.tv_nsec;
-
- //update average usage
-// pcpu_avg=(pcpu_avg*i+pcpu)/(i+1);
+ tsleep.tv_nsec = CONTROL_SLOT*1000-twork.tv_nsec;
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 (limit>0 && limit<1) {
- //resume process
+ //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);
- return;
+ remove_process_from_family(&pf, proc->pid);
}
}
- //now the process is allowed to run
+ //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);
- if (limit<1) {
- //stop the process, it has worked enough
+ //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) {
- fprintf(stderr,"Process %d dead!\n",proc->pid);
- return;
+ //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);
}
+ //now the process is forced to sleep
+ nanosleep(&tsleep,NULL);
i++;
}
+ cleanup_process_family(&pf);
}
int main(int argc, char **argv) {
@@ -265,6 +272,7 @@ int main(int argc, char **argv) {
int pid_ok = 0;
int process_ok = 0;
int limit_ok = 0;
+ int pid = 0;
//parse arguments
int next_option;
@@ -343,8 +351,7 @@ int main(int argc, char **argv) {
signal(SIGINT, quit);
signal(SIGTERM, quit);
- //the default priority for cpulimit
- default_priority_value = getpriority(PRIO_PROCESS, getpid());
+ //TODO: try to renice
while(1) {
//look for the target process..or wait for it
diff --git a/loop.c b/loop.c
deleted file mode 100644
index 83b354e..0000000
--- a/loop.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <pthread.h>
-
-void *loop() {
- printf("thread PID: %d\n", getpid());
- while(1);
-}
-
-int main()
-{
- printf("PID: %d\n", getpid());
- pthread_t th;
- pthread_create(&th, NULL, loop, NULL);
- while(1);
- return 0;
-}
diff --git a/process.c b/process.c
index 501a7be..a16267b 100644
--- a/process.c
+++ b/process.c
@@ -21,7 +21,7 @@
#include "process.h"
-int process_init(struct process *proc, int pid)
+int process_init(struct process_history *proc, int pid)
{
proc->pid = pid;
//init circular queue
@@ -34,25 +34,18 @@ int process_init(struct process *proc, int pid)
sprintf(proc->stat_file, "/proc/%d/stat", proc->pid);
FILE *fd = fopen(proc->stat_file, "r");
fclose(fd);
+ proc->usage.pcpu = 0;
+ proc->usage.workingrate = 0;
return (fd == NULL);
}
-//return ta-tb in microseconds (no overflow checks, you've been warned!)
-static inline unsigned long timediff(const struct timespec *ta,const struct timespec *tb)
+//return t1-t2 in microseconds (no overflow checks, so better watch out!)
+static inline unsigned long timediff(const struct timespec *t1,const struct timespec *t2)
{
- unsigned long us = (ta->tv_sec-tb->tv_sec)*1000000 + (ta->tv_nsec/1000 - tb->tv_nsec/1000);
- return us;
+ return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_nsec/1000 - t2->tv_nsec/1000);
}
-static inline unsigned long long tv_diff(struct timeval *tv1, struct timeval *tv2)
-{
- unsigned long long ret;
- ret = ((unsigned long long)(tv2->tv_sec - tv1->tv_sec)) * 1000000ULL;
- ret += tv2->tv_usec - tv1->tv_usec;
- return ret;
-}
-
-static int get_jiffies(struct process *proc) {
+static int get_jiffies(struct process_history *proc) {
FILE *f = fopen(proc->stat_file, "r");
if (f==NULL) return -1;
fgets(proc->buffer, sizeof(proc->buffer),f);
@@ -70,7 +63,7 @@ static int get_jiffies(struct process *proc) {
return utime+ktime;
}
-int process_monitor(struct process *proc, int last_working_quantum, struct cpu_usage *usage)
+int process_monitor(struct process_history *proc, int last_working_quantum, struct cpu_usage *usage)
{
//increment front index
proc->front_index = (proc->front_index+1) % HISTORY_SIZE;
@@ -90,13 +83,13 @@ int process_monitor(struct process *proc, int last_working_quantum, struct cpu_u
front->cputime = last_working_quantum;
if (proc->actual_history_size==1) {
- //not enough samples taken (it's the first one!), return 0
+ //not enough elements taken (it's the first one!), return 0
usage->pcpu = -1;
usage->workingrate = 1;
return 0;
}
else {
- //queue has samples enough
+ //queue has enough elements
//now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds
long dt = timediff(&(front->when), &(tail->when));
//the total time between tail and front in which the process was allowed to run
@@ -112,7 +105,7 @@ int process_monitor(struct process *proc, int last_working_quantum, struct cpu_u
}
}
-int process_close(struct process *proc)
+int process_close(struct process_history *proc)
{
if (kill(proc->pid,SIGCONT)!=0) {
fprintf(stderr,"Process %d is already dead!\n", proc->pid);
@@ -120,4 +113,3 @@ int process_close(struct process *proc)
proc->pid = 0;
return 0;
}
-
diff --git a/process.h b/process.h
index 1f4a40e..16cbf8f 100644
--- a/process.h
+++ b/process.h
@@ -54,14 +54,14 @@ struct process_screenshot {
};
//process descriptor
-struct process {
+struct process_history {
//the PID of the process
int pid;
//name of /proc/PID/stat file
char stat_file[20];
//read buffer for /proc filesystem
char buffer[1024];
- //recent history of the process (circular queue)
+ //recent history of the process (fixed circular queue)
struct process_screenshot history[HISTORY_SIZE];
//the index of the last screenshot made to the process
int front_index;
@@ -71,12 +71,14 @@ struct process {
int actual_history_size;
//total cputime saved in the history
long total_workingtime;
+ //current usage
+ struct cpu_usage usage;
};
-int process_init(struct process *proc, int pid);
+int process_init(struct process_history *proc, int pid);
-int process_monitor(struct process *proc, int last_working_quantum, struct cpu_usage *usage);
+int process_monitor(struct process_history *proc, int last_working_quantum, struct cpu_usage *usage);
-int process_close(struct process *proc);
+int process_close(struct process_history *proc);
#endif
diff --git a/processtest.c b/processtest.c
deleted file mode 100644
index be353b5..0000000
--- a/processtest.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "process.h"
-
-int main()
-{
- struct process p;
- process_init(&p, getpid());
- struct cpu_usage u;
- process_monitor(&p, 0, &u);
- return 0;
-}
-
diff --git a/procutils.c b/procutils.c
index 25eab3f..0a64454 100644
--- a/procutils.c
+++ b/procutils.c
@@ -22,129 +22,9 @@
#include <sys/utsname.h>
#include "procutils.h"
-int look_for_process_by_pid(int pid)
-{
- DIR *dip;
- struct dirent *dit;
- int ret=0;
-
- //open a directory stream to /proc directory
- if ((dip = opendir("/proc")) == NULL) {
- perror("opendir");
- return -10;
- }
-
- //read in from /proc and seek for process dirs
- while ((dit = readdir(dip)) != NULL) {
- //get pid
- if (pid==atoi(dit->d_name)) {
- //here is the process
- if (kill(pid,SIGCONT)==0) {
- //process is ok!
- ret = pid;
- }
- else {
- //process detected, but you aren't allowed to control it
- ret = -pid;
- }
- break;
- }
- }
-
- //close the dir stream and check for errors
- if (closedir(dip) == -1) {
- perror("closedir");
- return -11;
- }
-
- return ret;
-}
-
-//look for a process with a given name
-//process: the name of the wanted process. it can be an absolute path name to the executable file
-// or just the file name
-//return: pid of the found process, if it is found
-// 0, if it's not found
-// negative pid, if it is found but it's not possible to control it
-int look_for_process_by_name(const char *process)
-{
- //the name of /proc/pid/exe symbolic link pointing to the executable file
- char exelink[20];
- //the name of the executable file
- char exepath[PATH_MAX+1];
- //pid of the process, if it's found
- int pid = 0;
- //whether the variable process is the absolute path or not
- int is_absolute_path = process[0] == '/';
- //flag indicating if the a process with given name was found
- int found = 0;
-
- DIR *dip;
- struct dirent *dit;
-
- //open a directory stream to /proc directory
- if ((dip = opendir("/proc")) == NULL) {
- perror("opendir");
- return -10;
- }
-
- //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) {
- found = 0;
- if (is_absolute_path && strncmp(exepath, process, size)==0 && size==strlen(process)) {
- //process found
- found = 1;
- }
- else {
- //process found
- if (strncmp(exepath + size - strlen(process), process, strlen(process))==0) {
- found = 1;
- }
- }
- if (found==1) {
- if (kill(pid,SIGCONT)==0) {
- //process is ok!
- break;
- }
- else {
- //we don't have permission to send signal to that process
- //so, don't exit from the loop and look for another one with the same name
- found = -1;
- }
- }
- }
- }
- }
-
- //close the dir stream and check for errors
- if (closedir(dip) == -1) {
- perror("closedir");
- return -11;
- }
-
- if (found == 1) {
- //ok, the process was found
- return pid;
- }
- else if (found == 0) {
- //no process found
- return 0;
- }
- else if (found == -1) {
- //the process was found, but we haven't permission to control it
- return -pid;
- }
- //this can't ever happen
- return 0;
-
-}
+/* PROCESS STATISTICS FUNCTIONS */
+// returns pid of the parent process
static int getppid_of(int pid)
{
char file[20];
@@ -164,203 +44,348 @@ static int getppid_of(int pid)
return ppid;
}
-// searches a process in the tree by its pid
-// returns the address of the node, or NULL if it's not found
-static struct process_tree_node *locate_process(struct process_tree *ptree, int pid)
+// returns the start time of a process (used with pid to identify a process)
+static int get_starttime(int pid)
{
- struct process_tree_node *ret = NULL;
- int i;
- for (i=0; i<ptree->proc_size; i++)
- if (ptree->proc[i].pid>0 && ptree->proc[i].pid == pid)
- ret = &(ptree->proc[i]);
- return ret;
+ char file[20];
+ char buffer[1024];
+ sprintf(file, "/proc/%d/stat", pid);
+ FILE *fd = fopen(file, "r");
+ if (fd==NULL) return -1;
+ fgets(buffer, sizeof(buffer), fd);
+ fclose(fd);
+ char *p = buffer;
+ p = memchr(p+1,')', sizeof(buffer) - (p-buffer));
+ int sp = 20;
+ while (sp--)
+ p = memchr(p+1,' ',sizeof(buffer) - (p-buffer));
+ //pid of the parent process
+ int time = atoi(p+1);
+ return time;
}
-// builds the complete current process tree
-// it should be called just once, then it's enough to call the faster update_process_tree()
-// to keep the tree up to date
-// if spid is set to a valid process pid, subprocesses monitoring will be activated
-// returns 0 if successful
-int build_process_tree(struct process_tree *ptree)
+// detects whether a process is a kernel thread or not
+static int is_kernel_thread(int pid)
{
- return 0;
+ static char statfile[20];
+ static char buffer[64];
+ int ret;
+ sprintf(statfile, "/proc/%d/statm", pid);
+ FILE *fd = fopen(statfile, "r");
+ if (fd==NULL) return -1;
+ fgets(buffer, sizeof(buffer), fd);
+ ret = strncmp(buffer,"0 0 0",3)==0;
+ fclose(fd);
+ return ret;
}
-// updates the process tree
-// you should call build_process_tree() once before using this function
-// if spid is set to a valid process pid, even new_subsubprocesses is cleared and updated
-// returns 0 if successful
-int update_process_tree(struct process_tree *ptree)
-{
- return 0;
+// returns 1 if pid is a user process, 0 otherwise
+static int process_exists(int pid) {
+ static char statfile[20];
+ static char buffer[64];
+ int ret;
+ sprintf(statfile, "/proc/%d/statm", pid);
+ FILE *fd = fopen(statfile, "r");
+ if (fd==NULL) return 0;
+ fgets(buffer, sizeof(buffer), fd);
+ ret = strncmp(buffer,"0 0 0",3)!=0;
+ fclose(fd);
+ return ret;
}
-// finds the subprocesses of a given process (children, children of children, etc..)
-// the algorithm first finds the process and then, its subprocesses recursively
-// you should call build_process_tree() once before using this function
-// if pid is spid, consider using get_new_subprocesses() which is much faster and scalable O(1)
-// returns the number of subprocesses found, or -1 if the pid it's not found in the tree
-int get_subprocesses(struct process_tree *ptree, int pid, int **subprocesses, int *subprocesses_size)
-{
- return 0;
-}
+/* PID HASH FUNCTIONS */
-// returns special process subprocesses detected by last build_process_tree() or update_process_tree() call
-// you should call build_process_tree() once before using this function
-int get_new_subprocesses(struct process_tree *ptree, int **subprocesses, int *subprocesses_size)
+static int hash_process(struct process_family *f, struct process *p)
{
- *subprocesses_size = ptree->subprocesses_size;
-
- return 0;
-}
-
-// free the heap memory used by a process tree
-void cleanup_process_tree(struct process_tree *ptree)
-{
- int i;
- for (i=0; i<ptree->proc_size; i++) {
- //if it's a valid process then free the children
- if (ptree->proc[i].pid > 0 && ptree->proc[i].children_count > 0) {
- free(ptree->proc[i].children);
- ptree->proc[i].children = NULL;
- }
+ int ret;
+ struct list **l = &(f->hashtable[pid_hashfn(p->pid)]);
+ if (*l == NULL) {
+ //there is no process in this hashtable item
+ //allocate the list
+ *l = malloc(sizeof(struct list));
+ init_list(*l, 4);
+ add_elem(*l, p);
+ ret = 0;
+ f->count++;
}
- //free subprocesses of the special process
- if (ptree->new_subprocesses_size > 0) {
- free(ptree->new_subprocesses);
- ptree->new_subprocesses = NULL;
+ else {
+ //list already exists
+ struct process *tmp = (struct process*)locate_elem(*l, p);
+ if (tmp != NULL) {
+ //update process info
+ memcpy(tmp, p, sizeof(struct process));
+ ret = 1;
+ }
+ else {
+ //add new process
+ add_elem(*l, p);
+ ret = 0;
+ f->count++;
+ }
}
- //free the nodes dynamic array
- free(ptree->proc);
- ptree->proc = NULL;
+ return ret;
}
-// searches the sub processes of a given process
-// returns the number of subprocesses found
-int get_sub_processes2(struct process_tree *ptree, int pid, int **sub)
-{
- struct process_tree_node *p = locate_process(ptree, pid);
- int sub_block = 10;
- int sub_count = sub_block;
- *sub = malloc(sizeof(int) * sub_block);
+static void unhash_process(struct process_family *f, int pid) {
+ //remove process from hashtable
+ struct list **l = &(f->hashtable[pid_hashfn(pid)]);
+ if (*l == NULL)
+ return; //nothing done
+ struct list_node *node = locate_node(*l, &pid);
+ if (node != NULL)
+ destroy_node(*l, node);
+ f->count--;
}
+static struct process *find_process(struct process_family *f, int pid)
+{
+ struct list **l = &(f->hashtable[pid_hashfn(pid)]);
+ return (*l != NULL) ? (struct process*)locate_elem(*l, &pid) : NULL;
+}
-//TODO: add a get_process_tree(), get_sub_processes(tree), update_process_tree()
+/*
+static int is_member(struct process_family *f, int pid) {
+ struct process *p = find_process(f, pid);
+ return (p!=NULL && p->member);
+}
-static void visit_tree(struct process_tree_node *node, int **children, int *children_count)
-{
- int i;
- for (i=0; i<node->children_count; i++) {
- (*children)[*children_count] = node->children[i]->pid;
- (*children_count)++;
- visit_tree(node->children[i], children, children_count);
- }
- return;
+static int exists(struct process_family *f, int pid) {
+ struct process *p = find_process(f, pid);
+ return p!=NULL;
}
+*/
-int get_sub_processes_old(int pid, int **children)
-{
- //stuff for reading directories
- DIR *dip;
- struct dirent *dit;
+/* PROCESS ITERATOR STUFF */
+// creates an object that browse all running processes
+static int init_process_iterator(struct process_iterator *i) {
//open a directory stream to /proc directory
- if ((dip = opendir("/proc")) == NULL) {
+ if ((i->dip = opendir("/proc")) == NULL) {
perror("opendir");
- return -10;
+ return -1;
}
+ return 0;
+}
- //initial size of process dynamic array
- int proc_block = 64;
- //dinamic array containing pids of the processes
- int *proc = malloc(sizeof(int) * proc_block);
- int proc_count = 0;
- int children_count = 0;
-
- //read in from /proc and seek for processes
- while ((dit = readdir(dip)) != NULL) {
- //parse pid
- int pid2 = atoi(dit->d_name);
- if (pid2 == 0) continue; //not a process
- //check if the array must be enlarged
- if (proc_count>0 && (proc_count % proc_block == 0)) {
- proc = realloc(proc, sizeof(int) * (proc_count + proc_block));
- }
- //add pid2 to proc
- proc[proc_count++] = pid2;
+// reads the next user process from /process
+// automatic closing if the end of the list is reached
+static int read_next_process(struct process_iterator *i) {
+ //read in from /proc and seek for process dirs
+ int pid = 0;
+ while ((i->dit = readdir(i->dip)) != NULL) {
+ pid = atoi(i->dit->d_name);
+ if (pid<=0 || is_kernel_thread(pid))
+ continue;
+ //return the first found process
+ break;
}
- //close the dir stream and check for errors
- if (closedir(dip) == -1) {
- perror("closedir");
- return -11;
+ if (pid == 0) {
+ //no more processes, release resources
+ closedir(i->dip);
}
- //shrink proc to the real size
- proc = realloc(proc, sizeof(int) * proc_count);
- //which members of proc are children in the process tree?
- //create the children array
- *children = malloc(sizeof(int) * proc_count);
-
- //process tree
- struct process_tree_node nodes[proc_count], *init_process=NULL, *main_process=NULL;
+ return pid;
+}
- printf("%d processes detected\n", proc_count);
+/* PUBLIC FUNCTIONS */
- //counters
- int i, j;
- //initial size of dynamic children arrays
- int children_block = 8;
- //tree initialization
- for (i=0; i<proc_count; i++) {
- nodes[i].pid = proc[i];
- nodes[i].children_count = 0;
- nodes[i].children = malloc(sizeof(void*)*children_block);
- }
- //build the process tree
- for (i=0; i<proc_count; i++) {
- int ppid = getppid_of(proc[i]);
- //proc[i] is child of ppid
- //search ppid in the proc
- for (j=0; j<proc_count; j++) {
- if (proc[j] == ppid) break;
+// searches for all the processes derived from father and stores them
+// in the process family struct
+int create_process_family(struct process_family *f, int father)
+{
+ //process list initialization
+ init_list(&(f->members), 4);
+ //hashtable initialization
+ memset(&(f->hashtable), 0, sizeof(f->hashtable));
+ f->count = 0;
+ f->father = father;
+ //process iterator
+ struct process_iterator iter;
+ init_process_iterator(&iter);
+ int pid = 0;
+ while ((pid = read_next_process(&iter))) {
+ //check if process belongs to the family
+ int ppid = pid;
+ //TODO: optimize (add also parents)
+ while(ppid!=1 && ppid!=father) {
+ ppid = getppid_of(ppid);
}
- if (j==proc_count) {
- //ppid not found, so proc[i] must be the init process
- if (proc[i]==1) init_process = &(nodes[i]);
+ //allocate and insert the process
+ struct process *p = malloc(sizeof(struct process));
+ p->pid = pid;
+ p->starttime = get_starttime(pid);
+ if (ppid==1) {
+ p->member = 0;
}
- else {
- if (nodes[j].children_count>0 && (nodes[j].children_count % children_block == 0)) {
- nodes[j].children = realloc(nodes[j].children, sizeof(void*) * (nodes[j].children_count + children_block));
- }
- nodes[j].children[nodes[j].children_count] = &(nodes[i]);
- nodes[j].children_count++;
+ else if (pid != getpid()) {
+ //add to members (only if it's not the current cpulimit process!)
+ p->member = 1;
+ add_elem(&(f->members), p);
}
- if (nodes[i].pid == pid) main_process = &(nodes[i]);
+ //init history
+ p->history = malloc(sizeof(struct process_history));
+ process_init(p->history, pid);
+ //add to hashtable
+ hash_process(f, p);
}
+ return 0;
+}
-/* for (i=0; i<proc_count; i++) {
- if (nodes[i].children_count == 0) continue;
- printf("Process %d has %d children (", nodes[i].pid, nodes[i].children_count);
- for (j=0; j<nodes[i].children_count; j++) {
- printf("%d", nodes[i].children[j]->pid);
- if (j<nodes[i].children_count-1) printf(" ");
+// checks if there are new processes born in the specified family
+// if any they are added to the members list
+// the number of new born processes is returned
+int check_new_members(struct process_family *f)
+{
+ int ret = 0;
+ //process iterator
+ struct process_iterator iter;
+ init_process_iterator(&iter);
+ int pid = 0;
+ while ((pid = read_next_process(&iter))) {
+ struct process *newp = find_process(f, pid);
+ if (newp != NULL) continue; //already known
+ //the process is new, check if it belongs to the family
+ int ppid = getppid_of(pid);
+ //search the youngest known ancestor of the process
+ struct process *ancestor = NULL;
+ while((ancestor=find_process(f, ppid))==NULL) {
+ ppid = getppid_of(ppid);
}
- printf(")\n");
- }
-*/
- //get the children list
- visit_tree(main_process, children, &children_count);
- //shrink children to the real size
- *children = realloc(*children, sizeof(int) * children_count);
+ if (ancestor == NULL) {
+ //this should never happen! if does, find and correct the bug
+ printf("Fatal bug! Process %d is without parent\n", pid);
+ exit(1);
+ }
+ //allocate and insert the process
+ struct process *p = malloc(sizeof(struct process));
+ p->pid = pid;
+ p->starttime = get_starttime(pid);
+ p->member = 0;
+ if (ancestor->member) {
+ //add to members
+ p->member = 1;
+ add_elem(&(f->members), p);
+ ret++;
+ }
+ //init history
+ p->history = malloc(sizeof(struct process_history));
+ process_init(p->history, pid);
+ //add to hashtable
+ hash_process(f, p);
+ }
+ return ret;
+}
- //memory cleanup
- for (i=0; i<proc_count; i++) {
- free(nodes[i].children);
- nodes[i].children = NULL;
+// removes a process from the family by its pid
+void remove_process_from_family(struct process_family *f, int pid)
+{
+ struct list_node *node = locate_node(&(f->members), &pid);
+ if (node != NULL) {
+ struct process *p = (struct process*)(node->data);
+ free(p->history);
+ p->history = NULL;
+ destroy_node(&(f->members), node);
+ }
+ unhash_process(f, pid);
+}
+
+// free the heap memory used by a process family
+void cleanup_process_family(struct process_family *f)
+{
+ int i;
+ int size = sizeof(f->hashtable) / sizeof(struct process*);
+ for (i=0; i<size; i++) {
+ if (f->hashtable[i] != NULL) {
+ //free() history for each process
+ struct list_node *node = NULL;
+ for (node=f->hashtable[i]->first; node!=NULL; node=node->next) {
+ struct process *p = (struct process*)(node->data);
+ free(p->history);
+ p->history = NULL;
+ }
+ destroy_list(f->hashtable[i]);
+ free(f->hashtable[i]);
+ f->hashtable[i] = NULL;
+ }
}
- free(proc);
- proc = NULL;
+ flush_list(&(f->members));
+ f->count = 0;
+ f->father = 0;
+}
- return children_count;
+// look for a process by pid
+// search_pid : pid of the wanted process
+// return: pid of the found process, if it is found
+// 0, if it's not found
+// negative pid, if it is found but it's not possible to control it
+int look_for_process_by_pid(int pid)
+{
+ if (process_exists(pid))
+ return (kill(pid,SIGCONT)==0) ? pid : -pid;
+ return 0;
}
+// look for a process with a given name
+// process: the name of the wanted process. it can be an absolute path name to the executable file
+// or just the file name
+// return: pid of the found process, if it is found
+// 0, if it's not found
+// negative pid, if it is found but it's not possible to control it
+int look_for_process_by_name(const char *process)
+{
+ //the name of /proc/pid/exe symbolic link pointing to the executable file
+ char exelink[20];
+ //the name of the executable file
+ char exepath[PATH_MAX+1];
+ //whether the variable process is the absolute path or not
+ int is_absolute_path = process[0] == '/';
+ //flag indicating if the a process with given name was found
+ int found = 0;
+
+ //process iterator
+ struct process_iterator iter;
+ init_process_iterator(&iter);
+ int pid = 0;
+ while ((pid = read_next_process(&iter))) {
+ //read the executable link
+ sprintf(exelink,"/proc/%d/exe",pid);
+ int size = readlink(exelink, exepath, sizeof(exepath));
+ if (size>0) {
+ found = 0;
+ if (is_absolute_path && strncmp(exepath, process, size)==0 && size==strlen(process)) {
+ //process found
+ found = 1;
+ }
+ else {
+ //process found
+ if (strncmp(exepath + size - strlen(process), process, strlen(process))==0) {
+ found = 1;
+ }
+ }
+ if (found==1) {
+ if (kill(pid,SIGCONT)==0) {
+ //process is ok!
+ break;
+ }
+ else {
+ //we don't have permission to send signal to that process
+ //so, don't exit from the loop and look for another one with the same name
+ found = -1;
+ }
+ }
+ }
+ }
+ if (found == 1) {
+ //ok, the process was found
+ return pid;
+ }
+ else if (found == 0) {
+ //no process found
+ return 0;
+ }
+ else if (found == -1) {
+ //the process was found, but we haven't permission to control it
+ return -pid;
+ }
+ //this MUST NOT happen
+ return 0;
+}
diff --git a/ptest.c b/ptest.c
new file mode 100644
index 0000000..2901347
--- /dev/null
+++ b/ptest.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "procutils.h"
+
+int main()
+{
+ printf("PID %d\n", getpid());
+ int i;
+ for (i=0;i<10;i++) {
+ if (fork()>0){
+ printf("PID %d\n", getpid());
+ while(1);
+ }
+ }
+ return 0;
+}
Un proyecto texto-plano.xyz