<p>Trying to learn many things at once: A language, an operating system, creating command line tools, compilation, etc. goes against cognitive load theory. That is why so many students fail. Keep it simple teach one thing at a time. You will do it in less time to can then teach the other things, with time to spare.</p>
<p>As a tutor I have taught a student, to create various Unix tools using python. We made: <code>cat</code>, <code>ls</code>, <code>tee</code>, <code>grep</code>, etc. and a simple shell. It did not obscure anything about how operating systems work. It did reduce cognitive load</p>

CC BY-SA 4.0
2020-07-17T06:05:47.387Z
I agree that this should be split, however we do not have the time in the curriculum. They need to learn some language anyhow (they only know Haskell at that point).
ljrk
CC BY-SA 4.0
2020-07-17T06:15:27.807Z
How would you create programming exercises to apply learnt things about e.g. memory handling within the OS with Python? I started learning with Pascal (but w/o records) and it was never clear to me until I have finally seen them as pointers in C. Before I had no understanding whatsoever.
ljrk
CC BY-SA 4.0
2020-07-17T14:59:49.570Z
Cognitive load theory says that you can do both in less time, if you do one after the other. I used to try to teach many things at once, until I realised that it dose not work. I still do, IF it does not increase cognitive load.
ctrl-alt-delor
CC BY-SA 4.0
2020-07-17T15:04:06.997Z
Is memory an operating system thing? It seems like a CPU/architecture thing? Therefore split the course into 2 or 3 parts: learn to use Unix, learn to program in python, learn to write Unix tools in python (which tool is memory map based+), learn system architecture, learn to program by directly accessing memory. (Also note python provides mmap, with mutable string/arrays, to directly access memory (this memory is backed by a file, there may be other similar libraries).
ctrl-alt-delor
CC BY-SA 4.0
2020-07-17T21:27:12.573Z
Hmm, I think the issue is that it just "seems" that we can do this "more efficient" in parallel but this is only due the students not actually learning many things. If we were to split the course, we'd probably remove content but not because it's less efficient but because it makes it more obvious that the course is over crammed.
ljrk
CC BY-SA 4.0
2020-07-17T21:29:21.943Z
A bit of an issue we have is that "programming skills" are considered a prerequisite to the course, however that is simply unrealistic. But that's nothing we can fix here, unfortunately :) OTOH, "learning to program" is not really part of our (heavily academic) curriculum, neither is using UNIX like systems.
ljrk
CC BY-SA 4.0
2020-07-17T21:32:40.287Z
I think we simply need to go over the courses goals, it's simply unrealistic right now. However, the "current" way of doing things of learning C/Rust "in tandem" is, I think, helpful insofar, as some C concepts "require" some OS knowledge and vice-versa, but this should be a solvable problem. RE: Memory and OS: Yes, kinda, but the memory management/allocation is configured/driven by algorithms in the OS, while the memory protection is realised using the MMU. We focus on the former in our course.
ljrk
CC BY-SA 4.0
2020-07-17T21:37:43.090Z
Thanks for the really in-depth thoughts! I think you're right and our current way of doing things simply hides the issues. The curriculum us quite unrealistic (w/o prior knowledge, which is the case for most ppl teaching, including me) and splitting the course as you proposed will improve the course but it will still be less good than what I'd like it to be. Regardless, I still ponder whether using Rust (instead of C or Python) would be a good idea. In my experience Python is good for learning programming but that's not the goal here; anyhow different question (and heated topic). Thanks again!
ljrk
CC BY-SA 4.0
2020-07-18T10:42:50.337Z
Note: I have NO opinion of rust (I have not used it, not even as a programmer.). I only discussed python, as that is what I used. I also tried golang, it is very nice, however it makes it hard to create your own inter-process communication, as it already has it: I got a circular buffer working in golang, but it would not fail where it should (because of non atomicity). Therefore check the language has no features that will get in the way (these are often features that you want, most of the time).
ctrl-alt-delor
CC BY-SA 4.0
2020-07-18T13:54:41.790Z
Yep, that's why I'm unsure about Python. But all that doesn't matter as much as splitting the course, anyway
ljrk
CC BY-SA 4.0
2020-07-22T21:24:59.750Z
@larkey "however we do not have the time in the curriculum" Then your question is really, how do I teach more than I have time for, and the answer is, obviously you can't, and so why are you asking? But whatever you do teach you should separate concerns per this answer.
philipxy
CC BY-SA 4.0
2020-07-22T22:09:36.493Z
@philipxy It's more "how to make the best of it" (since I'm not in the position to change that). And regardless whether we have time or not, the question still stands, doesn't it?
ljrk
CC BY-SA 4.0
2020-07-16T21:57:57.917Z
2020-07-16T21:57:57.917Z
ctrl-alt-delor
<p>In our Bachelor curriculum the course Operating Systems and Computer Networks (5 CP) is designated to be taken in the second semester.  Currently it uses C, and while I'm personally very fond of C, it does usually overwhelm the students to learn C enough in order to be able to complete the practical exercises.</p>
<p>However, the focus of the course is not to learn a language but rather the concepts and applying them in small exercises, while getting a &quot;feel&quot; for operating systems or low-level development.</p>
<h1>Issues with C and Rust</h1>
<p>Issues students had with C:</p>
<ul>
<li>Reading the documentation / man-pages: They're often written for much more experienced people.</li>
<li>Self-teaching C: It's difficult to find resources both understandable and 'to the point' and 'correct'. Most C only resources, often top hits on Google (e.g. geeks4geeks), are simply wrong  code.</li>
<li>Dealing with undefined behavior.</li>
<li>Dealing with complex and difficult-to-understand error messages by the compiler.</li>
<li>Compilation of the program.</li>
</ul>
<p>A few of these issues could be addressed with Rust:</p>
<ul>
<li>The documentation is excellent.</li>
<li>Online-Tutorials don't need to be perfect as with C, since the compiler is pedantic enough.</li>
<li>There's no/less undefined behavior.</li>
<li>The toolchain is easy to use.</li>
</ul>
<p>Other languages and their issues:</p>
<ul>
<li>Python is a popular choice, however I had bad experiences with students being unable to verify their code correctly since it's never compiled and the missing type system can be daunting sometimes as well. Also it often creates a bit too much abstraction in order to be able to look 'into' things.</li>
<li>Go was used in some advanced courses here for networked programming or parallel programming.  It has nice tooling as well but targets a different kind of abstraction as well.</li>
<li>C++ is simply too complex, while improves some areas of C above, it does so without really removing the pitfalls of C.</li>
<li>Java has similar issues as Go and Python here.</li>
</ul>
<h1>Rust Low-Level Abstraction Library</h1>
<p>However Rust has many abstractions that actually stop us from being able to really 'go down' the stack and look 'into' what's happening (at least, without understanding the details of Rust which isn't the goal).  For example for the students to fiddle around with syscalls they are supposed to write a small cat(1) program using open,close,read,write(3) etc.  There's of course the <code>libc</code> crate but using that basically requires knowledge of C and Rust to really deal with things.  I thus drafted a small library which I currently host <a href="https://git.imp.fu-berlin.de/koenigl2/ti3-rust/" rel="nofollow noreferrer">here</a> with really thin abstractions around those syscalls that make dealing with them from Rust quite nicer. <sup>1</sup></p>
<p>The code logic is now quite similar, but hopefully the easier tooling helps the students:</p>
<pre class="lang-c prettyprint-override"><code>ssize_t n = read(src, buffer, sizeof(buffer));
if (n &lt; 0) { return 1; }
if (n == 0) { return 0; }

