thefed wrote:
> 
> On Jan 4, 2008, at 11:36 PM, M. Edward (Ed) Borasky wrote:
> 
>> If you're willing to sacrifice portability away from Linux, I can give
>> you some quick-and-dirty code that will go directly into /proc and
>> snapshot this once every five minutes.
> 
> I'd love to see that code!
> 
> But what I'm actually trying to do is to actually _limit_ the CPU usage
> from ruby - not shut it down if it uses to much.
> 
> I want to teach the duck the trick, not punish for doing it wrong!
> 
> IE, make it so all my programs never use more than 15% of my CPU.
> 
> Thanks,
> Ari
> 
> 

Well ... from inside a Ruby script, open the following file:

/proc/self/stat

You will get a line something like this:

 $ more /proc/self/stat
9931 (more) R 9923 9931 9923 34816 9931 4194304 281 0 1 0 0 0 0 0 20 0 1
0 10067 10 8445952 197 18446744073709551615 4194304 4223844
140735769651408 184467440737 09551615 47274997513552 0 0 0 134742022 0 0
0 17 0 0 0 2

Yeah, a bunch of numbers separated by spaces. I *think* this hasn't
changed in many kernel versions, but here's the C code that creates that
line from the source of the 2.6.23 kernel. If you want to look at yours,
the file name is /usr/src/linux/fs/proc/array.c

  res = sprintf(buffer, "%d (%s) %c %d %d %d %d %d %u %lu \
%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
%lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu\n",
    task->pid,
    tcomm,
    state,
    ppid,
    pgid,
    sid,
    tty_nr,
    tty_pgrp,
    task->flags,
    min_flt,
    cmin_flt,
    maj_flt,
    cmaj_flt,
    cputime_to_clock_t(utime),
    cputime_to_clock_t(stime),
    cputime_to_clock_t(cutime),
    cputime_to_clock_t(cstime),
    priority,
    nice,
    num_threads,
    start_time,
    vsize,
    mm ? get_mm_rss(mm) : 0,
    rsslim,
    mm ? mm->start_code : 0,
    mm ? mm->end_code : 0,
    mm ? mm->start_stack : 0,
    esp,
    eip,
    /* The signal information here is obsolete.
     * It must be decimal for Linux 2.0 compatibility.
     * Use /proc/#/status for real-time signals.
     */
    task->pending.signal.sig[0] & 0x7fffffffUL,
    task->blocked.sig[0] & 0x7fffffffUL,
    sigign      .sig[0] & 0x7fffffffUL,
    sigcatch    .sig[0] & 0x7fffffffUL,
    wchan,
    0UL,
    0UL,
    task->exit_signal,
    task_cpu(task),
    task->rt_priority,
    task->policy,
    (unsigned long long)delayacct_blkio_ticks(task));
  if (mm)
    mmput(mm);


OK ... now what? Well, you read that line with Ruby, and the kernel very
nicely printed a bunch of space-separated fields. There's one field for
each line in the "sprintf". The ones you want are

    cputime_to_clock_t(utime),
    cputime_to_clock_t(stime),

These are, in order, the number of CPU ticks your Ruby process has
consumed since process creation in user mode and the number of CPU ticks
consumed since process creation in system mode. These start at zero and
increase every time the kernel looks at the process and sees what it has
done.

There's one more piece of the puzzle. Those counts are in CPU ticks,
known as "jiffies" in Linux. You want to convert them to seconds. The
conversion factor is called "HZ" and it's in a kernel header file. On my
Athlon64, it's in

/usr/include/asm-i386/param.h:#define HZ 100

So ... your script has to

1. Read /proc/self/stat
2. Split the line up into fields
3. Pick out the right two fields
4. Add them together
5. Find the definition of HZ in the kernel header file
6. Divide the total in step 4 by HZ

You now have the total number of CPU seconds your process has used.

Or ... if you want to build a C extension, you could call "getrusage",
which is a lot more portable, since it will run on almost all platforms.

Or, you could open your copy of the Pickaxe (2nd edition -- Ruby 1.8)
and go to page 566. There you will find a method called "process.times".

:)

This reminds me of the joke about the student who was asked how to
measure the height of a building using a barometer.