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, write a function

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)> {

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.

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 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) => {

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.