const size_t read = (size_t)n;
</code></pre>
<p>Becomes:</p>
<pre class="lang-rust prettyprint-override"><code>let read = match read(src, &amp;mut buffer, BUFSIZ) {
    0 =&gt; return 0,
    n if n &lt; 0 =&gt; return 1,
    n =&gt; n as size_t,
};
</code></pre>
<p>And similarly</p>
<pre class="lang-c prettyprint-override"><code>while (remaining &gt; 0) {
        ssize_t written = write(dest, &amp;buffer[read - remaining],
                                remaining);
        if (written &lt; 0) { return 1; }

        remaining -= written;
}
</code></pre>
<p>is now:</p>
<pre class="lang-rust prettyprint-override"><code>let mut remaining = read;
while remaining &gt; 0 {
    let written = match write(dest, &amp;mut buffer[read - remaining..], remaining) {
        n if n &lt; 0 =&gt; return 1,
        n =&gt; n as size_t,
    };
    remaining -= written;
}
</code></pre>
<p>I also think it's far less confusing for students to have &quot;return values&quot; to be separate from program &quot;exit codes&quot;.  However I'd like to hear some opinions or get some feedback whether you think this is worth pursuing.</p>
<p>For those wanting to try this, in <code>Cargo.toml</code> add:</p>
<pre class="lang-toml prettyprint-override"><code>[dependencies]
libc = &quot;0.2&quot;
ti3-rust = { git = &quot;https://git.imp.fu-berlin.de/koenigl2/ti3-rust.git&quot; }
</code></pre>
<h1>Remaining Issues</h1>
<p>Some things are lost when using Rust.  Since <code>cargo</code> now handles building, the possibility to learn about (Preprocessing) -&gt; Compilation -&gt; Assembly -&gt; Linking is lost.  While the goal is not to be CLI compilation magicians, I think understanding this pipeline does help to reason about certain OS behavior.  However one might rightfully argue that this is simply too in-depth for a 5 CP second semester course anyway.</p>
<hr />
<p><sup>1</sup> However this library should not be used in the real-world as it simply wraps the low-level functions in the libc with <code>unsafe</code> blocks and does some ugly casting.</p>

CC BY-SA 4.0
2020-07-18T13:20:52.990Z
Have you seen [this](https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html)?  JFTR I know almost no rust and still less go. But I have a great interest in this question...
Rushi
CC BY-SA 4.0
2020-07-18T14:09:30.330Z
@Rusi I like the way goes about certain things, and as Rob nicely points out, it's about composition and simplicity. However, I think it's no surprise that Go was developed at Google with their needs in mind. If we go low-level, we often end up needing to be really... "specific" in a way Go doesn't allow, unfortunately. I think this could be fixed and the idea of Go combined with the specifics and security of Rust, but we're not there yet. Anyhow, for this course I think Rust is the better choice since it forces you to reason about details.
ljrk
<programming><language-choice><c><operating-systems>
Teaching an absolute beginners class Operating Systems with Rust
# Teaching an absolute beginners class Operating Systems with Rust

(Asked by: username_0 on 2020-07-16)

In our Bachelor curriculum the course Operating Systems and Computer Networks (5 CP) is designated to be taken in the second semester.  Currently it uses C, and while I'm personally very fond of C, it does usually overwhelm the students to learn C enough in order to be able to complete the practical exercises.
However, the focus of the course is not to learn a language but rather the concepts and applying them in small exercises, while getting a "feel" for operating systems or low-level development.
Issues with C and Rust
Issues students had with C:

Reading the documentation / man-pages: They're often written for much more experienced people.
Self-teaching C: It's difficult to find resources both understandable and 'to the point' and 'correct'. Most C only resources, often top hits on Google (e.g. geeks4geeks), are simply wrong  code.
Dealing with undefined behavior.
Dealing with complex and difficult-to-understand error messages by the compiler.
Compilation of the program.

A few of these issues could be addressed with Rust:

The documentation is excellent.
Online-Tutorials don't need to be perfect as with C, since the compiler is pedantic enough.
There's no/less undefined behavior.
The toolchain is easy to use.

Other languages and their issues:

Python is a popular choice, however I had bad experiences with students being unable to verify their code correctly since it's never compiled and the missing type system can be daunting sometimes as well. Also it often creates a bit too much abstraction in order to be able to look 'into' things.
Go was used in some advanced courses here for networked programming or parallel programming.  It has nice tooling as well but targets a different kind of abstraction as well.
C++ is simply too complex, while improves some areas of C above, it does so without really removing the pitfalls of C.
Java has similar issues as Go and Python here.

Rust Low-Level Abstraction Library
However Rust has many abstractions that actually stop us from being able to really 'go down' the stack and look 'into' what's happening (at least, without understanding the details of Rust which isn't the goal).  For example for the students to fiddle around with syscalls they are supposed to write a small cat(1) program using open,close,read,write(3) etc.  There's of course the libc crate but using that basically requires knowledge of C and Rust to really deal with things.  I thus drafted a small library which I currently host here with really thin abstractions around those syscalls that make dealing with them from Rust quite nicer. 1
The code logic is now quite similar, but hopefully the easier tooling helps the students:
ssize_t n = read(src, buffer, sizeof(buffer));
if (n < 0) { return 1; }
if (n == 0) { return 0; }

