Chapter 2: Exercises
This page is a generated reference surface for selective reading. It exists to keep the learner apps guide-first while still preserving source access.
Learning objectives
- Explain the main ideas and vocabulary in Exercises.
- Work through the source examples for Exercises without depending on raw chunk order.
- Use Exercises as selective reference when learner modules point back to Operating System Concepts.
Prerequisites
- Earlier prerequisite concepts leading into Chapter 2: Exercises.
Module targets
module-01-processes-scheduling
AI companion modes
- Explain simply
- Socratic tutor
- Quiz me
- Challenge my understanding
- Diagnose my confusion
- Generate extra practice
- Revision mode
- Connect forward / backward
Source-of-truth note
This unit is anchored to Operating System Concepts and the source chapter "Chapter 2: Exercises". Use external resources only to clarify, extend, or modernize details without replacing the chapter's conceptual spine.
External enrichment
No chapter-specific enrichment resources are curated yet. Add them in the unit manifest when a source clearly improves learning.
Source provenance
- Primary source:
Operating System Concepts - Source chapter 02: Chapter 2: Exercises
- Raw source file:
035-chapter-2-exercises.md - Raw source file:
036-programming-projects-part-1.md - Raw source file:
037-programming-projects-part-2.md
Merged source
Chapter 2 Exercises
Chapter 2 Exercises
The services and functions provided by an operating system can be divided into two main categories. Briefly describe the two categories, and discuss how they differ.
2.10 Describe three general methods for passing parameters to the operating system.
2.11 Describe how you could obtain a statistical profile of the amount of time a program spends executing different sections of its code. Discuss the importance of obtaining such a statistical profile.
2.12 What are the advantages and disadvantages of using the same systemcall interface for manipulating both files and devices?
2.13 Would it be possible for the user to develop a new command interpreter using the system-call interface provided by the operating system?
2.14 Describe why Android uses ahead-of-time (AOT) rather than just-in-time (JIT) compilation.
2.15 What are the two models of interprocess communication? What are the strengths and weaknesses of the two approaches?
2.16 Contrast and compare an application programming interface (API) and an application binary interface (ABI).
2.17 Why is the separation of mechanism and policy desirable?
2.18 It is sometimes difficult to achieve a layered approach if two components of the operating system are dependent on each other. Identify a scenario in which it is unclear how to layer two system components that require tight coupling of their functionalities.
2.19 What is the main advantage of the microkernel approach to system design? How do user programs and system services interact in a microkernel architecture? What are the disadvantages of using the microkernel approach?
2.20 What are the advantages of using loadable kernel modules?
2.21 How are iOS and Android similar? How are they different?
2.22 Explain why Java programs running on Android systems do not use the standard Java API and virtual machine.
2.23 The experimental Synthesis operating system has an assembler incorporated in the kernel. To optimize system-call performance, the kernel assembles routines within kernel space to minimize the path that the system call must take through the kernel. This approach is the antithesis of the layered approach, in which the path through the kernel is extended to make building the operating system easier. Discuss the pros and cons of the Synthesis approach to kernel design and system-performance optimization.
Programming Projects Part 1
Programming Projects (Part 1)
Introduction to Linux Kernel Modules In this project, you will learn how to create a kernel module and load it into the Linux kernel. You will then modify the kernel module so that it creates an entry in the /proc file system. The project can be completed using the Linux virtual machine that is available with this text. Although you may use any text editor to write these C programs, you will have to use the terminal application to compile the programs, and you will have to enter commands on the command line to manage the modules in the kernel.
As you'll discover, the advantage of developing kernel modules is that it is a relatively easy method of interacting with the kernel, thus allowing you to write programs that directly invoke kernel functions. It is important for you to keep in mind that you are indeed writing kernel code that directly interacts with the kernel. That normally means that any errors in the code could crash the system! However, since you will be using a virtual machine, any failures will at worst only require rebooting the system.
Programming Projects
I. Kernel Modules Overview
The first part of this project involves following a series of steps for creating and inserting a module into the Linux kernel.
You can list all kernel modules that are currently loaded by entering the command
lsmod
This command will list the current kernel modules in three columns: name, size, and where the module is being used.
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h>
/* This function is called when the module is loaded. */int simple init(void) { printk(KERN INFO "Loading Kernel Module∖n");
return 0; }
/* This function is called when the module is removed. */void simple exit(void) { printk(KERN INFO "Removing Kernel Module∖n"); }
/* Macros for registering module entry and exit points. */module init(simple init); module exit(simple exit);
MODULE LICENSE("GPL");
MODULE DESCRIPTION("Simple Module");
MODULE AUTHOR("SGG");
Kernel module simple.c.
Figure 2.21
The program in Figure 2.21 (named simple.c and available with the source code for this text) illustrates a very basic kernel module that prints appropriate messages when it is loaded and unloaded.
init() is the module entry point, which represents The function simple the function that is invoked when the module is loaded into the kernel. Simiexit() function is the module exit point--the function that larly, the simple is called when the module is removed from the kernel.
The module entry point function must return an integer value, with 0 representing success and any other value representing failure. The module exit point function returns void. Neither the module entry point nor the module exit point is passed any parameters. The two following macros are used for registering the module entry and exit points with the kernel: module init(simple init)
module exit(simple exit)
Notice in the figure how the module entry and exit point functions make calls to the printk() function. printk() is the kernel equivalent of printf(), but its output is sent to a kernel log buffer whose contents can be read by the dmesg command. One difference between printf() and printk() is that printk() allows us to specify a priority flag, whose values are given in the <linux/printk.h> include file. In this instance, the priority is KERN
INFO, which is defined as an informational message.
The final lines--MODULE
LICENSE(), MODULE
DESCRIPTION(), and MOD-
ULE AUTHOR()--represent details regarding the software license, description of the module, and author. For our purposes, we do not require this information, but we include it because it is standard practice in developing kernel modules.
This kernel module simple.c is compiled using the Makefile accompanying the source code with this project. To compile the module, enter the following on the command line:
make
The compilation produces several files. The file simple.ko represents the compiled kernel module. The following step illustrates inserting this module into the Linux kernel.
II. Loading and Removing Kernel Modules
Kernel modules are loaded using the insmod command, which is run as follows:
sudo insmod simple.ko
To check whether the module has loaded, enter the lsmod command and search for the module simple. Recall that the module entry point is invoked when the module is inserted into the kernel. To check the contents of this message in the kernel log buffer, enter the command
dmesg
You should see the message "Loading Module."
Removing the kernel module involves invoking the rmmod command (notice that the.ko suffix is unnecessary):
sudo rmmod simple
Programming Projects
Be sure to check with the dmesg command to ensure the module has been removed.
Because the kernel log buffer can fill up quickly, it often makes sense to clear the buffer periodically. This can be accomplished as follows:
sudo dmesg -c
Proceed through the steps described above to create the kernel module and to load and unload the module. Be sure to check the contents of the kernel log buffer using dmesg to ensure that you have followed the steps properly.
As kernel modules are running within the kernel, it is possible to obtain values and call functions that are available only in the kernel and not to regular user applications. For example, the Linux include file <linux/hash.h> defines several hashing functions for use within the kernel. This file also defines the constant value GOLDEN
RATIO PRIME (which is defined as an unsigned long).
This value can be printed out as follows:
INFO "%lu∖n", GOLDEN printk(KERN
RATIO
PRIME);
As another example, the include file <linux/gcd.h> defines the following function
unsigned long gcd(unsigned long a, unsigned b);
which returns the greatest common divisor of the parameters a and b.
Once you are able to correctly load and unload your module, complete the following additional steps:
- Print out the value of GOLDEN
RATIO PRIME in the simple init() function.
- Print out the greatest common divisor of 3,300 and 24 in the simple exit() function.
As compiler errors are not often helpful when performing kernel development, it is important to compile your program often by running make regularly. Be sure to load and remove the kernel module and check the kernel log buffer using dmesg to ensure that your changes to simple.c are working properly.
Programming Projects Part 2
Programming Projects (Part 2)
In Section 1.4.3, we described the role of the timer as well as the timer interrupt handler. In Linux, the rate at which the timer ticks (the tick rate) is the value HZ defined in <asm/param.h>. The value of HZ determines the frequency of the timer interrupt, and its value varies by machine type and architecture. For example, if the value of HZ is 100, a timer interrupt occurs 100 times per second, or every 10 milliseconds. Additionally, the kernel keeps track of the global variable jiffies, which maintains the number of timer interrupts that have occurred since the system was booted. The jiffies variable is declared in the file <linux/jiffies.h>.
-
Print out the values of jiffies and HZ in the simple init() function.
-
Print out the value of jiffies in the simple exit() function.
Before proceeding to the next set of exercises, consider how you can use the different values of jiffies in simple init() and simple exit() to determine the number of seconds that have elapsed since the time the kernel module was loaded and then removed.
III. The /proc File System
The /proc file system is a "pseudo" file system that exists only in kernel memory and is used primarily for querying various kernel and per-process statistics.
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/proc fs.h> #include <asm/uaccess.h>
#define BUFFER
SIZE 128 #define PROC NAME "hello"
t proc read(struct file *file, char user *usr buf, ssize size t count, loff t *pos);
ops = { static struct file operations proc.owner = THIS
MODULE,.read = proc read, };
/* This function is called when the module is loaded. /int proc init(void) { / creates the /proc/hello entry */proc create(PROC NAME, 0666, NULL, &proc ops);
return 0; }
/* This function is called when the module is removed. /void proc exit(void) { / removes the /proc/hello entry */remove proc entry(PROC
NAME, NULL); }
The /proc file-system kernel module, Part 1
Figure 2.22
This exercise involves designing kernel modules that create additional entries in the /proc file system involving both kernel statistics and information related
Programming Projects
to specific processes. The entire program is included in Figure 2.22 and Figure 2.23.
We begin by describing how to create a new entry in the /proc file system. The following program example (named hello.c and available with the source code for this text) creates a /proc entry named /proc/hello. If a user enters the command
cat /proc/hello
the infamous Hello World message is returned.
/* This function is called each time /proc/hello is read */ssize t proc read(struct file *file, char user *usr buf, size t count, loff t *pos) { int rv = 0; char buffer[BUFFER SIZE]; static int completed = 0;
if (completed) { completed = 0; return 0; }
completed = 1;
rv = sprintf(buffer, "Hello World∖n");
/* copies kernel space buffer to user space usr buf */copy to user(usr buf, buffer, rv);
return rv; } module init(proc init); module exit(proc exit);
MODULE LICENSE("GPL");
MODULE DESCRIPTION("Hello Module");
MODULE AUTHOR("SGG");
The /proc file system kernel module, Part 2
Figure 2.23
In the module entry point proc init(), we create the new /proc/hello entry using the proc create() function. This function is passed proc ops, which contains a reference to a struct file operations. This struct initial-
izes the.owner and.read members. The value of.read is the name of the function proc read() that is to be called whenever /proc/hello is read.
Examining this proc read() function, we see that the string "Hello World∖n" is written to the variable buffer where buffer exists in kernel memory. Since /proc/hello can be accessed from user space, we must copy the contents of buffer to user space using the kernel function copy to user().
This function copies the contents of kernel memory buffer to the variable usr buf, which exists in user space. Each time the /proc/hello file is read, the proc read() function is called repeatedly until it returns 0, so there must be logic to ensure that this function returns 0 once it has collected the data (in this case, the string "Hello World∖n") that is to go into the corresponding /proc/hello file.
Finally, notice that the /proc/hello file is removed in the module exit point proc exit() using the function remove proc entry().
IV. Assignment
This assignment will involve designing two kernel modules:
- Design a kernel module that creates a /proc file named /proc/jiffies that reports the current value of jiffies when the /proc/jiffies file is read, such as with the command
cat /proc/jiffies
Be sure to remove /proc/jiffies when the module is removed.
- Design a kernel module that creates a proc file named /proc/seconds that reports the number of elapsed seconds since the kernel module was loaded. This will involve using the value of jiffies as well as the HZ rate. When a user enters the command
cat /proc/seconds
your kernel module will report the number of seconds that have elapsed since the kernel module was first loaded. Be sure to remove /proc/seconds when the module is removed.