<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ro">
	<id>http://wiki.dcae.pub.ro/index.php?action=history&amp;feed=atom&amp;title=OS_Lab_4_-_Processes._Signals</id>
	<title>OS Lab 4 - Processes. Signals - Revizia istoricului</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.dcae.pub.ro/index.php?action=history&amp;feed=atom&amp;title=OS_Lab_4_-_Processes._Signals"/>
	<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Processes._Signals&amp;action=history"/>
	<updated>2026-11-02T15:14:39Z</updated>
	<subtitle>Istoricul versiunilor pentru această pagină din wiki</subtitle>
	<generator>MediaWiki 1.35.14</generator>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Processes._Signals&amp;diff=8435&amp;oldid=prev</id>
		<title>Vserbu: Pagină nouă: In the last lab, we used system calls to work with files. So far, each of our programs has been a single process. In this lab, we will create n...</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Processes._Signals&amp;diff=8435&amp;oldid=prev"/>
		<updated>2026-10-29T09:57:30Z</updated>

		<summary type="html">&lt;p&gt;Pagină nouă: In &lt;a href=&quot;/index.php/OS_Lab_3_-_Files._Memory_mapping&quot; title=&quot;OS Lab 3 - Files. Memory mapping&quot;&gt;the last lab&lt;/a&gt;, we used system calls to work with files. So far, each of our programs has been a single process. In this lab, we will create n...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;In [[OS_Lab_3_-_Files._Memory_mapping|the last lab]], we used system calls to work with files. So far, each of our programs has been a single process. In this lab, we will create new processes with [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], run other programs in them with &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, wait for them to end with [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;], and send and handle &amp;#039;&amp;#039;&amp;#039;signals&amp;#039;&amp;#039;&amp;#039;. As an application, we will write a small &amp;#039;&amp;#039;&amp;#039;shell&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Learning objectives ==&lt;br /&gt;
