int main (int argc, char **argv, char **envp) { mach_port_t boot; error_t err; void *genport; process_t startup_port; mach_port_t startup; struct argp argp = { 0, 0, 0, "Hurd process server" }; argp_parse (&argp, argc, argv, 0, 0, 0); initialize_version_info (); err = task_get_bootstrap_port (mach_task_self (), &boot); assert_perror (err); if (boot == MACH_PORT_NULL) error (2, 0, "proc server can only be run by init during boot"); proc_bucket = ports_create_bucket (); proc_class = ports_create_class (0, 0); generic_port_class = ports_create_class (0, 0); exc_class = ports_create_class (exc_clean, 0); ports_create_port (generic_port_class, proc_bucket, sizeof (struct port_info), &genport); generic_port = ports_get_right (genport); /* Create the initial proc object for init (PID 1). */ init_proc = create_init_proc (); /* Create the startup proc object for /hurd/init (PID 2). */ startup_proc = allocate_proc (MACH_PORT_NULL); startup_proc->p_deadmsg = 1; complete_proc (startup_proc, HURD_PID_STARTUP); /* Create our own proc object. */ self_proc = allocate_proc (mach_task_self ()); assert (self_proc); complete_proc (self_proc, HURD_PID_PROC); startup_port = ports_get_send_right (startup_proc); err = startup_procinit (boot, startup_port, &startup_proc->p_task, &authserver, &_hurd_host_priv, &_hurd_device_master); assert_perror (err); mach_port_deallocate (mach_task_self (), startup_port); mach_port_mod_refs (mach_task_self (), authserver, MACH_PORT_RIGHT_SEND, 1); _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authserver); mach_port_deallocate (mach_task_self (), boot); proc_death_notify (startup_proc); add_proc_to_hash (startup_proc); /* Now that we have the task port. */ /* Set our own argv and envp locations. */ self_proc->p_argv = (vm_address_t) argv; self_proc->p_envp = (vm_address_t) envp; /* Give ourselves good scheduling performance, because we are so important. */ err = increase_priority (); if (err) error (0, err, "Increasing priority failed"); #if 0 err = register_new_task_notification (_hurd_host_priv, generic_port, MACH_MSG_TYPE_MAKE_SEND); if (err) error (0, err, "Registering task notifications failed"); #endif { /* Get our stderr set up to print on the console, in case we have to panic or something. */ mach_port_t cons; error_t err; err = device_open (_hurd_device_master, D_READ|D_WRITE, "console", &cons); assert_perror (err); stdin = mach_open_devstream (cons, "r"); stdout = stderr = mach_open_devstream (cons, "w"); mach_port_deallocate (mach_task_self (), cons); } startup = file_name_lookup (_SERVERS_STARTUP, 0, 0); if (MACH_PORT_VALID (startup)) { err = startup_essential_task (startup, mach_task_self (), MACH_PORT_NULL, "proc", _hurd_host_priv); if (err) /* Due to the single-threaded nature of /hurd/startup, it can only handle requests once the core server bootstrap has completed. Therefore, it does not bind itself to /servers/startup until it is ready. */ /* Fall back to abusing the message port lookup. */ startup_fallback = 1; err = mach_port_deallocate (mach_task_self (), startup); assert_perror (err); } else /* Fall back to abusing the message port lookup. */ startup_fallback = 1; while (1) ports_manage_port_operations_multithread (proc_bucket, message_demuxer, 0, 0, 0); }
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; //slice of the slot in which the process is stopped struct timespec tsleep; //when the last twork has started struct timeval startwork; //when the last twork has finished struct timeval endwork; //initialization memset(&twork, 0, sizeof(struct timespec)); memset(&tsleep, 0, sizeof(struct timespec)); memset(&startwork, 0, sizeof(struct timeval)); memset(&endwork, 0, sizeof(struct timeval)); //last working time in microseconds unsigned long workingtime = 0; //generic list item struct list_node *node; //counter int c = 0; //get a better priority increase_priority(); //build the family 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) { update_process_group(&pgroup); if (pgroup.proclist->count==0) { if (verbose) printf("No more processes.\n"); break; } //total cpu actual usage (range 0-1) //1 means that the processes are using 100% cpu double pcpu = -1; //estimate how much the controlled processes are using the cpu in the working interval for (node = pgroup.proclist->first; node != NULL; node = node->next) { struct process *proc = (struct process*)(node->data); if (proc->cpu_usage < 0) { continue; } if (pcpu < 0) pcpu = 0; pcpu += proc->cpu_usage; } //adjust work and sleep time slices if (pcpu < 0) { //it's the 1st cycle, initialize workingrate pcpu = limit; workingrate = limit; twork.tv_nsec = TIME_SLOT * limit * 1000; } else { //adjust workingrate workingrate = MIN(workingrate / pcpu * limit, 1); twork.tv_nsec = TIME_SLOT * 1000 * workingrate; } 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); } //resume processes node = pgroup.proclist->first; while (node != NULL) { struct list_node *next_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, "SIGCONT failed. Process %d dead!\n", proc->pid); //remove process from group delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } node = next_node; } //now processes are free to run (same working slice for all) gettimeofday(&startwork, NULL); nanosleep(&twork, NULL); gettimeofday(&endwork, NULL); workingtime = timediff(&endwork, &startwork); 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 processes only if tsleep>0 node = pgroup.proclist->first; while (node != NULL) { struct list_node *next_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, "SIGSTOP failed. Process %d dead!\n", proc->pid); //remove process from group delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } node = next_node; } //now the processes are sleeping nanosleep(&tsleep,NULL); } c++; } close_process_group(&pgroup); }
int main(int argc, char **argv) { //get program name // char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0])); // program_name = p==NULL?argv[0]:(p+1); program_name = argv[0]; int run_in_background = FALSE; //parse arguments int next_option; /* A string listing valid short options letters. */ const char* short_options="p:e:P:l:c:bqkrvzh"; /* An array describing valid long options. */ const struct option long_options[] = { { "pid", required_argument, NULL, 'p' }, { "exe", required_argument, NULL, 'e' }, { "path", required_argument, NULL, 'P' }, { "limit", required_argument, NULL, 'l' }, { "background", no_argument, NULL, 'b' }, { "quiet", no_argument, NULL, 'q' }, { "verbose", no_argument, NULL, 'v' }, { "lazy", no_argument, NULL, 'z' }, { "help", no_argument, NULL, 'h' }, { "cpu", required_argument, NULL, 'c'}, { NULL, 0, NULL, 0 } }; //argument variables const char *exe=NULL; const char *path=NULL; int perclimit=0; int pid_ok = FALSE; int process_ok = FALSE; int limit_ok = FALSE; int last_known_argument = 0; int kill_process = FALSE; // kill process instead of stopping it int restore_process = FALSE; // restore killed process // struct rlimit maxlimit; NCPU = get_ncpu(); opterr = 0; // avoid unwanted error messages for unknown parameters do { next_option = getopt_long (argc, argv, short_options,long_options, NULL); switch(next_option) { case 'b': run_in_background = TRUE; last_known_argument++; break; case 'p': pid=atoi(optarg); if (pid) // valid PID { pid_ok = TRUE; lazy = TRUE; } last_known_argument += 2; break; case 'e': exe=optarg; process_ok = TRUE; last_known_argument += 2; break; case 'P': path=optarg; process_ok = TRUE; last_known_argument += 2; break; case 'l': perclimit=atoi(optarg); limit_ok = TRUE; last_known_argument += 2; break; case 'c': NCPU = atoi(optarg); last_known_argument += 2; break; case 'k': kill_process = TRUE; last_known_argument++; break; case 'r': restore_process = TRUE; last_known_argument++; break; case 'v': verbose = TRUE; last_known_argument++; break; case 'q': quiet = TRUE; last_known_argument++; break; case 'z': lazy = TRUE; last_known_argument++; break; case 'h': print_usage (stdout, 1); last_known_argument++; break; case 'o': last_known_argument++; next_option = -1; break; case '?': print_usage (stderr, 1); last_known_argument++; break; case -1: break; // default: // abort(); } } while(next_option != -1); // try to launch a program passed on the command line // But only if we do not already have a PID to watch if ( (last_known_argument + 1 < argc) && (pid_ok == FALSE) ) { last_known_argument++; // if we stopped on "--" jump to the next parameter if ( (last_known_argument + 1 < argc) && (! strcmp(argv[last_known_argument], "--") ) ) last_known_argument++; pid_t forked_pid; // try to launch remaining arguments if (verbose) { int index = last_known_argument; printf("Launching %s", argv[index]); for (index = last_known_argument + 1; index < argc; index++) printf(" %s", argv[index]); printf(" with limit %d\n", perclimit); } forked_pid = fork(); if (forked_pid == -1) // error { printf("Failed to launch specified process.\n"); exit(1); } else if (forked_pid == 0) // target child { execvp(argv[last_known_argument], &(argv[last_known_argument]) ); exit(2); } else // parent who will now fork the throttler { pid_t limit_pid; // if we are planning to kill a process, give it // a running head start to avoid death at start-up if (kill_process) sleep(5); limit_pid = fork(); if (limit_pid == 0) // child { pid = forked_pid; // the first child lazy = TRUE; pid_ok = TRUE; if (verbose) printf("Throttling process %d\n", (int) pid); } else // parent exit(0); } /* else if (forked_pid == 0) // child { lazy = TRUE; pid_ok = TRUE; pid = getppid(); if (verbose) printf("Throttling process %d\n", (int) pid); } else // parent { execvp(argv[last_known_argument], &(argv[last_known_argument])); // we should never return exit(2); } */ } // end of launching child process if (!process_ok && !pid_ok) { fprintf(stderr,"Error: You must specify a target process\n"); print_usage (stderr, 1); exit(1); } if ((exe!=NULL && path!=NULL) || (pid_ok && (exe!=NULL || path!=NULL))) { fprintf(stderr,"Error: You must specify exactly one target process\n"); print_usage (stderr, 1); exit(1); } if (!limit_ok) { fprintf(stderr,"Error: You must specify a cpu limit\n"); print_usage (stderr, 1); exit(1); } float limit=perclimit/100.0; if ( (limit <= 0.00) || (limit > NCPU) ) { fprintf(stderr,"Error: limit must be in the range of 1 to %d00\n", NCPU); print_usage (stderr, 1); exit(1); } // check to see if we should fork if (run_in_background) { pid_t process_id; process_id = fork(); if (! process_id) exit(0); else { setsid(); process_id = fork(); if (process_id) exit(0); } } //parameters are all ok! signal(SIGINT,quit); signal(SIGTERM,quit); my_pid = getpid(); if (verbose) printf("%d CPUs detected.\n", NCPU); increase_priority(); /* Instead of all this big block of code to detect and change priority settings, let us just use the increase_priority() function. It is a little more simple and takes a more gradual approach, rather than "all or nothing". -- Jesse if (setpriority(PRIO_PROCESS, my_pid,-20)!=0) { //if that failed, check if we have a limit // by how much we can raise the priority #ifdef RLIMIT_NICE //check if non-root can even make changes // (ifdef because it's only available in linux >= 2.6.13) nice_lim=getpriority(PRIO_PROCESS, my_pid); getrlimit(RLIMIT_NICE, &maxlimit); //if we can do better then current if( (20 - (signed)maxlimit.rlim_cur) < nice_lim && setpriority(PRIO_PROCESS, my_pid, 20 - (signed)maxlimit.rlim_cur)==0 //and it actually works ) { //if we can do better, but not by much, warn about it if( (nice_lim - (20 - (signed)maxlimit.rlim_cur)) < 9) { printf("Warning, can only increase priority by %d.\n", nice_lim - (20 - (signed)maxlimit.rlim_cur)); } //our new limit nice_lim = 20 - (signed)maxlimit.rlim_cur; } else // otherwise don't try to change priority. // The below will also run if it's not possible // for non-root to change priority #endif { printf("Warning: cannot renice.\nTo work better you should run this program as root, or adjust RLIMIT_NICE.\nFor example in /etc/security/limits.conf add a line with: * - nice -10\n\n"); nice_lim=INT_MAX; } } else { nice_lim=-20; } */ //time quantum in microseconds. it's splitted in a working period and a sleeping one int period=100000; struct timespec twork,tsleep; //working and sleeping intervals memset(&twork,0,sizeof(struct timespec)); memset(&tsleep,0,sizeof(struct timespec)); wait_for_process: //look for the target process..or wait for it if (exe != NULL) pid=getpidof(exe); else if (path != NULL) pid=getpidof(path); else waitforpid(pid); //process detected...let's play //init compute_cpu_usage internal stuff compute_cpu_usage(0,0,NULL); //main loop counter int i=0; struct timespec startwork,endwork; long workingtime=0; //last working time in microseconds if (verbose) print_caption(); float pcpu_avg=0; //here we should already have high priority, for time precision while(1) { //estimate how much the controlled process is using the cpu in its working interval struct cpu_usage cu; if (compute_cpu_usage(pid,workingtime,&cu)==-1) { if (!quiet) fprintf(stderr,"Process %d dead!\n",pid); if (lazy) exit(2); //wait until our process appears goto wait_for_process; } //cpu actual usage of process (range 0-1) float pcpu=cu.pcpu; //rate at which we are keeping active the process (range 0-1) float workingrate=cu.workingrate; //adjust work and sleep time slices if (pcpu>0) { twork.tv_nsec=min(period*limit*1000/pcpu*workingrate,period*1000); } else if (pcpu==0) { twork.tv_nsec=period*1000; } else if (pcpu==-1) { //not yet a valid idea of cpu usage pcpu=limit; workingrate=limit; twork.tv_nsec=min(period*limit*1000,period*1000); } tsleep.tv_nsec=period*1000-twork.tv_nsec; //update average usage pcpu_avg=(pcpu_avg*i+pcpu)/(i+1); if (verbose && i%10==0 && i>0) { printf("%0.2f%%\t%6ld us\t%6ld us\t%0.2f%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100); if (i%200 == 0) print_caption(); } // if (limit<1 && limit>0) { // printf("Comparing %f to %f\n", pcpu, limit); if (pcpu < limit) { // printf("Continue\n"); //resume process if (kill(pid,SIGCONT)!=0) { if (!quiet) fprintf(stderr,"Process %d dead!\n",pid); if (lazy) exit(2); //wait until our process appears goto wait_for_process; } } #ifdef __APPLE_ // OS X does not have clock_gettime, use clock_get_time clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); startwork.tv_sec = mts.tv_sec; startwork.tv_nsec = mts.tv_nsec; #else clock_gettime(CLOCK_REALTIME,&startwork); #endif nanosleep(&twork,NULL); //now process is working #ifdef __APPLE__ // OS X does not have clock_gettime, use clock_get_time // clock_serv_t cclock; // mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); endwork.tv_sec = mts.tv_sec; endwork.tv_nsec = mts.tv_nsec; #else clock_gettime(CLOCK_REALTIME,&endwork); #endif workingtime=timediff(&endwork,&startwork); // if (limit<1) { // printf("Checking %f vs %f\n", pcpu, limit); if (pcpu > limit) { // When over our limit we may run into // situations where we want to kill // the offending process, then restart it if (kill_process) { kill(pid, SIGKILL); if (!quiet) fprintf(stderr, "Process %d killed.\n", pid); if ( (lazy) && (! restore_process) ) exit(2); // restart killed process if (restore_process) { pid_t new_process; new_process = fork(); if (new_process == -1) { fprintf(stderr, "Failed to restore killed process.\n"); } else if (new_process == 0) { // child which becomes new process if (verbose) printf("Relaunching %s\n", argv[last_known_argument]); execvp(argv[last_known_argument], &(argv[last_known_argument]) ); } else // parent { // we need to track new process pid = new_process; // avoid killing child process sleep(5); } } } // do not kll process, just throttle it else { // printf("Stop\n"); //stop process, it has worked enough if (kill(pid,SIGSTOP)!=0) { if (!quiet) fprintf(stderr,"Process %d dead!\n", pid); if (lazy) exit(2); //wait until our process appears goto wait_for_process; } nanosleep(&tsleep,NULL); //now process is sleeping } // end of throttle process } // end of process using too much CPU i++; } return 0; }