const size_t read = (size_t)n;

Becomes:
let read = match read(src, &mut buffer, BUFSIZ) {
    0 => return 0,
    n if n < 0 => return 1,
    n => n as size_t,
};

And similarly
while (remaining > 0) {
        ssize_t written = write(dest, &buffer[read - remaining],
                                remaining);
        if (written < 0) { return 1; }

        remaining -= written;
}

is now:
let mut remaining = read;
while remaining > 0 {
    let written = match write(dest, &mut buffer[read - remaining..], remaining) {
        n if n < 0 => return 1,
        n => n as size_t,
    };
    remaining -= written;
}

I also think it's far less confusing for students to have "return values" to be separate from program "exit codes".  However I'd like to hear some opinions or get some feedback whether you think this is worth pursuing.
For those wanting to try this, in Cargo.toml add:
[dependencies]
libc = "0.2"
ti3-rust = { git = "https://git.imp.fu-berlin.de/koenigl2/ti3-rust.git" }

Remaining Issues
Some things are lost when using Rust.  Since cargo now handles building, the possibility to learn about (Preprocessing) -> Compilation -> Assembly -> Linking is lost.  While the goal is not to be CLI compilation magicians, I think understanding this pipeline does help to reason about certain OS behavior.  However one might rightfully argue that this is simply too in-depth for a 5 CP second semester course anyway.

