diff options
author | Angelo Marletta <angelo.marletta@gmail.com> | 2012-06-23 05:02:54 -0700 |
---|---|---|
committer | Angelo Marletta <angelo.marletta@gmail.com> | 2012-06-23 05:02:54 -0700 |
commit | 157117a985b0ba13b8df6ac164c57027e4374216 (patch) | |
tree | ccc7f862b705a8049bd6fcc74a9c959b6784dda2 | |
parent | 0ba6a181d84c196c9d9854ded7528baac66af461 (diff) | |
parent | b8bc055ee5131c333af3422569bde24bb760ac65 (diff) | |
download | cpulimit-157117a985b0ba13b8df6ac164c57027e4374216.tar.gz |
Merge pull request #3 from opsengine/develop
Major refactoring of cpulimit
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | src/Makefile | 22 | ||||
-rw-r--r-- | src/cpulimit.c | 166 | ||||
-rw-r--r-- | src/list.c | 2 | ||||
-rw-r--r-- | src/list.h | 2 | ||||
-rw-r--r-- | src/process.c | 233 | ||||
-rw-r--r-- | src/process_group.c | 203 | ||||
-rw-r--r-- | src/process_group.h | 53 | ||||
-rw-r--r-- | src/process_iterator.c | 26 | ||||
-rw-r--r-- | src/process_iterator.h (renamed from src/process.h) | 61 | ||||
-rw-r--r-- | src/process_iterator_apple.c | 77 | ||||
-rw-r--r-- | src/process_iterator_freebsd.c | 95 | ||||
-rw-r--r-- | src/process_iterator_linux.c | 159 | ||||
-rw-r--r-- | src/procutils.c | 463 | ||||
-rw-r--r-- | src/procutils.h | 2 | ||||
-rw-r--r-- | tests/Makefile | 20 | ||||
-rwxr-xr-x | tests/busy | bin | 8672 -> 0 bytes | |||
-rw-r--r-- | tests/busy.c | 8 | ||||
-rw-r--r-- | tests/process_iterator_test.c | 196 |
19 files changed, 926 insertions, 864 deletions
@@ -1,3 +1,5 @@ *.o *~ src/cpulimit +busy +process_iterator_test diff --git a/src/Makefile b/src/Makefile index 33c632d..86fbfbd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,22 +1,28 @@ CC?=gcc -CFLAGS?=-Wall -O2 +CFLAGS?=-Wall -g -D_GNU_SOURCE TARGETS=cpulimit -LIBS=process.o procutils.o list.o +LIBS=list.o process_iterator.o process_group.o -all:: $(TARGETS) +UNAME := $(shell uname) + +ifeq ($(UNAME), FreeBSD) +LIBS+=-lkvm +endif + +all:: $(TARGETS) $(LIBS) cpulimit: cpulimit.c $(LIBS) $(CC) -o cpulimit cpulimit.c $(LIBS) $(CFLAGS) -process.o: process.c process.h - $(CC) -c process.c $(CFLAGS) - -procutils.o: procutils.c procutils.h - $(CC) -c procutils.c $(CFLAGS) +process_iterator.o: process_iterator.c process_iterator.h + $(CC) -c process_iterator.c $(CFLAGS) list.o: list.c list.h $(CC) -c list.c $(CFLAGS) +process_group.o: process_group.c process_group.h process_iterator.o list.o + $(CC) -c process_group.c $(CFLAGS) + clean: rm -f *~ *.o $(TARGETS) diff --git a/src/cpulimit.c b/src/cpulimit.c index 300d320..394b132 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -61,8 +61,7 @@ #include <sys/resource.h> #include <sys/wait.h> -#include "process.h" -#include "procutils.h" +#include "process_group.h" #include "list.h" //some useful macro @@ -83,7 +82,7 @@ /* GLOBAL VARIABLES */ //the "family" -struct process_family pf; +struct process_group pgroup; //pid of cpulimit pid_t cpulimit_pid; //name of this program (maybe cpulimit...) @@ -99,35 +98,16 @@ int verbose = 0; //lazy mode (exits if there is no process) int lazy = 0; -static void *memrchr(const void *s, int c, size_t n) -{ - const unsigned char *start = (const unsigned char*)s; - const unsigned char *end = (const unsigned char*)s; - - end+=n-1; - - while(end>=start) { - if(*end==c) - return (void *)end; - else - end--; - } - - return NULL; -} - //SIGINT and SIGTERM signal handler static void quit(int sig) { //let all the processes continue if stopped struct list_node *node = NULL; - for (node=pf.members.first; node!= NULL; node=node->next) { + for (node=pgroup.proclist->first; node!= NULL; node=node->next) { struct process *p = (struct process*)(node->data); kill(p->pid, SIGCONT); - process_close(p); } - //free all the memory - cleanup_process_family(&pf); + close_process_group(&pgroup); //fix ^C little problem printf("\r"); fflush(stdout); @@ -185,22 +165,7 @@ static int get_ncpu() { return ncpu; } -#ifdef __linux__ - -#include <sys/vfs.h> - -static int check_proc() -{ - struct statfs mnt; - if (statfs("/proc", &mnt) < 0) - return 0; - if (mnt.f_type!=0x9fa0) - return 0; - return 1; -} -#endif - -void limit_process(pid_t pid, double limit, int ignore_children) +void limit_process(pid_t pid, double limit, int include_children) { //slice of the slot in which the process is allowed to run struct timespec twork; @@ -226,39 +191,17 @@ void limit_process(pid_t pid, double limit, int ignore_children) increase_priority(); //build the family - create_process_family(&pf, pid); - if (ignore_children) { - //delete any process with a different pid than the father - for (node=pf.members.first; node!=NULL; node=node->next) { - struct process *proc = (struct process*)(node->data); - if (proc->pid != pid) - remove_process_from_family(&pf, proc->pid); - } - } - - if (!ignore_children && verbose) printf("Members in the family owned by %d: %d\n", pf.father, pf.members.count); + init_process_group(&pgroup, pid, include_children); + + if (verbose) printf("Members in the process group owned by %d: %d\n", pgroup.target_pid, pgroup.proclist->count); //rate at which we are keeping active the processes (range 0-1) //1 means that the process are using all the twork slice double workingrate = -1; while(1) { - if (!ignore_children && c%10==0) { - //update the process family (checks only for new members) - int new_children = update_process_family(&pf); - if (verbose && new_children) { - printf("%d new children processes detected (", new_children); - int j; - node = pf.members.last; - for (j=0; j<new_children; j++) { - printf("%d", ((struct process*)(node->data))->pid); - if (j<new_children-1) printf(" "); - node = node->previous; - } - printf(")\n"); - } - } + update_process_group(&pgroup); - if (pf.members.count==0) { + if (pgroup.proclist->count==0) { if (verbose) printf("No more processes.\n"); break; } @@ -268,24 +211,12 @@ void limit_process(pid_t pid, double limit, int ignore_children) double pcpu = -1; //estimate how much the controlled processes are using the cpu in the working interval - for (node=pf.members.first; node!=NULL; node=node->next) { + for (node = pgroup.proclist->first; node != NULL; node = node->next) { struct process *proc = (struct process*)(node->data); - if (proc->is_zombie) { - //process is zombie, remove it from family - fprintf(stderr,"Process %d is zombie!\n", proc->pid); - remove_process_from_family(&pf, proc->pid); + if (proc->cpu_usage < 0) { continue; } - if (process_monitor(proc) != 0) { - //process is dead, remove it from family - if (verbose) fprintf(stderr,"Process %d dead!\n", proc->pid); - remove_process_from_family(&pf, proc->pid); - continue; - } - if (proc->cpu_usage<0) { - continue; - } - if (pcpu<0) pcpu = 0; + if (pcpu < 0) pcpu = 0; pcpu += proc->cpu_usage; } @@ -294,52 +225,52 @@ void limit_process(pid_t pid, double limit, int ignore_children) //it's the 1st cycle, initialize workingrate pcpu = limit; workingrate = limit; - twork.tv_nsec = TIME_SLOT*limit*1000; + twork.tv_nsec = TIME_SLOT * limit * 1000; } else { //adjust workingrate workingrate = MIN(workingrate / pcpu * limit, 1); - twork.tv_nsec = TIME_SLOT*1000*workingrate; + twork.tv_nsec = TIME_SLOT * 1000 * workingrate; } - tsleep.tv_nsec = TIME_SLOT*1000-twork.tv_nsec; + tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec; if (verbose) { if (c%200==0) printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n"); if (c%10==0 && c>0) - printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100); + printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n", pcpu*100, twork.tv_nsec/1000, tsleep.tv_nsec/1000, workingrate*100); } //resume processes - for (node=pf.members.first; node!=NULL; node=node->next) { + for (node = pgroup.proclist->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 - if (verbose) fprintf(stderr,"Process %d dead!\n", proc->pid); - remove_process_from_family(&pf, proc->pid); + if (verbose) fprintf(stderr, "Process %d dead!\n", proc->pid); + //remove_process_from_family(&pf, proc->pid); } } //now processes are free to run (same working slice for all) gettimeofday(&startwork, NULL); - nanosleep(&twork,NULL); + nanosleep(&twork, NULL); gettimeofday(&endwork, NULL); - workingtime = timediff(&endwork,&startwork); + workingtime = timediff(&endwork, &startwork); - long delay = workingtime-twork.tv_nsec/1000; + long delay = workingtime - twork.tv_nsec/1000; if (c>0 && delay>10000) { //delay is too much! signal to user? //fprintf(stderr, "%d %ld us\n", c, delay); } if (tsleep.tv_nsec>0) { - //stop only if tsleep>0, instead it's useless - for (node=pf.members.first; node!=NULL; node=node->next) { + //stop only if tsleep>0 + for (node = pgroup.proclist->first; node != NULL; node = node->next) { struct process *proc = (struct process*)(node->data); if (kill(proc->pid,SIGSTOP)!=0) { //process is dead, remove it from family if (verbose) fprintf(stderr,"Process %d dead!\n", proc->pid); - remove_process_from_family(&pf, proc->pid); + //remove_process_from_family(&pf, proc->pid); } } //now the processes are sleeping @@ -347,7 +278,7 @@ void limit_process(pid_t pid, double limit, int ignore_children) } c++; } - cleanup_process_family(&pf); + close_process_group(&pgroup); } int main(int argc, char **argv) { @@ -361,8 +292,8 @@ int main(int argc, char **argv) { int ignore_children = 0; //get program name - char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0])); - program_name = p==NULL?argv[0]:(p+1); + char *p = (char*)memrchr(argv[0], (unsigned int)'/', strlen(argv[0])); + program_name = p==NULL ? argv[0] : (p+1); //get current pid cpulimit_pid = getpid(); //get cpu count @@ -422,12 +353,12 @@ int main(int argc, char **argv) { } } while(next_option != -1); - if (pid_ok && (pid<=1 || pid>=65536)) { + if (pid_ok && (pid <= 1 || pid >= 65536)) { fprintf(stderr,"Error: Invalid value for argument PID\n"); print_usage(stderr, 1); exit(1); } - if (pid!=0) { + if (pid != 0) { lazy = 1; } @@ -436,14 +367,14 @@ int main(int argc, char **argv) { print_usage(stderr, 1); exit(1); } - double limit = perclimit/100.0; + double limit = perclimit / 100.0; if (limit<0 || limit >NCPU) { fprintf(stderr,"Error: limit must be in the range 0-%d00\n", NCPU); print_usage(stderr, 1); exit(1); } - int command_mode = optind<argc; + int command_mode = optind < argc; if (exe_ok + pid_ok + command_mode == 0) { fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n"); print_usage(stderr, 1); @@ -463,19 +394,12 @@ int main(int argc, char **argv) { //print the number of available cpu if (verbose) printf("%d cpu detected\n", NCPU); -#ifdef __linux__ - if (!check_proc()) { - fprintf(stderr, "procfs is not mounted!\nAborting\n"); - exit(-2); - } -#endif - if (command_mode) { int i; //executable file const char *cmd = argv[optind]; //command line arguments - char **cmd_args = (char**)malloc((argc-optind+1)*sizeof(char*)); + char **cmd_args = (char**)malloc((argc-optind + 1) * sizeof(char*)); if (cmd_args==NULL) exit(2); for (i=0; i<argc-optind; i++) { cmd_args[i] = argv[i+optind]; @@ -494,7 +418,14 @@ int main(int argc, char **argv) { if (child < 0) { exit(EXIT_FAILURE); } - else if (child > 0) { + else if (child == 0) { + //target process code + int ret = execvp(cmd, cmd_args); + //if we are here there was an error, show it + perror("Error"); + exit(ret); + } + else { //parent code free(cmd_args); int limiter = fork(); @@ -521,13 +452,6 @@ int main(int argc, char **argv) { exit(0); } } - else { - //target process code - int ret = execvp(cmd, cmd_args); - //if we are here there was an error, show it - perror("Error"); - exit(ret); - } } while(1) { @@ -535,7 +459,7 @@ int main(int argc, char **argv) { pid_t ret = 0; if (pid_ok) { //search by pid - ret = look_for_process_by_pid(pid); + ret = find_process_by_pid(pid); if (ret == 0) { printf("No process found\n"); } @@ -545,7 +469,7 @@ int main(int argc, char **argv) { } else { //search by file or path name - ret = look_for_process_by_name(exe); + ret = find_process_by_name(exe); if (ret == 0) { printf("No process found\n"); } @@ -558,7 +482,7 @@ int main(int argc, char **argv) { } if (ret > 0) { if (ret == cpulimit_pid) { - printf("Process %d is cpulimit itself! Aborting to avoid deadlock\n", ret); + printf("Target process %d is cpulimit itself! Aborting because it makes no sense\n", ret); exit(1); } printf("Process %d found\n", pid); @@ -127,7 +127,7 @@ void *locate_elem(struct list *l,void *elem) { return(xlocate_elem(l,elem,0,0)); } -void flush_list(struct list *l) { +void clear_list(struct list *l) { struct list_node *tmp; while(l->first!=EMPTYLIST) { tmp=l->first; @@ -133,7 +133,7 @@ void *locate_elem(struct list *l,void *elem); /* * Delete all the elements in the list */ -void flush_list(struct list *l); +void clear_list(struct list *l); /* * Delete every element in the list, and free the memory pointed by all the node data diff --git a/src/process.c b/src/process.c deleted file mode 100644 index 398d9e9..0000000 --- a/src/process.c +++ /dev/null @@ -1,233 +0,0 @@ -/** - * - * cpulimit - a cpu limiter for Linux - * - * Copyright (C) 2005-2008, by: Angelo Marletta <marlonx80@hotmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU 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. - */ - -//TODO: add documentation to public functions - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <signal.h> -#include <time.h> -#include <sys/utsname.h> - -#include "process.h" - -#ifdef __APPLE__ -#include <sys/sysctl.h> -#include <errno.h> -#endif - -#ifdef __linux__ -int get_proc_info(struct process *p, pid_t pid) { -/* static char statfile[20]; - static char buffer[64]; - int ret; - sprintf(statfile, "/proc/%d/stat", pid); - FILE *fd = fopen(statfile, "r"); - if (fd==NULL) return -1; - fgets(buffer, sizeof(buffer), fd); - fclose(fd); - - char state; - - int n = sscanf(buffer, "%d %s %c %d %d %d %d %d " - "%lu %lu %lu %lu %lu %lu %lu " - "%ld %ld %ld %ld %ld %ld " - "%lu ", - &p->pid, - &p->command, - &state, - NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, - &utime,&stime,&cutime,&cstime, - NULL,NULL,NULL,NULL, - &starttime, - );*/ - return 0; -} -#elif defined __APPLE__ -int get_proc_info(struct process *p, pid_t pid) { - int err; - struct kinfo_proc *result = NULL; - size_t length; - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; - - /* We start by calling sysctl with result == NULL and length == 0. - That will succeed, and set length to the appropriate length. - We then allocate a buffer of that size and call sysctl again - with that buffer. - */ - length = 0; - err = sysctl(mib, 4, NULL, &length, NULL, 0); - if (err == -1) { - err = errno; - } - if (err == 0) { - result = malloc(length); - err = sysctl(mib, 4, result, &length, NULL, 0); - if (err == -1) - err = errno; - if (err == ENOMEM) { - free(result); /* clean up */ - result = NULL; - } - } - - p->pid = result->kp_proc.p_pid; - p->ppid = result->kp_eproc.e_ppid; - p->starttime = result->kp_proc.p_starttime.tv_sec; - p->last_jiffies = result->kp_proc.p_cpticks; - //p_pctcpu - - return 0; -} -#endif - -// returns the start time of a process (used with pid to identify a process) -static int get_starttime(pid_t pid) -{ -#ifdef __linux__ - char file[20]; - char buffer[1024]; - sprintf(file, "/proc/%d/stat", pid); - FILE *fd = fopen(file, "r"); - if (fd==NULL) return -1; - if (fgets(buffer, sizeof(buffer), fd) == NULL) return -1; - 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)); - //start time of the process - int time = atoi(p+1); - return time; -#elif defined __APPLE__ - struct process proc; - get_proc_info(&proc, pid); - return proc.starttime; -#endif -} - -static int get_jiffies(struct process *proc) { -#ifdef __linux__ - FILE *f = fopen(proc->stat_file, "r"); - if (f==NULL) return -1; - if (fgets(proc->buffer, sizeof(proc->buffer),f) == NULL) return -1; - fclose(f); - char *p = proc->buffer; - p = memchr(p+1,')', sizeof(proc->buffer) - (p-proc->buffer)); - int sp = 12; - while (sp--) - p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer)); - //user mode jiffies - int utime = atoi(p+1); - p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer)); - //kernel mode jiffies - int ktime = atoi(p+1); - return utime+ktime; -#elif defined __APPLE__ - struct process proc2; - get_proc_info(&proc2, proc->pid); - return proc2.last_jiffies; -#endif -} - -//return t1-t2 in microseconds (no overflow checks, so better watch out!) -static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2) -{ - return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); -} - -/*static int*/ int process_update(struct process *proc) { - //TODO: get any process statistic here - //check that starttime is not changed(?), update jiffies, parent, zombie status - return 0; -} - -int process_init(struct process *proc, int pid) -{ - //general members - proc->pid = pid; - proc->starttime = get_starttime(pid); - proc->cpu_usage = 0; - memset(&(proc->last_sample), 0, sizeof(struct timeval)); - proc->last_jiffies = -1; - //system dependent members -#ifdef __linux__ -//TODO: delete these members for the sake of portability? - //test /proc file descriptor for reading - sprintf(proc->stat_file, "/proc/%d/stat", pid); - FILE *fd = fopen(proc->stat_file, "r"); - if (fd == NULL) return 1; - fclose(fd); -#endif - return 0; -} - -//parameter in range 0-1 -#define ALFA 0.08 - -int process_monitor(struct process *proc) -{ - int j = get_jiffies(proc); - if (j<0) return -1; //error retrieving jiffies count (maybe the process is dead) - struct timeval now; - gettimeofday(&now, NULL); - if (proc->last_jiffies==-1) { - //store current time - proc->last_sample = now; - //store current jiffies - proc->last_jiffies = j; - //it's the first sample, cannot figure out the cpu usage - proc->cpu_usage = -1; - return 0; - } - //time from previous sample (in ns) - long dt = timediff(&now, &(proc->last_sample)); - //how many jiffies in dt? - double max_jiffies = dt * HZ / 1000000.0; - double sample = (j - proc->last_jiffies) / max_jiffies; - if (proc->cpu_usage == -1) { - //initialization - proc->cpu_usage = sample; - } - else { - //usage adjustment - proc->cpu_usage = (1-ALFA) * proc->cpu_usage + ALFA * sample; - } - - //store current time - proc->last_sample = now; - //store current jiffies - proc->last_jiffies = j; - - return 0; -} - -int process_close(struct process *proc) -{ - if (kill(proc->pid,SIGCONT)!=0) { - fprintf(stderr,"Process %d is already dead!\n", proc->pid); - } - proc->pid = 0; - return 0; -} diff --git a/src/process_group.c b/src/process_group.c new file mode 100644 index 0000000..71f180b --- /dev/null +++ b/src/process_group.c @@ -0,0 +1,203 @@ +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include <sys/time.h> +#include <signal.h> + +#include <assert.h> + +#include "process_iterator.h" +#include "process_group.h" +#include "list.h" + +// look for a process by pid +// search_pid : pid of the wanted process +// return: pid of the found process, if successful +// negative pid, if the process does not exist or if the signal fails +int find_process_by_pid(pid_t pid) +{ + return (kill(pid,0)==0) ? pid : -pid; +} + +// 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 find_process_by_name(const char *process_name) +{ + //whether the variable process_name is the absolute path or not + int is_absolute_path = process_name[0] == '/'; + //flag indicating if the a process with given name was found + int found = 0; + pid_t pid = -1; + + //process iterator + struct process_iterator it; + struct process proc; + struct process_filter filter; + filter.pid = 0; + filter.include_children = 0; + init_process_iterator(&it, &filter); + while (get_next_process(&it, &proc) != -1) + { + pid = proc.pid; + int size = strlen(proc.command); + + found = 0; + if (is_absolute_path && strncmp(proc.command, process_name, size)==0 && size==strlen(process_name)) { + //process found + found = 1; + } + else { + //process found + if (strncmp(proc.command + size - strlen(process_name), process_name, strlen(process_name))==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 (close_process_iterator(&it) != 1) exit(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 + exit(2); +} + +int init_process_group(struct process_group *pgroup, int target_pid, int include_children) +{ + //hashtable initialization + memset(&pgroup->proctable, 0, sizeof(pgroup->proctable)); + pgroup->target_pid = target_pid; + pgroup->include_children = include_children; + pgroup->proclist = (struct list*)malloc(sizeof(struct list)); + init_list(pgroup->proclist, 4); + memset(&pgroup->last_update, 0, sizeof(pgroup->last_update)); + update_process_group(pgroup); + return 0; +} + +int close_process_group(struct process_group *pgroup) +{ + int i; + int size = sizeof(pgroup->proctable) / sizeof(struct process*); + for (i=0; i<size; i++) { + if (pgroup->proctable[i] != NULL) { + //free() history for each process + destroy_list(pgroup->proctable[i]); + free(pgroup->proctable[i]); + pgroup->proctable[i] = NULL; + } + } + clear_list(pgroup->proclist); + free(pgroup->proclist); + pgroup->proclist = NULL; + return 0; +} + +void remove_terminated_processes(struct process_group *pgroup) +{ + //TODO +} + +//return t1-t2 in microseconds (no overflow checks, so better watch out!) +static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2) +{ + return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); +} + +//parameter in range 0-1 +#define ALFA 0.08 +#define MIN_DT 20 + +void update_process_group(struct process_group *pgroup) +{ + struct process_iterator it; + struct process tmp_process; + struct process_filter filter; + struct timeval now; + gettimeofday(&now, NULL); + //time elapsed from previous sample (in ms) + long dt = timediff(&now, &pgroup->last_update) / 1000; + filter.pid = pgroup->target_pid; + filter.include_children = pgroup->include_children; + init_process_iterator(&it, &filter); + clear_list(pgroup->proclist); + init_list(pgroup->proclist, 4); + + while (get_next_process(&it, &tmp_process) != -1) + { +// struct timeval t; +// gettimeofday(&t, NULL); +// printf("T=%ld.%ld PID=%d PPID=%d START=%d CPUTIME=%d\n", t.tv_sec, t.tv_usec, tmp_process.pid, tmp_process.ppid, tmp_process.starttime, tmp_process.cputime); + int hashkey = pid_hashfn(tmp_process.pid + tmp_process.starttime); + if (pgroup->proctable[hashkey] == NULL) + { + //empty bucket + pgroup->proctable[hashkey] = malloc(sizeof(struct list)); + struct process *new_process = malloc(sizeof(struct process)); + tmp_process.cpu_usage = -1; + memcpy(new_process, &tmp_process, sizeof(struct process)); + init_list(pgroup->proctable[hashkey], 4); + add_elem(pgroup->proctable[hashkey], new_process); + add_elem(pgroup->proclist, new_process); + } + else + { + //existing bucket + struct process *p = (struct process*)locate_elem(pgroup->proctable[hashkey], &tmp_process); + if (p == NULL) + { + //process is new. add it + struct process *new_process = malloc(sizeof(struct process)); + tmp_process.cpu_usage = -1; + memcpy(new_process, &tmp_process, sizeof(struct process)); + add_elem(pgroup->proctable[hashkey], new_process); + add_elem(pgroup->proclist, new_process); + } + else + { + assert(tmp_process.pid == p->pid); + assert(tmp_process.ppid == p->ppid); + assert(tmp_process.starttime == p->starttime); + add_elem(pgroup->proclist, p); + if (dt < MIN_DT) continue; + //process exists. update CPU usage + double sample = 1.0 * (tmp_process.cputime - p->cputime) / dt; + if (p->cpu_usage == -1) { + //initialization + p->cpu_usage = sample; + } + else { + //usage adjustment + p->cpu_usage = (1.0-ALFA) * p->cpu_usage + ALFA * sample; + } + p->cputime = tmp_process.cputime; + } + } + } + close_process_iterator(&it); + if (dt < MIN_DT) return; + pgroup->last_update = now; +} diff --git a/src/process_group.h b/src/process_group.h new file mode 100644 index 0000000..100711d --- /dev/null +++ b/src/process_group.h @@ -0,0 +1,53 @@ +/** + * + * cpulimit - a cpu limiter for Linux + * + * Copyright (C) 2005-2012, by: Angelo Marletta <marlonx80@hotmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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. + */ + +#ifndef __PROCESS_GROUP_H + +#define __PROCESS_GROUP_H + +#include "process_iterator.h" + +#include "list.h" + +#define PIDHASH_SZ 1024 +#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) + +struct process_group +{ + //hashtable with all the processes (array of struct list of struct process) + struct list *proctable[PIDHASH_SZ]; + struct list *proclist; + pid_t target_pid; + int include_children; + struct timeval last_update; +}; + +int init_process_group(struct process_group *pgroup, int target_pid, int include_children); + +void update_process_group(struct process_group *pgroup); + +int close_process_group(struct process_group *pgroup); + +int find_process_by_pid(pid_t pid); + +int find_process_by_name(const char *process_name); + +#endif diff --git a/src/process_iterator.c b/src/process_iterator.c new file mode 100644 index 0000000..0e31c81 --- /dev/null +++ b/src/process_iterator.c @@ -0,0 +1,26 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/procfs.h> +#include <time.h> +#include "process_iterator.h" + +//See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 + +#ifdef __linux__ + +#include "process_iterator_linux.c" + +#elif defined __FreeBSD__ + +#include "process_iterator_freebsd.c" + +#elif defined __APPLE__ + +#include "process_iterator_apple.c" + +#else + +#error Platform not supported + +#endif diff --git a/src/process.h b/src/process_iterator.h index 0280ad6..82fadac 100644 --- a/src/process.h +++ b/src/process_iterator.h @@ -2,7 +2,7 @@ * * cpulimit - a cpu limiter for Linux * - * Copyright (C) 2005-2008, by: Angelo Marletta <marlonx80@hotmail.com> + * Copyright (C) 2005-2012, by: Angelo Marletta <marlonx80@hotmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,13 +19,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef __PROCESS_H +#ifndef __PROCESS_ITERATOR_H -#define __PROCESS_H +#define __PROCESS_ITERATOR_H -#include <sys/time.h> #include <unistd.h> #include <limits.h> +#include <dirent.h> //USER_HZ detection, from openssl code #ifndef HZ @@ -45,44 +45,51 @@ # endif #endif +#ifdef __FreeBSD__ +#include <kvm.h> +#endif + // process descriptor struct process { //pid of the process pid_t pid; - //pid of the process + //ppid of the process pid_t ppid; - //start time + //start time (unix timestamp) int starttime; - //is member of the family? - int member; //TODO: delete this field - //total number of jiffies used by the process at time last_sample - int last_jiffies; - //timestamp when last_jiffies and cpu_usage was calculated - struct timeval last_sample; + //cputime used by the process (in milliseconds) + int cputime; //actual cpu usage estimation (value in range 0-1) double cpu_usage; - //1 if the process is zombie - int is_zombie; //absolute path of the executable file char command[PATH_MAX+1]; +}; - //system-dependent members -//TODO: delete these members for the sake of portability? -#ifdef __linux__ - //preallocate buffers for performance - //name of /proc/PID/stat file - char stat_file[20]; - //read buffer for /proc filesystem - char buffer[1024]; -#endif +struct process_filter { + int pid; + int include_children; + char program_name[PATH_MAX+1]; }; -int get_proc_info(struct process *p, pid_t pid); +struct process_iterator { +#ifdef __linux__ + DIR *dip; + int boot_time; +#elif defined __FreeBSD__ + kvm_t *kd; + struct kinfo_proc *procs; + int count; + int i; +#elif defined __APPLE__ + struct kinfo_proc *proclist; +#endif + struct process_filter *filter; +}; -int process_init(struct process *proc, pid_t pid); +int init_process_iterator(struct process_iterator *i, struct process_filter *filter); -int process_monitor(struct process *proc); +int get_next_process(struct process_iterator *i, struct process *p); -int process_close(struct process *proc); +int close_process_iterator(struct process_iterator *i); #endif diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c new file mode 100644 index 0000000..ee574e7 --- /dev/null +++ b/src/process_iterator_apple.c @@ -0,0 +1,77 @@ +int init_process_iterator(struct process_iterator *it) { + return 0; +} + +int get_next_process(struct process_iterator *it, struct process *p) { + return -1; +} + +int close_process_iterator(struct process_iterator *it) { + return 0; +} + + // int err; + // struct kinfo_proc *result = NULL; + // size_t length; + // int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; + + // /* We start by calling sysctl with result == NULL and length == 0. + // That will succeed, and set length to the appropriate length. + // We then allocate a buffer of that size and call sysctl again + // with that buffer. + // */ + // length = 0; + // err = sysctl(mib, 4, NULL, &length, NULL, 0); + // if (err == -1) { + // err = errno; + // } + // if (err == 0) { + // result = malloc(length); + // err = sysctl(mib, 4, result, &length, NULL, 0); + // if (err == -1) + // err = errno; + // if (err == ENOMEM) { + // free(result); /* clean up */ + // result = NULL; + // } + // } + + // i->proclist = result; + // i->count = err == 0 ? length / sizeof *result : 0; + // i->c = 0; + +// int get_proc_info(struct process *p, pid_t pid) { +// int err; +// struct kinfo_proc *result = NULL; +// size_t length; +// int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + +// /* We start by calling sysctl with result == NULL and length == 0. +// That will succeed, and set length to the appropriate length. +// We then allocate a buffer of that size and call sysctl again +// with that buffer. +// */ +// length = 0; +// err = sysctl(mib, 4, NULL, &length, NULL, 0); +// if (err == -1) { +// err = errno; +// } +// if (err == 0) { +// result = malloc(length); +// err = sysctl(mib, 4, result, &length, NULL, 0); +// if (err == -1) +// err = errno; +// if (err == ENOMEM) { +// free(result); /* clean up */ +// result = NULL; +// } +// } + +// p->pid = result->kp_proc.p_pid; +// p->ppid = result->kp_eproc.e_ppid; +// p->starttime = result->kp_proc.p_starttime.tv_sec; +// p->last_jiffies = result->kp_proc.p_cpticks; +// //p_pctcpu + +// return 0; +// } diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c new file mode 100644 index 0000000..eb6cfaa --- /dev/null +++ b/src/process_iterator_freebsd.c @@ -0,0 +1,95 @@ +#include <sys/sysctl.h> +#include <sys/user.h> +#include <fcntl.h> +#include <paths.h> + +int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { + char errbuf[_POSIX2_LINE_MAX]; + it->i = 0; + /* Open the kvm interface, get a descriptor */ + if ((it->kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf)) == NULL) { + fprintf(stderr, "kvm_open: %s\n", errbuf); + return -1; + } + /* Get the list of processes. */ + if ((it->procs = kvm_getprocs(it->kd, KERN_PROC_PROC, 0, &it->count)) == NULL) { + kvm_close(it->kd); + fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); + return -1; + } + it->filter = filter; + return 0; +} + +static void kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) +{ + proc->pid = kproc->ki_pid; + proc->ppid = kproc->ki_ppid; + proc->cputime = kproc->ki_runtime / 1000; + proc->starttime = kproc->ki_start.tv_sec; + char **args = kvm_getargv(kd, kproc, sizeof(proc->command)); + if (args != NULL) + { + memcpy(proc->command, args[0], strlen(args[0]) + 1); + } +} + +static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) +{ + int count; + struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); + if (count == 0 || kproc == NULL) + { + fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(kd)); + return -1; + } + kproc2proc(kd, kproc, process); + return 0; +} + +int get_next_process(struct process_iterator *it, struct process *p) { + if (it->i == it->count) + { + return -1; + } + if (it->filter->pid > 0 && !it->filter->include_children) + { + get_single_process(it->kd, it->filter->pid, p); + it->i = it->count = 1; + return 0; + } + while (it->i < it->count) + { + struct kinfo_proc *kproc = &(it->procs[it->i]); + if (kproc->ki_flag & P_SYSTEM) + { + // skip system processes + it->i++; + continue; + } + if (it->filter->pid > 0 && it->filter->include_children) + { + kproc2proc(it->kd, kproc, p); + it->i++; + if (p->pid != it->filter->pid && p->ppid != it->filter->pid) + continue; + return 0; + } + else if (it->filter->pid == 0) + { + kproc2proc(it->kd, kproc, p); + it->i++; + return 0; + } + } + return -1; +} + +int close_process_iterator(struct process_iterator *it) { + if (kvm_close(it->kd) == -1) { + fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); + return -1; + } + return 0; +} + diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c new file mode 100644 index 0000000..a15be45 --- /dev/null +++ b/src/process_iterator_linux.c @@ -0,0 +1,159 @@ +#include <sys/vfs.h> + +static int get_boot_time() +{ + int uptime; + FILE *fp = fopen ("/proc/uptime", "r"); + if (fp != NULL) + { + char buf[BUFSIZ]; + char *b = fgets(buf, BUFSIZ, fp); + if (b == buf) + { + char *end_ptr; + double upsecs = strtod(buf, &end_ptr); + uptime = (int)upsecs; + } + fclose (fp); + } + time_t now = time(NULL); + return now - uptime; +} + +static int check_proc() +{ + struct statfs mnt; + if (statfs("/proc", &mnt) < 0) + return 0; + if (mnt.f_type!=0x9fa0) + return 0; + return 1; +} + +int init_process_iterator(struct process_iterator *it, struct process_filter *filter) +{ + if (!check_proc()) { + fprintf(stderr, "procfs is not mounted!\nAborting\n"); + exit(-2); + } + //open a directory stream to /proc directory + if ((it->dip = opendir("/proc")) == NULL) + { + perror("opendir"); + return -1; + } + it->filter = filter; + it->boot_time = get_boot_time(); + return 0; +} + +static int read_process_info(pid_t pid, struct process *p) +{ + static char buffer[1024]; + static char statfile[32]; + static char exefile[1024]; + p->pid = pid; + //read stat file + sprintf(statfile, "/proc/%d/stat", p->pid); + FILE *fd = fopen(statfile, "r"); + if (fd==NULL) return -1; + if (fgets(buffer, sizeof(buffer), fd)==NULL) { + fclose(fd); + return -1; + } + fclose(fd); + char *token = strtok(buffer, " "); + int i; + for (i=0; i<3; i++) token = strtok(NULL, " "); + p->ppid = atoi(token); + for (i=0; i<10; i++) + token = strtok(NULL, " "); + p->cputime = atoi(token) * 1000 / HZ; + token = strtok(NULL, " "); + p->cputime += atoi(token) * 1000 / HZ; + for (i=0; i<7; i++) + token = strtok(NULL, " "); + p->starttime = atoi(token) / sysconf(_SC_CLK_TCK); + //read command line + sprintf(exefile,"/proc/%d/cmdline", p->pid); + fd = fopen(exefile, "r"); + if (fgets(buffer, sizeof(buffer), fd)==NULL) { + fclose(fd); + return -1; + } + fclose(fd); + sscanf(buffer, "%s", p->command); + return 0; +} + +static pid_t getppid_of(pid_t pid) +{ + char statfile[20]; + char buffer[1024]; + sprintf(statfile, "/proc/%d/stat", pid); + FILE *fd = fopen(statfile, "r"); + if (fd==NULL) return -1; + if (fgets(buffer, sizeof(buffer), fd)==NULL) { + fclose(fd); + return -1; + } + fclose(fd); + char *token = strtok(buffer, " "); + int i; + for (i=0; i<3; i++) token = strtok(NULL, " "); + return atoi(token); +} + +static int is_child_of(pid_t child_pid, pid_t parent_pid) +{ + int ppid = child_pid; + while(ppid > 1 && ppid != parent_pid) { + ppid = getppid_of(ppid); + } + return ppid == parent_pid; +} + +int get_next_process(struct process_iterator *it, struct process *p) +{ + if (it->dip == NULL) + { + //end of processes + return -1; + } + if (it->filter->pid > 0 && !it->filter->include_children) + { + read_process_info(it->filter->pid, p); + //p->starttime += it->boot_time; + closedir(it->dip); + it->dip = NULL; + return 0; + } + struct dirent *dit; + //read in from /proc and seek for process dirs + while ((dit = readdir(it->dip)) != NULL) { + if(strtok(dit->d_name, "0123456789") != NULL) + continue; + p->pid = atoi(dit->d_name); + if (it->filter->pid > 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) continue; + read_process_info(p->pid, p); + //p->starttime += it->boot_time; + break; + } + if (dit == NULL) + { + //end of processes + closedir(it->dip); + it->dip = NULL; + return -1; + } + return 0; +} + +int close_process_iterator(struct process_iterator *it) { + if (it->dip != NULL && closedir(it->dip) == -1) { + perror("closedir"); + return 1; + } + it->dip = NULL; + return 0; +} diff --git a/src/procutils.c b/src/procutils.c deleted file mode 100644 index fb7aaae..0000000 --- a/src/procutils.c +++ /dev/null @@ -1,463 +0,0 @@ -/** - * - * cpulimit - a cpu limiter for Linux - * - * Copyright (C) 2005-2008, by: Angelo Marletta <marlonx80@hotmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU 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. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <signal.h> -#include <string.h> -#include <limits.h> -#include <fcntl.h> -#include <sys/utsname.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include "procutils.h" - -#ifdef __APPLE__ -#include <sys/sysctl.h> -#include <errno.h> -#endif - -/* PROCESS STATISTICS FUNCTIONS */ - -//deprecated -// returns pid of the parent process -static pid_t getppid_of(pid_t pid) -{ -#ifdef __linux__ - char file[20]; - char buffer[1024]; - sprintf(file, "/proc/%d/stat", pid); - FILE *fd = fopen(file, "r"); - if (fd==NULL) return -1; - if (fgets(buffer, sizeof(buffer), fd)==NULL) return -1; - fclose(fd); - char *p = buffer; - p = memchr(p+1,')', sizeof(buffer) - (p-buffer)); - int sp = 2; - while (sp--) - p = memchr(p+1,' ',sizeof(buffer) - (p-buffer)); - //pid of the parent process - pid_t ppid = atoi(p+1); - return ppid; -#elif defined __APPLE__ - struct process p; - get_proc_info(&p, pid); - return p.ppid; -#endif -} - -#ifdef __linux__ -// detects whether a process is a kernel thread or not -static int is_kernel_thread(pid_t 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 -1; - if (fgets(buffer, sizeof(buffer), fd)==NULL) return -1; - ret = strncmp(buffer,"0 0 0",3)==0; - fclose(fd); - return ret; -} -#endif - -//deprecated -// returns 1 if pid is an existing pid, 0 otherwise -static int process_exists(pid_t pid) { -#ifdef __linux__ - static char procdir[20]; - struct stat procstat; - sprintf(procdir, "/proc/%d", pid); - return stat(procdir, &procstat)==0; -#elif defined __APPLE__ - struct process p; - return get_proc_info(&p, pid)==0; -#endif -} - -/* PID HASH FUNCTIONS */ - -static int hash_process(struct process_family *f, struct process *p) -{ - int ret; - struct list **l = &(f->proctable[pid_hashfn(p->pid)]); - if (*l == NULL) { - //there is no process in this hashtable item - //allocate the list - *l = (struct list*)malloc(sizeof(struct list)); - init_list(*l, 4); - add_elem(*l, p); - ret = 0; - f->count++; - } - 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)); - free(p); - p = NULL; - ret = 1; - } - else { - //add new process - add_elem(*l, p); - ret = 0; - f->count++; - } - } - return ret; -} - -static void unhash_process(struct process_family *f, pid_t pid) { - //remove process from hashtable - struct list **l = &(f->proctable[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 *seek_process(struct process_family *f, pid_t pid) -{ - struct list **l = &(f->proctable[pid_hashfn(pid)]); - return (*l != NULL) ? (struct process*)locate_elem(*l, &pid) : NULL; -} - -/* PROCESS ITERATOR STUFF */ - -// creates an object that browse all running processes -int init_process_iterator(struct process_iterator *i) { -#ifdef __linux__ - //open a directory stream to /proc directory - if ((i->dip = opendir("/proc")) == NULL) { - perror("opendir"); - return -1; - } -#elif defined __APPLE__ - - int err; - struct kinfo_proc *result = NULL; - size_t length; - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; - - /* We start by calling sysctl with result == NULL and length == 0. - That will succeed, and set length to the appropriate length. - We then allocate a buffer of that size and call sysctl again - with that buffer. - */ - length = 0; - err = sysctl(mib, 4, NULL, &length, NULL, 0); - if (err == -1) { - err = errno; - } - if (err == 0) { - result = malloc(length); - err = sysctl(mib, 4, result, &length, NULL, 0); - if (err == -1) - err = errno; - if (err == ENOMEM) { - free(result); /* clean up */ - result = NULL; - } - } - - i->procList = result; - i->count = err == 0 ? length / sizeof *result : 0; - i->c = 0; - -#endif - i->current = (struct process*)calloc(1, sizeof(struct process)); - return 0; -} - -int close_process_iterator(struct process_iterator *i) { -#ifdef __linux__ - if (closedir(i->dip) == -1) { - perror("closedir"); - return 1; - } -#elif defined __APPLE__ - //TODO -#endif - return 0; -} - -// reads the next user process from /process -// automatic closing if the end of the list is reached -int read_next_process(struct process_iterator *i) { -#ifdef __linux__ - pid_t pid = 0; -//TODO read this to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 - //read in from /proc and seek for process dirs - while ((i->dit = readdir(i->dip)) != NULL) { - if(strtok(i->dit->d_name, "0123456789") != NULL) - continue; - pid = atoi(i->dit->d_name); - if (is_kernel_thread(pid)) - continue; - //return the first found process - break; - } - if (pid == 0) { - //no more processes - closedir(i->dip); - free(i->current); - i->current = NULL; - return -1; - } - //read the executable link - char statfile[20]; - sprintf(statfile,"/proc/%d/cmdline",pid); - FILE *fd = fopen(statfile, "r"); - if (fd == NULL) return -1; - char buffer[1024]; - if (fgets(buffer, sizeof(buffer), fd)==NULL) return -2; - fclose(fd); - sscanf(buffer, "%s", (char*)&i->current->command); - i->current->pid = pid; - -#elif defined __APPLE__ - if (i->c >= i->count) { - //no more processes - free(i->procList); - i->procList = NULL; - free(i->current); - i->current = NULL; - return -1; - } - i->current->pid = i->procList[i->c].kp_proc.p_pid; - strncpy(i->current->command, i->procList[i->c].kp_proc.p_comm, MAXCOMLEN); -printf("%d %d %s\n", i->c, i->current->pid, i->current->command);//i->procList[i->c].kp_proc.p_comm); -//printf("%d %d %s\n", i->c, i->current->pid, i->proc[i->c].kp_proc.p_comm); - i->c++; -#endif - return 0; -} - -/* PUBLIC FUNCTIONS */ - -// search for all the processes derived from father and stores them -// in the process family struct -int create_process_family(struct process_family *f, pid_t father) -{ - //process list initialization (4 bytes key) - init_list(&(f->members), 4); - //hashtable initialization - memset(&(f->proctable), 0, sizeof(f->proctable)); - f->count = 0; - f->father = father; - //process iterator - struct process_iterator iter; - init_process_iterator(&iter); - int pid = 0; - while (read_next_process(&iter)==0) { - pid = iter.current->pid; - //check if process belongs to the family - int ppid = pid; - //TODO: optimize adding also these parents, and continue if process is already present - while(ppid!=1 && ppid!=father) { - ppid = getppid_of(ppid); - } - //allocate process descriptor - struct process *p = (struct process*)calloc(1, sizeof(struct process)); - //init process - process_init(p, pid); - if (ppid==1) { - //the init process - p->member = 0; - } - else if (pid != getpid()) { - //add to members (but exclude the current cpulimit process!) - p->member = 1; - add_elem(&(f->members), p); - } - //add to hashtable - hash_process(f, p); - } - return 0; -} - -// 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 update_process_family(struct process_family *f) -{ - int ret = 0; - //process iterator - struct process_iterator iter; - init_process_iterator(&iter); - int pid = 0; - while (read_next_process(&iter)==0) { - pid = iter.current->pid; - struct process *newp = seek_process(f, pid); - if (newp != NULL) continue; //already known //TODO: what if newp is a new process with the same PID?? - //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=seek_process(f, ppid))==NULL) { - ppid = getppid_of(ppid); - } - if (ancestor == NULL) { - //this should never happen! if does, find and correct the bug - fprintf(stderr, "Fatal bug! Process %d is without parent\n", pid); - exit(1); - } - //allocate and insert the process - struct process *p = (struct process*)calloc(1, sizeof(struct process)); - //init process - process_init(p, pid); - if (ancestor->member) { - //add to members - p->member = 1; - add_elem(&(f->members), p); - ret++; - } - else { - //not a member - p->member = 0; - } - //add to hashtable - hash_process(f, p); - } - return ret; -} - -// removes a process from the family by its pid -void remove_process_from_family(struct process_family *f, pid_t 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; - delete_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->proctable) / sizeof(struct process*); - for (i=0; i<size; i++) { - if (f->proctable[i] != NULL) { - //free() history for each process - struct list_node *node = NULL; - for (node=f->proctable[i]->first; node!=NULL; node=node->next) { -// struct process *p = (struct process*)(node->data); -// free(p->history); -// p->history = NULL; - } - destroy_list(f->proctable[i]); - free(f->proctable[i]); - f->proctable[i] = NULL; - } - } - flush_list(&(f->members)); - f->count = 0; - f->father = 0; -} - -// 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(pid_t pid) -{ - if (process_exists(pid)) - return (kill(pid,0)==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_name) -{ - //whether the variable process_name is the absolute path or not - int is_absolute_path = process_name[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); - pid_t pid = 0; - - while (read_next_process(&iter)==0) { - pid = iter.current->pid; - - int size = strlen(iter.current->command); - - found = 0; - if (is_absolute_path && strncmp(iter.current->command, process_name, size)==0 && size==strlen(process_name)) { - //process found - found = 1; - } - else { - //process found - if (strncmp(iter.current->command + size - strlen(process_name), process_name, strlen(process_name))==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 (close_process_iterator(&iter) != 1) exit(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 - exit(2); -} - diff --git a/src/procutils.h b/src/procutils.h index b5a91a3..c39a239 100644 --- a/src/procutils.h +++ b/src/procutils.h @@ -62,7 +62,7 @@ struct process_iterator { struct kinfo_proc *procList; int count; int c; -#elif defined __hpux +#elif defined __FreeBSD__ int count; #endif struct process *current; diff --git a/tests/Makefile b/tests/Makefile index 11f7e2a..ffc80fa 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,12 +1,22 @@ CC?=gcc -CFLAGS?=-Wall -O2 -TARGETS=busy -LIBS?=-lpthread +CFLAGS?=-Wall -g +TARGETS=busy process_iterator_test +SRC=../src +SYSLIBS?=-lpthread +LIBS=$(SRC)/list.o $(SRC)/process_iterator.o $(SRC)/process_group.o +UNAME := $(shell uname) + +ifeq ($(UNAME), FreeBSD) +LIBS+=-lkvm +endif all:: $(TARGETS) -busy: busy.c $(LIBS) - $(CC) -o busy busy.c $(LIBS) $(CFLAGS) +busy: busy.c $(SYSLIBS) + $(CC) -o busy busy.c $(SYSLIBS) $(CFLAGS) + +process_iterator_test: process_iterator_test.c $(LIBS) + $(CC) -I$(SRC) -o process_iterator_test process_iterator_test.c $(LIBS) $(SYSLIBS) $(CFLAGS) clean: rm -f *~ *.o $(TARGETS) diff --git a/tests/busy b/tests/busy Binary files differdeleted file mode 100755 index 1a8a589..0000000 --- a/tests/busy +++ /dev/null diff --git a/tests/busy.c b/tests/busy.c index bb2e2ed..b3afb7c 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -13,7 +13,7 @@ int main(int argc, char **argv) { int i = 0; int num_threads = 1; if (argc == 2) num_threads = atoi(argv[1]); - for (i=0; i<num_threads; i++) + for (i=0; i<num_threads-1; i++) { pthread_t thread; int ret; @@ -23,7 +23,7 @@ int main(int argc, char **argv) { exit(1); } } - printf("Press ENTER to exit..."); - getchar(); + loop(); return 0; -}
\ No newline at end of file +} + diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c new file mode 100644 index 0000000..f5b7586 --- /dev/null +++ b/tests/process_iterator_test.c @@ -0,0 +1,196 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <assert.h> +#include <time.h> +#include <signal.h> +#include <string.h> + +#include <process_iterator.h> +#include <process_group.h> + +volatile sig_atomic_t child; + +void kill_child(int sig) +{ + kill(child, SIGINT); +} + +void test_single_process() +{ + struct process_iterator it; + struct process process; + struct process_filter filter; + int count; + //don't iterate children + filter.pid = getpid(); + filter.include_children = 0; + count = 0; +// time_t now = time(NULL); + init_process_iterator(&it, &filter); + while (get_next_process(&it, &process) == 0) + { + assert(process.pid == getpid()); + assert(process.ppid == getppid()); + assert(process.cputime < 100); +// assert(process.starttime == now || process.starttime == now - 1); + count++; + } + assert(count == 1); + close_process_iterator(&it); + //iterate children + filter.pid = getpid(); + filter.include_children = 0; + count = 0; +// now = time(NULL); + init_process_iterator(&it, &filter); + while (get_next_process(&it, &process) == 0) + { + assert(process.pid == getpid()); + assert(process.ppid == getppid()); + assert(process.cputime < 100); +// assert(process.starttime == now || process.starttime == now - 1); + count++; + } + assert(count == 1); + close_process_iterator(&it); +} + +void test_multiple_process() +{ + struct process_iterator it; + struct process process; + struct process_filter filter; + pid_t child = fork(); + if (child == 0) + { + //child is supposed to be killed by the parent :/ + sleep(1); + exit(1); + } + filter.pid = getpid(); + filter.include_children = 1; + init_process_iterator(&it, &filter); + int count = 0; +// time_t now = time(NULL); + while (get_next_process(&it, &process) == 0) + { + if (process.pid == getpid()) assert(process.ppid == getppid()); + else if (process.pid == child) assert(process.ppid == getpid()); + else assert(0); + assert(process.cputime < 100); +// assert(process.starttime == now || process.starttime == now - 1); + count++; + } + assert(count == 2); + close_process_iterator(&it); + kill(child, SIGINT); +} + +void test_all_processes() +{ + struct process_iterator it; + struct process process; + struct process_filter filter; + filter.pid = 0; + filter.include_children = 0; + init_process_iterator(&it, &filter); + int count = 0; +// time_t now = time(NULL); + while (get_next_process(&it, &process) == 0) + { + if (process.pid == getpid()) + { + assert(process.ppid == getppid()); + assert(process.cputime < 100); +// assert(process.starttime == now || process.starttime == now - 1); + } + count++; + } + assert(count >= 10); + close_process_iterator(&it); +} + +void test_process_group_all() +{ + struct process_group pgroup; + assert(init_process_group(&pgroup, 0, 0) == 0); + update_process_group(&pgroup); + struct list_node *node = NULL; + int count = 0; + for (node=pgroup.proclist->first; node!= NULL; node=node->next) { + count++; + } + assert(count > 10); + update_process_group(&pgroup); + assert(close_process_group(&pgroup) == 0); +} + +void test_process_group_single(int include_children) +{ + struct process_group pgroup; + child = fork(); + if (child == 0) + { + //child is supposed to be killed by the parent :/ + while(1); + exit(1); + } + signal(SIGABRT, &kill_child); + signal(SIGTERM, &kill_child); + assert(init_process_group(&pgroup, child, include_children) == 0); + int i; + double tot_usage = 0; + for (i=0; i<100; i++) + { + update_process_group(&pgroup); + struct list_node *node = NULL; + int count = 0; + for (node=pgroup.proclist->first; node!= NULL; node=node->next) { + struct process *p = (struct process*)(node->data); + assert(p->pid == child); + assert(p->ppid == getpid()); + assert(p->cpu_usage <= 1.2); + tot_usage += p->cpu_usage; + count++; + } + assert(count == 1); + struct timespec interval; + interval.tv_sec = 0; + interval.tv_nsec = 50000000; + nanosleep(&interval, NULL); + } + assert(tot_usage / i < 1.1 && tot_usage / i > 0.8); + assert(close_process_group(&pgroup) == 0); + kill(child, SIGINT); +} + +void test_process_name(const char * command) +{ + struct process_iterator it; + struct process process; + struct process_filter filter; + filter.pid = getpid(); + filter.include_children = 0; + init_process_iterator(&it, &filter); + assert(get_next_process(&it, &process) == 0); + assert(process.pid == getpid()); + assert(process.ppid == getppid()); + assert(strncmp(command, process.command, strlen(process.command)) == 0); + assert(get_next_process(&it, &process) != 0); + close_process_iterator(&it); +} + +int main(int argc, char **argv) +{ +// printf("Pid %d\n", getpid()); + test_single_process(); + test_multiple_process(); + test_all_processes(); + test_process_group_all(); + test_process_group_single(0); + test_process_group_single(1); + test_process_name(argv[0]); + return 0; +} |