&lt;br /&gt;
By the end of this lab you should be able to:&lt;br /&gt;
&lt;br /&gt;
# create a new process with [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], and tell the parent from the child by the value it returns;&lt;br /&gt;
# wait for a child with [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;], and read its exit status;&lt;br /&gt;
# run another program with the &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt; family of functions, and explain what the process keeps and what it loses;&lt;br /&gt;
# send signals with [https://man7.org/linux/man-pages/man2/kill.2.html &amp;lt;code&amp;gt;kill(2)&amp;lt;/code&amp;gt;], and handle them with [https://man7.org/linux/man-pages/man2/sigaction.2.html &amp;lt;code&amp;gt;sigaction(2)&amp;lt;/code&amp;gt;];&lt;br /&gt;
# put all of these together into a simple shell.&lt;br /&gt;
&lt;br /&gt;
== Processes ==&lt;br /&gt;
&lt;br /&gt;
The kernel runs &amp;#039;&amp;#039;&amp;#039;tasks&amp;#039;&amp;#039;&amp;#039;, and a &amp;#039;&amp;#039;&amp;#039;process&amp;#039;&amp;#039;&amp;#039; is a group of tasks which share their memory, and usually their open files and more. In this lab, each process will have a single task, so we will only use the word process from now on.&lt;br /&gt;
&lt;br /&gt;
Every process is identified by a number, its &amp;#039;&amp;#039;&amp;#039;PID&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;process ID&amp;#039;&amp;#039;), and has a &amp;#039;&amp;#039;&amp;#039;parent&amp;#039;&amp;#039;&amp;#039;: the process that created it. A process can find its own PID using [https://man7.org/linux/man-pages/man2/getpid.2.html &amp;lt;code&amp;gt;getpid(2)&amp;lt;/code&amp;gt;], and the PID of its parent using [https://man7.org/linux/man-pages/man2/getppid.2.html &amp;lt;code&amp;gt;getppid(2)&amp;lt;/code&amp;gt;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
pid_t getpid(void);  // The PID of the process itself&lt;br /&gt;
pid_t getppid(void); // The PID of its parent&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== fork(2) ===&lt;br /&gt;
&lt;br /&gt;
A new process is created with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
pid_t fork(void);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;] creates a copy of the calling process, the &amp;#039;&amp;#039;&amp;#039;child&amp;#039;&amp;#039;&amp;#039;. Both processes continue from the same point, the return from [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], so [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;] &amp;#039;&amp;#039;&amp;#039;returns twice&amp;#039;&amp;#039;&amp;#039;, once in each process:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! In the !! [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;] returns&lt;br /&gt;
|-&lt;br /&gt;
| Parent || The PID of the child&lt;br /&gt;
|-&lt;br /&gt;
| Child || 0&lt;br /&gt;
|-&lt;br /&gt;
| Parent, on error (no child was created) || -1, and sets &amp;lt;code&amp;gt;errno&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_4_-_fork.png|center|thumb|650px|[https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;] returns twice: the PID of the child in the parent, and 0 in the child. From there on, the two processes run independently, each with its own copy of the memory.]]&lt;br /&gt;
&lt;br /&gt;
The child gets a copy of everything the parent has:&lt;br /&gt;
&lt;br /&gt;
* its memory: the code, the global variables, the heap and the stack. The copy is made with &amp;#039;&amp;#039;copy-on-write&amp;#039;&amp;#039; (see the course), so it is fast, but after [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;] a change made by one process is not seen by the other;&lt;br /&gt;
* its file descriptor table: the same numbers point to the same open files, so the two processes share the file offsets;&lt;br /&gt;
* its current working directory, its signal handlers (see below) and more.&lt;br /&gt;
&lt;br /&gt;
The following program creates a child, and both processes print their PIDs and a variable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int x = 1;&lt;br /&gt;
&lt;br /&gt;
    pid_t pid = fork();&lt;br /&gt;
    if (pid == -1) {&lt;br /&gt;
        print_error(&amp;quot;fork&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    if (pid == 0) {&lt;br /&gt;
        // The child: its own copy of x.&lt;br /&gt;
        x = 2;&lt;br /&gt;
        snprintf(msg, sizeof msg, &amp;quot;child:  PID %d, parent %d, x = %d\n&amp;quot;, getpid(), getppid(), x);&lt;br /&gt;
        write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
        return EXIT_SUCCESS;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The parent: pid is the PID of the child.&lt;br /&gt;
    waitpid(pid, nullptr, 0);&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;parent: PID %d, child %d, x = %d\n&amp;quot;, getpid(), pid, x);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
child:  PID 2741, parent 2740, x = 2&lt;br /&gt;
parent: PID 2740, child 2741, x = 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parent calls [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;], which we will look at next, to wait for the child to end before printing, so the child&amp;#039;s line always comes first. Without it, the two lines could come in either order: after [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], the kernel runs the two processes independently.&lt;br /&gt;
&lt;br /&gt;
=== waitpid(2) ===&lt;br /&gt;
&lt;br /&gt;
A parent waits for a child to end with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
pid_t waitpid(pid_t pid, int *wstatus, int options);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt; || The PID of the child to wait for, or &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; for any child&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;wstatus&amp;lt;/code&amp;gt; || Where to store the status of the child, or &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt; if we do not need it&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;options&amp;lt;/code&amp;gt; || 0 to block until the child ends. &amp;lt;code&amp;gt;WNOHANG&amp;lt;/code&amp;gt;: do not block, return 0 if the child has not ended yet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;] returns the PID of the child that ended, or &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; on error (for example, &amp;lt;code&amp;gt;ECHILD&amp;lt;/code&amp;gt; if there is no such child). The status is an &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; which packs several pieces of information. It is read with the following macros:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Macro !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;WIFEXITED(status)&amp;lt;/code&amp;gt; || True if the child ended normally, by returning from &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; or calling [https://man7.org/linux/man-pages/man3/exit.3.html &amp;lt;code&amp;gt;exit(3)&amp;lt;/code&amp;gt;]&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;WEXITSTATUS(status)&amp;lt;/code&amp;gt; || The exit status, from 0 to 255. Only valid if &amp;lt;code&amp;gt;WIFEXITED(status)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;WIFSIGNALED(status)&amp;lt;/code&amp;gt; || True if the child was killed by a signal&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;WTERMSIG(status)&amp;lt;/code&amp;gt; || The number of the signal. Only valid if &amp;lt;code&amp;gt;WIFSIGNALED(status)&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    pid_t pid = fork();&lt;br /&gt;
    if (pid == -1) {&lt;br /&gt;
        print_error(&amp;quot;fork&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
    if (pid == 0) {&lt;br /&gt;
        exit(3);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    int status;&lt;br /&gt;
    if (waitpid(pid, &amp;amp;status, 0) == -1) {&lt;br /&gt;
        print_error(&amp;quot;waitpid&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    if (WIFEXITED(status)) {&lt;br /&gt;
        snprintf(msg, sizeof msg, &amp;quot;child %d exited with status %d\n&amp;quot;, pid, WEXITSTATUS(status));&lt;br /&gt;
    } else if (WIFSIGNALED(status)) {&lt;br /&gt;
        snprintf(msg, sizeof msg, &amp;quot;child %d was killed by signal %d\n&amp;quot;, pid, WTERMSIG(status));&lt;br /&gt;
    }&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
child 2803 exited with status 3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; A child that has ended, but has not been waited for yet, is a &amp;#039;&amp;#039;&amp;#039;zombie&amp;#039;&amp;#039;&amp;#039;: the kernel has freed its memory and files, and keeps only its PID and its status, for when the parent waits for it. &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; shows it as &amp;lt;code&amp;gt;&amp;amp;lt;defunct&amp;amp;gt;&amp;lt;/code&amp;gt;. A parent should always wait for its children: leaving zombies around for a long time is bad practice. If the parent ends first, the child is adopted by process 1, which waits for it.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exec ===&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;] can only make copies of the running program. To run a &amp;#039;&amp;#039;&amp;#039;different&amp;#039;&amp;#039;&amp;#039; program, a process calls [https://man7.org/linux/man-pages/man2/execve.2.html &amp;lt;code&amp;gt;execve(2)&amp;lt;/code&amp;gt;]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
int execve(const char *path, char *const argv[], char *const envp[]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The kernel loads the executable file at &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt; into the process, in place of the program that is running, and starts it with the given arguments and environment. Both &amp;lt;code&amp;gt;argv&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;envp&amp;lt;/code&amp;gt; are arrays of strings ending with &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
On success, [https://man7.org/linux/man-pages/man2/execve.2.html &amp;lt;code&amp;gt;execve(2)&amp;lt;/code&amp;gt;] &amp;#039;&amp;#039;&amp;#039;does not return&amp;#039;&amp;#039;&amp;#039;: the program that called it is gone. If it returns, it failed, and it returns &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; and sets &amp;lt;code&amp;gt;errno&amp;lt;/code&amp;gt; (for example, &amp;lt;code&amp;gt;ENOENT&amp;lt;/code&amp;gt; if there is no such file).&lt;br /&gt;
&lt;br /&gt;
The process is the same: it keeps its PID, its parent, its open file descriptors and its current working directory. Only its memory, and the program in it, are replaced.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;glibc&amp;#039;&amp;#039; provides several functions on top of [https://man7.org/linux/man-pages/man2/execve.2.html &amp;lt;code&amp;gt;execve(2)&amp;lt;/code&amp;gt;], which differ in how they take the arguments and how they find the program:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Function !! Arguments !! The program is !! Environment&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;execl(path, arg0, arg1, ..., nullptr)&amp;lt;/code&amp;gt; || A list || &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt; || Inherited&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;execlp(file, arg0, arg1, ..., nullptr)&amp;lt;/code&amp;gt; || A list || Searched for in &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; || Inherited&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;execv(path, argv)&amp;lt;/code&amp;gt; || An array || &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt; || Inherited&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;execvp(file, argv)&amp;lt;/code&amp;gt; || An array || Searched for in &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; || Inherited&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;execve(path, argv, envp)&amp;lt;/code&amp;gt; || An array || &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;envp&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A common pattern when launching new programs is calling &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt; in the child, right after [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;]. The following program runs &amp;lt;code&amp;gt;ls -l /&amp;lt;/code&amp;gt; and waits for it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    pid_t pid = fork();&lt;br /&gt;
    if (pid == -1) {&lt;br /&gt;
        print_error(&amp;quot;fork&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (pid == 0) {&lt;br /&gt;
        char *argv[] = {&amp;quot;ls&amp;quot;, &amp;quot;-l&amp;quot;, &amp;quot;/&amp;quot;, nullptr};&lt;br /&gt;
        execvp(argv[0], argv);&lt;br /&gt;
        // execvp() only returns if it failed.&lt;br /&gt;
        print_error(argv[0]);&lt;br /&gt;
        _exit(127);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    int status;&lt;br /&gt;
    waitpid(pid, &amp;amp;status, 0);&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;ls exited with status %d\n&amp;quot;, WEXITSTATUS(status));&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
total 64&lt;br /&gt;
lrwxrwxrwx   1 root root     7 Apr 22  2024 bin -&amp;gt; usr/bin&lt;br /&gt;
drwxr-xr-x   4 root root  4096 Sep  3 15:07 boot&lt;br /&gt;
...&lt;br /&gt;
drwxr-xr-x  12 root root  4096 Sep  3 21:06 var&lt;br /&gt;
ls exited with status 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_4_-_fork_exec_wait.png|center|thumb|700px|How a shell runs a command: it forks a copy of itself, the copy prepares itself and calls &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt; to become the command, and the shell waits for it to end.]]&lt;br /&gt;
&lt;br /&gt;
== Signals ==&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;signal&amp;#039;&amp;#039;&amp;#039; is a small number that the kernel sends to a process, to tell it that something happened, right away, whatever the process is doing. Signals come from the kernel itself (an invalid memory access, a child that ended), from the terminal (Ctrl+C), or from other processes. Each signal has a name, a number and a &amp;#039;&amp;#039;&amp;#039;default action&amp;#039;&amp;#039;&amp;#039;, which is what happens to a process that does nothing about it:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Signal !! Number !! Default action !! Sent when&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt; || 2 || End the process || Ctrl+C is pressed in the terminal&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; || 9 || End the process. &amp;#039;&amp;#039;&amp;#039;Cannot be caught or ignored&amp;#039;&amp;#039;&amp;#039; || &amp;lt;code&amp;gt;kill -KILL&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kill -9&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGSEGV&amp;lt;/code&amp;gt; || 11 || End the process || An invalid memory access&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGPIPE&amp;lt;/code&amp;gt; || 13 || End the process || Writing to a pipe that nobody reads&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; || 15 || End the process || &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt;: a polite request to end&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGCHLD&amp;lt;/code&amp;gt; || 17 || Ignore || A child ended or stopped&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; || 18 || Continue a stopped process || &amp;lt;code&amp;gt;kill -CONT&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt; || 19 || Stop (pause) the process. &amp;#039;&amp;#039;&amp;#039;Cannot be caught or ignored&amp;#039;&amp;#039;&amp;#039; || &amp;lt;code&amp;gt;kill -STOP&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; || 20 || Stop the process || Ctrl+Z is pressed in the terminal&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;kill -l&amp;lt;/code&amp;gt; lists all of them.&lt;br /&gt;
&lt;br /&gt;
=== Sending signals: kill(2) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;signal.h&amp;gt;&lt;br /&gt;
int kill(pid_t pid, int sig);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Despite its name, [https://man7.org/linux/man-pages/man2/kill.2.html &amp;lt;code&amp;gt;kill(2)&amp;lt;/code&amp;gt;] sends any signal, not only those which end the process. A process may only send signals to processes of the same user, unless it runs as the superuser (&amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The following program starts a child which waits forever, and ends it with &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    pid_t pid = fork();&lt;br /&gt;
    if (pid == -1) {&lt;br /&gt;
        print_error(&amp;quot;fork&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
    if (pid == 0) {&lt;br /&gt;
        pause(); // Wait forever, or until a signal ends the process.&lt;br /&gt;
        _exit(0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sleep(1);&lt;br /&gt;
    if (kill(pid, SIGTERM) == -1) {&lt;br /&gt;
        print_error(&amp;quot;kill&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    int status;&lt;br /&gt;
    waitpid(pid, &amp;amp;status, 0);&lt;br /&gt;
    if (WIFSIGNALED(status)) {&lt;br /&gt;
        char msg[256];&lt;br /&gt;
        snprintf(msg, sizeof msg, &amp;quot;child %d: %s\n&amp;quot;, pid, strsignal(WTERMSIG(status)));&lt;br /&gt;
        write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    }&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
child 2911: Terminated&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man3/strsignal.3.html &amp;lt;code&amp;gt;strsignal(3)&amp;lt;/code&amp;gt;] (from &amp;lt;code&amp;gt;&amp;amp;lt;string.h&amp;amp;gt;&amp;lt;/code&amp;gt;) returns the description of a signal, the way [https://man7.org/linux/man-pages/man3/strerror.3.html &amp;lt;code&amp;gt;strerror(3)&amp;lt;/code&amp;gt;] does for &amp;lt;code&amp;gt;errno&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; Ctrl+C sends &amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt; to all the processes running in the foreground of the terminal: a program, and the children it creates, all receive it.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling signals: sigaction(2) ===&lt;br /&gt;
&lt;br /&gt;
A process can choose what happens when it receives a signal, with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;signal.h&amp;gt; (simplified)&lt;br /&gt;
struct sigaction {&lt;br /&gt;
    void (*sa_handler)(int); // A function, SIG_DFL or SIG_IGN&lt;br /&gt;
    sigset_t sa_mask;        // Signals to block while the handler runs&lt;br /&gt;
    int sa_flags;            // SA_RESTART, ...&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! &amp;lt;code&amp;gt;sa_handler&amp;lt;/code&amp;gt; !! When the signal arrives&lt;br /&gt;
|-&lt;br /&gt;
| A function &amp;lt;code&amp;gt;void handler(int sig)&amp;lt;/code&amp;gt; || The kernel interrupts the program and calls the function, the &amp;#039;&amp;#039;&amp;#039;signal handler&amp;#039;&amp;#039;&amp;#039;, with the number of the signal. When the handler returns, the program continues where it was&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIG_IGN&amp;lt;/code&amp;gt; || The signal is ignored&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SIG_DFL&amp;lt;/code&amp;gt; || The default action (see the table above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_4_-_signal_handler.png|center|thumb|700px|A signal handler is the reverse of a system call: the kernel calls into the program. After the handler, [https://man7.org/linux/man-pages/man2/sigreturn.2.html &amp;lt;code&amp;gt;sigreturn(2)&amp;lt;/code&amp;gt;] takes the program back to where it was interrupted.]]&lt;br /&gt;
&lt;br /&gt;
The following program counts Ctrl+C presses, and ends after the third one:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static volatile sig_atomic_t count = 0;&lt;br /&gt;
&lt;br /&gt;
static void on_sigint(int sig)&lt;br /&gt;
{&lt;br /&gt;
    (void) sig;&lt;br /&gt;
    count++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    struct sigaction sa = {.sa_handler = on_sigint};&lt;br /&gt;
    sigemptyset(&amp;amp;sa.sa_mask);&lt;br /&gt;
    if (sigaction(SIGINT, &amp;amp;sa, nullptr) == -1) {&lt;br /&gt;
        char msg[256];&lt;br /&gt;
        snprintf(msg, sizeof msg, &amp;quot;sigaction: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
        write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while (count &amp;lt; 3) {&lt;br /&gt;
        pause(); // Sleep until a signal arrives.&lt;br /&gt;
        const char msg[] = &amp;quot; Caught SIGINT\n&amp;quot;;&lt;br /&gt;
        write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    }&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
^C Caught SIGINT&lt;br /&gt;
^C Caught SIGINT&lt;br /&gt;
^C Caught SIGINT&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A signal handler can run between any two instructions of the program, so it must be careful:&lt;br /&gt;
&lt;br /&gt;
* It should only change global variables of type &amp;lt;code&amp;gt;volatile sig_atomic_t&amp;lt;/code&amp;gt;, which the program reads after it returns, like &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; above.&lt;br /&gt;
* It should only call &amp;#039;&amp;#039;&amp;#039;async-signal-safe&amp;#039;&amp;#039;&amp;#039; functions, such as [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man2/kill.2.html &amp;lt;code&amp;gt;kill(2)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/_exit.2.html &amp;lt;code&amp;gt;_exit(2)&amp;lt;/code&amp;gt;]. [https://man7.org/linux/man-pages/man3/printf.3.html &amp;lt;code&amp;gt;printf(3)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man3/malloc.3.html &amp;lt;code&amp;gt;malloc(3)&amp;lt;/code&amp;gt;] are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; safe: the handler may have interrupted the program in the middle of one of them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; If a signal arrives while the process is blocked in a system call, such as [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] or [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;], the system call is interrupted: after the handler returns, it fails with &amp;lt;code&amp;gt;errno&amp;lt;/code&amp;gt; set to &amp;lt;code&amp;gt;EINTR&amp;lt;/code&amp;gt;. With &amp;lt;code&amp;gt;SA_RESTART&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;sa_flags&amp;lt;/code&amp;gt;, most system calls are restarted instead, as if nothing happened.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blocking signals: sigprocmask(2) ===&lt;br /&gt;
&lt;br /&gt;
A process can also &amp;#039;&amp;#039;&amp;#039;block&amp;#039;&amp;#039;&amp;#039; signals for a while, for example during a part of the program that should not be interrupted. A blocked signal is not lost: it stays &amp;#039;&amp;#039;&amp;#039;pending&amp;#039;&amp;#039;&amp;#039;, and is delivered as soon as it is unblocked.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;signal.h&amp;gt;&lt;br /&gt;
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sigset_t set;&lt;br /&gt;
sigemptyset(&amp;amp;set);&lt;br /&gt;
sigaddset(&amp;amp;set, SIGINT);&lt;br /&gt;
sigprocmask(SIG_BLOCK, &amp;amp;set, nullptr);   // Ctrl+C waits from here...&lt;br /&gt;
// ...&lt;br /&gt;
sigprocmask(SIG_UNBLOCK, &amp;amp;set, nullptr); // ...until here&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Signals, fork(2) and exec ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! !! After [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], in the child !! After &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Signals with a handler || Same handler || Back to the default action: the handler was part of the old program&lt;br /&gt;
|-&lt;br /&gt;
| Ignored signals (&amp;lt;code&amp;gt;SIG_IGN&amp;lt;/code&amp;gt;) || Still ignored || Still ignored&lt;br /&gt;
|-&lt;br /&gt;
| Blocked signals || Still blocked || Still blocked&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Reading lines: readline ==&lt;br /&gt;
&lt;br /&gt;
The project template already uses the GNU &amp;#039;&amp;#039;readline&amp;#039;&amp;#039; library, which reads a line from the terminal and lets the user edit it and recall earlier lines with the arrow keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;readline/readline.h&amp;gt; and &amp;lt;readline/history.h&amp;gt;&lt;br /&gt;
char *readline(const char *prompt);&lt;br /&gt;
void add_history(const char *line);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man3/readline.3.html &amp;lt;code&amp;gt;readline(3)&amp;lt;/code&amp;gt;] prints the prompt and returns the line, without the newline, in memory allocated with [https://man7.org/linux/man-pages/man3/malloc.3.html &amp;lt;code&amp;gt;malloc(3)&amp;lt;/code&amp;gt;], which the program must [https://man7.org/linux/man-pages/man3/free.3.html &amp;lt;code&amp;gt;free(3)&amp;lt;/code&amp;gt;]. At the end of the input (Ctrl+D on an empty line), it returns &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;add_history()&amp;lt;/code&amp;gt; saves a line for the arrow keys.&lt;br /&gt;
&lt;br /&gt;
By default, [https://man7.org/linux/man-pages/man3/readline.3.html &amp;lt;code&amp;gt;readline(3)&amp;lt;/code&amp;gt;] installs signal handlers of its own while it waits for a line, to put the terminal back in order before the program is interrupted. A program which handles signals itself turns this off, before anything else, with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
rl_catch_signals = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exercises ==&lt;br /&gt;
&lt;br /&gt;
Write the program in a new copy of the project template. As a reminder from [[OS_Lab_1_-_Linux_setup._Bash_primer._C_primer#getting-the-template|the first lab]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/oslab &amp;amp;&amp;amp; cd ~/oslab&lt;br /&gt;
curl -OL https://wiki.dcae.pub.ro/images/9/93/Os_lab_skeleton.zip&lt;br /&gt;
unzip -d lab04 Os_lab_skeleton.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this lab:&lt;br /&gt;
&lt;br /&gt;
* Use the &amp;#039;&amp;#039;glibc&amp;#039;&amp;#039; wrappers for the system calls ([https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man3/execvp.3.html &amp;lt;code&amp;gt;execvp(3)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man2/kill.2.html &amp;lt;code&amp;gt;kill(2)&amp;lt;/code&amp;gt;], ...).&lt;br /&gt;
* As in the last lab, do not use the standard I/O library for output: build the text with [https://man7.org/linux/man-pages/man3/snprintf.3.html &amp;lt;code&amp;gt;snprintf(3)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;] it. Use [https://man7.org/linux/man-pages/man3/readline.3.html &amp;lt;code&amp;gt;readline(3)&amp;lt;/code&amp;gt;] for input.&lt;br /&gt;
&lt;br /&gt;
=== A simple shell ===&lt;br /&gt;
&lt;br /&gt;
Write a shell: a program which reads commands with [https://man7.org/linux/man-pages/man3/readline.3.html &amp;lt;code&amp;gt;readline(3)&amp;lt;/code&amp;gt;], runs them, and waits for them to end, until the user types &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt; or presses Ctrl+D.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
$ echo hello world&lt;br /&gt;
hello world&lt;br /&gt;
$ ls /nonexistent&lt;br /&gt;
ls: cannot access &amp;#039;/nonexistent&amp;#039;: No such file or directory&lt;br /&gt;
[exit status 2]&lt;br /&gt;
$ nosuchcmd&lt;br /&gt;
nosuchcmd: command not found&lt;br /&gt;
[exit status 127]&lt;br /&gt;
$ cd /usr&lt;br /&gt;
$ pwd&lt;br /&gt;
/usr&lt;br /&gt;
$ exit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Read a line, and split it into words, at spaces and tabs, into an array of strings ending with &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt;. [https://man7.org/linux/man-pages/man3/strtok_r.3.html &amp;lt;code&amp;gt;strtok_r(3)&amp;lt;/code&amp;gt;] can be used.&lt;br /&gt;
# Run the command: [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], call [https://man7.org/linux/man-pages/man3/execvp.3.html &amp;lt;code&amp;gt;execvp(3)&amp;lt;/code&amp;gt;] in the child, and [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;] for it in the parent. If [https://man7.org/linux/man-pages/man3/execvp.3.html &amp;lt;code&amp;gt;execvp(3)&amp;lt;/code&amp;gt;] fails, print &amp;lt;code&amp;gt;NAME: command not found&amp;lt;/code&amp;gt; on standard error, and end the child with status 127.&lt;br /&gt;
# After the command ends, print its exit status on standard error if it is not 0, as &amp;lt;code&amp;gt;[exit status N]&amp;lt;/code&amp;gt;, or the signal that killed it, as &amp;lt;code&amp;gt;[killed by signal N: NAME]&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Add two &amp;#039;&amp;#039;&amp;#039;built-in&amp;#039;&amp;#039;&amp;#039; commands, which the shell runs itself, without [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;]: &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;cd DIR&amp;lt;/code&amp;gt;, which changes the current working directory with [https://man7.org/linux/man-pages/man2/chdir.2.html &amp;lt;code&amp;gt;chdir(2)&amp;lt;/code&amp;gt;], or to &amp;lt;code&amp;gt;$HOME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;getenv(&amp;quot;HOME&amp;quot;)&amp;lt;/code&amp;gt;) if there is no &amp;lt;code&amp;gt;DIR&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Bonus:&amp;#039;&amp;#039;&amp;#039; Set &amp;lt;code&amp;gt;rl_catch_signals = 0&amp;lt;/code&amp;gt; as the first thing in &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;, and install a handler for &amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt; which passes the signal on to the running command, with [https://man7.org/linux/man-pages/man2/kill.2.html &amp;lt;code&amp;gt;kill(2)&amp;lt;/code&amp;gt;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sleep 10&lt;br /&gt;
^C[killed by signal 2: Interrupt]&lt;br /&gt;
$ echo still here&lt;br /&gt;
still here&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Keep the PID of the running command in a &amp;lt;code&amp;gt;volatile sig_atomic_t&amp;lt;/code&amp;gt; global variable, and set it back to 0 when the command ends. At the prompt, there is no command to pass the signal to.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;SA_RESTART&amp;lt;/code&amp;gt;, or check for &amp;lt;code&amp;gt;EINTR&amp;lt;/code&amp;gt; around [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;].&lt;br /&gt;
* The handler is installed before [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;]. What happens to it in the child, after &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;? Why would &amp;lt;code&amp;gt;SIG_IGN&amp;lt;/code&amp;gt; not work instead of a handler?&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Extra:&amp;#039;&amp;#039;&amp;#039; Support output redirection: &amp;lt;code&amp;gt;ls -l / &amp;gt; listing.txt&amp;lt;/code&amp;gt;. In the child, before [https://man7.org/linux/man-pages/man3/execvp.3.html &amp;lt;code&amp;gt;execvp(3)&amp;lt;/code&amp;gt;], open the file, and make the standard output point to it with [https://man7.org/linux/man-pages/man2/dup2.2.html &amp;lt;code&amp;gt;dup2(2)&amp;lt;/code&amp;gt;]: &amp;lt;code&amp;gt;dup2(fd, STDOUT_FILENO)&amp;lt;/code&amp;gt; makes file descriptor 1 point to the same open file as &amp;lt;code&amp;gt;fd&amp;lt;/code&amp;gt;, which can then be closed. Remove &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt; and the file name from the arguments.&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
</feed>