Reader small image

You're reading from  Practical System Programming for Rust Developers

Product typeBook
Published inDec 2020
PublisherPackt
ISBN-139781800560963
Edition1st Edition
Tools
Right arrow
Author (1)
Prabhu Eshwarla
Prabhu Eshwarla
author image
Prabhu Eshwarla

Prabhu Eshwarla has been shipping high-quality, business-critical software to large enterprises and running IT operations for over 25 years. He is also a passionate teacher of complex technologies. Prabhu has worked with Hewlett Packard and has deep experience in software engineering, engineering management, and IT operations. Prabhu is passionate about Rust and blockchain and specializes in distributed systems. He considers coding to be a creative craft, and an excellent tool to create new digital worlds (and experiences) sustained through rigorous software engineering.
Read more about Prabhu Eshwarla

Right arrow

Chapter 8: Working with Processes and Signals

Do you know how commands are executed when you type them into a terminal interface on your computer? Are these commands directly executed by the operating system, or is there an intermediate program that handles them? When you run a program from the command line in the foreground, and press Ctrl + C, who is listening to this keypress, and how is the program terminated? How can multiple user programs be run at the same time by the operating system? What is the difference between a program and a process? If you are curious, then read on.

In the previous chapter, we learned how to control and alter the terminal interface that is used to interact with the users in command-line applications.

In this chapter, we will look at processes, which are the second most popular abstraction in systems programming after files. We'll learn what processes are, how they differ from programs, how they are started and terminated, and how the process...

Technical requirements

Verify that rustc, and cargo have been installed correctly with the following command:

rustc –version
cargo --version

The Git repo for the code in this chapter can be found at https://github.com/PacktPublishing/Practical-System-Programming-for-Rust-Developers/tree/master/Chapter08.

Note

The section on signal handling requires a Unix-like development environment (Unix, Linux, or macOS), as Microsoft Windows does not directly have the concept of signals. If you work with Windows, download a virtual machine such as Oracle VirtualBox (https://www.virtualbox.org/wiki/Downloads) or use a Docker container to launch a Unix/Linux image to follow along.

Understanding Linux process concepts and syscalls

In this section, we'll cover the fundamentals of process management and get an appreciation of why it is important for systems programming. We'll look at the process life cycle, including creating new processes, setting their environment parameters, working with their standard input and output, and terminating the processes.

This section starts with understanding the differences between a program and a process. We'll then go into a few key details about the fundamentals of processes in Linux. Lastly, we'll see an overview of how to manage the process life cycle with Rust using syscalls encapsulated by the Rust standard library.

How does a program become a process?

A process is a running program. To be precise, it is an instance of a running program. You can have multiple instances of a single program running at the same time, such as starting a text editor from multiple terminal windows. Each such instance...

Spawning processes with Rust

In the Rust standard library, std::process is the module for working with processes. In this section, we'll look at how to spawn new processes, interact with child processes, and abort the current process using the Rust standard library. The Rust standard library internally uses the corresponding Unix/Linux syscalls to invoke the kernel operations for managing processes.

Let's begin with launching new child processes.

Spawning new child processes

The std::process::Command is used to launch a program at a specified path, or to run a standard shell command. The configuration parameters for the new process can be constructed using a builder pattern. Let's see a simple example:

use std::process::Command;
fn main() {
    Command::new("ls")
        .spawn()
        .expect("ls command failed to start");
}

The code...

Handling I/O and environment variables

In this section, we'll look at how to handle I/O with child processes, and also learn to set and clear environment variables for the child process.

Why would we need this?

Take the example of a load balancer that is tasked with spawning new workers (Unix processes) in response to incoming requests. Let's assume the new worker process reads configuration parameters from environment variables to perform its tasks. The load balancer process then would need to spawn the worker process and also set its environment variables. Likewise, there may be another situation where the parent process wants to read a child process's standard output or standard error and route it to a log file. Let's understand how to perform such activities in Rust. We'll start with handling the I/O of the child process.

Handling the I/O of child processes

Standard input (stdin), standard output (stdout), and standard error (stderr) are...

Handling panic, errors, and signals

Processes can fail due to various error conditions. These have to be handled in a controlled manner. There may also be situations where we want to terminate a process in response to external inputs, such as a user pressing Ctrl + C. How we can handle such situations is the focus of this section.

Note

In cases when processes exit due to errors, the operating system itself performs some cleanup, such as releasing memory, closing network connections, and releasing any file handles associated with the process. But sometimes, you may want program-driven controls to handle these cases.

Failures in process execution can broadly be classified into two types – unrecoverable errors and recoverable errors. When a process encounters an unrecoverable error, there is sometimes no option but to abort the process. Let's see how to do that.

Aborting the current process

We saw how to terminate and exit from a process in the Spawning processes...

Writing a shell program in Rust (project)

We learned in the Delving into Linux process fundamentals section what a shell program is. In this section, let's build a shell program, adding features iteratively.

In the first iteration, we'll write the basic code to read a shell command from the command line and spawn a child process to execute the command. Next, we'll add the ability to pass command arguments to the child process. Lastly, we will personalize the shell by adding support for users to enter commands in a more natural-language-like syntax. We'll also introduce error handling in this last iteration. Let's get started:

  1. Let's first create a new project:
    cargo new myshell && cd myshell
  2. Create three files: src/iter1.rs, src/iter2.rs, and src/iter3.rs. The code for the three iterations will be placed in these files so that it will be easy to build and test each iteration separately.
  3. Add the following to Cargo.toml:
    [[bin]]...

Summary

In this chapter, we reviewed the basics of processes in Unix-like operating systems. We learned how to spawn a child process, interact with its standard input and standard output, and execute a command with its arguments. We also saw how to set and clear environment variables. We looked at the various ways to terminate a process on error conditions, and how to detect and handle external signals. We finally wrote a shell program in Rust that can execute the standard Unix commands, but also accept a couple of commands in a natural-language format. We also handled a set of errors to make the program more robust.

Continuing on the topic of managing system resources, in the next chapter, we will learn how to manage threads of a process and build concurrent systems programs in Rust.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Practical System Programming for Rust Developers
Published in: Dec 2020Publisher: PacktISBN-13: 9781800560963
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Prabhu Eshwarla

Prabhu Eshwarla has been shipping high-quality, business-critical software to large enterprises and running IT operations for over 25 years. He is also a passionate teacher of complex technologies. Prabhu has worked with Hewlett Packard and has deep experience in software engineering, engineering management, and IT operations. Prabhu is passionate about Rust and blockchain and specializes in distributed systems. He considers coding to be a creative craft, and an excellent tool to create new digital worlds (and experiences) sustained through rigorous software engineering.
Read more about Prabhu Eshwarla