1 However this library should not be used in the real-world as it simply wraps the low-level functions in the libc with unsafe blocks and does some ugly casting.

**Tags:** programming, language-choice, c, operating-systems

> **Comments:**
> **username_1**: Have you seen [this](https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html)?  JFTR I know almost no rust and still less go. But I have a great interest in this question...  
> **username_0**: @Rusi I like the way goes about certain things, and as Rob nicely points out, it's about composition and simplicity. However, I think it's no surprise that Go was developed at Google with their needs in mind. If we go low-level, we often end up needing to be really... "specific" in a way Go doesn't allow, unfortunately. I think this could be fixed and the idea of Go combined with the specifics and security of Rust, but we're not there yet. Anyhow, for this course I think Rust is the better choice since it forces you to reason about details.  

---

## 1 answers

### Answer by username_2 on 2020-07-16. Score: 8 | Accepted answer

Trying to learn many things at once: A language, an operating system, creating command line tools, compilation, etc. goes against cognitive load theory. That is why so many students fail. Keep it simple teach one thing at a time. You will do it in less time to can then teach the other things, with time to spare.

As a tutor I have taught a student, to create various Unix tools using python. We made: `cat`, `ls`, `tee`, `grep`, etc. and a simple shell. It did not obscure anything about how operating systems work. It did reduce cognitive load

> **Comments:**
> **username_0**: I agree that this should be split, however we do not have the time in the curriculum. They need to learn some language anyhow (they only know Haskell at that point).  
> **username_0**: How would you create programming exercises to apply learnt things about e.g. memory handling within the OS with Python? I started learning with Pascal (but w/o records) and it was never clear to me until I have finally seen them as pointers in C. Before I had no understanding whatsoever.  
> **username_2**: Cognitive load theory says that you can do both in less time, if you do one after the other. I used to try to teach many things at once, until I realised that it dose not work. I still do, IF it does not increase cognitive load.  
> **username_2**: Is memory an operating system thing? It seems like a CPU/architecture thing? Therefore split the course into 2 or 3 parts: learn to use Unix, learn to program in python, learn to write Unix tools in python (which tool is memory map based+), learn system architecture, learn to program by directly accessing memory. (Also note python provides mmap, with mutable string/arrays, to directly access memory (this memory is backed by a file, there may be other similar libraries).  
> **username_0**: Hmm, I think the issue is that it just "seems" that we can do this "more efficient" in parallel but this is only due the students not actually learning many things. If we were to split the course, we'd probably remove content but not because it's less efficient but because it makes it more obvious that the course is over crammed.  
> **username_0**: A bit of an issue we have is that "programming skills" are considered a prerequisite to the course, however that is simply unrealistic. But that's nothing we can fix here, unfortunately :) OTOH, "learning to program" is not really part of our (heavily academic) curriculum, neither is using UNIX like systems.  
> **username_0**: I think we simply need to go over the courses goals, it's simply unrealistic right now. However, the "current" way of doing things of learning C/Rust "in tandem" is, I think, helpful insofar, as some C concepts "require" some OS knowledge and vice-versa, but this should be a solvable problem. RE: Memory and OS: Yes, kinda, but the memory management/allocation is configured/driven by algorithms in the OS, while the memory protection is realised using the MMU. We focus on the former in our course.  
> **username_0**: Thanks for the really in-depth thoughts! I think you're right and our current way of doing things simply hides the issues. The curriculum us quite unrealistic (w/o prior knowledge, which is the case for most ppl teaching, including me) and splitting the course as you proposed will improve the course but it will still be less good than what I'd like it to be. Regardless, I still ponder whether using Rust (instead of C or Python) would be a good idea. In my experience Python is good for learning programming but that's not the goal here; anyhow different question (and heated topic). Thanks again!  
> **username_2**: Note: I have NO opinion of rust (I have not used it, not even as a programmer.). I only discussed python, as that is what I used. I also tried golang, it is very nice, however it makes it hard to create your own inter-process communication, as it already has it: I got a circular buffer working in golang, but it would not fail where it should (because of non atomicity). Therefore check the language has no features that will get in the way (these are often features that you want, most of the time).  
> **username_0**: Yep, that's why I'm unsure about Python. But all that doesn't matter as much as splitting the course, anyway  
> **username_3**: @larkey "however we do not have the time in the curriculum" Then your question is really, how do I teach more than I have time for, and the answer is, obviously you can't, and so why are you asking? But whatever you do teach you should separate concerns per this answer.  
> **username_0**: @username_3 It's more "how to make the best of it" (since I'm not in the position to change that). And regardless whether we have time or not, the question still stands, doesn't it?  

---

