Part 3. A proc primer (15 points)
In this part of the lab, you’re going to make a simple program that reads some information about running processes from a virtual file.
Linux exposes a bunch of information to user processes through a virtual file
system called procfs
. It’s a virtual file system because there are no
actual files. Instead, when you read from the files in the procfs
file
system, Linux returns information about processes. Exposing information to
users as a file is a common Unix approach. Read this very short Wikipedia
article about the philosophy where everything is a
file.
The procfs
file system is usually mounted at /proc
, meaning we can read
the virtual files in the file system by treating them as normal files inside
the /proc
directory.
The proc(5) man page describes the contents of the files in /proc
in
great details. We’ll be returning to man page repeatedly. But it’s very long
so don’t go read it now. This lab write up will tell you when and where to
look in the file for the information you need.
Your task
In runnable.rs
, write a function
#![allow(unused)] fn main() { type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>; /// Returns the number of currently runnable kernel scheduling entities /// (processes and threads) and the total number of kernel scheduling entities. fn scheduling_entities() -> Result<(usize, usize)> { todo!() } }
This function is going to return either Ok((runnable, total))
or an
Err(err)
. That is, on success, it’ll return a pair of numbers, (runnable, total)
(hence the two parentheses in the Ok
case) and on error, well, it’ll
return some error. The number of runnable “kernel scheduling entities” tells
us how many processes (and threads, which we’ll talk about later in the
course) are currently ready to run (or are running). A process that’s blocked
waiting for input, for example, is not runnable.
Linux exposes that information to you (as well as other information) in the
virtual file /proc/loadavg
. For example,
$ cat loadavg
0.00 0.01 0.04 5/756 26030
Remember that to split a String
(or really a &str
) on white space, we can
use str::split_whitespace()
to get an iterator and we can use collect()
to
read the elements from an interator into a collection, in this case, a
Vec<&str>
. If we wanted to split a string on a particular character, we can
use the str::split()
method. Click the link for the documentation.
The manual page informs me that the final number is the process identifier, PID, for the most recently created process.
#![allow(unused)] fn main() { let loadavg = "0.00 0.01 0.04 5/756 26030"; let parts: Vec<&str> = loadavg.split_whitespace().collect(); println!("Most recent PID: {}", parts[4]); }
Figure out which of those numbers corresponds to the runnable and which to the
total kernel scheduling entities by reading the man page and searching
it for the documentation for /proc/loadavg
.
Recall that you can parse an integer from a string using the parse()
method.
Here’s an example main
function you can use in runnable.rs
to print out the
number of runnable entities.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>; fn scheduling_entities() -> Result<(usize, usize)> { Ok((10, 300)) } fn main() { match scheduling_entities() { Ok((runnable, total)) => { println!("Runnable entities {runnable} of {total}"); } Err(err) => { eprintln!("{err}"); std::process::exit(1); } } }
If you run your code on a Linux system, you should see something like
Runnable entities 10 of 100
(your numbers will differ).
If you run your code on a non-Linux system (and you followed the tip about
attaching file names to error messages using .map_err()
), you’ll get an
error like this.
$ cargo run
/proc/loadavg: No such file or directory (os error 2)
Now that you have some experience reading the procfs manual and
getting information out of the files there, it’s time to start working on the
process
library in the next part.