Esempio n. 1
0
File: main.c Progetto: Larhard/hurd
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);
}
Esempio n. 2
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);
}
Esempio n. 3
0
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;
}