diff --git a/README.md b/README.md index bec7b50..266de1a 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,31 @@ # Spark Sessions -Educational content for the Spark Sessions +Educational content for the Spark Sessions. -## Philosophy -When students enter 42's core curriculum they might only have ever programmed 4 or 5 C modules. These projects are divided in parts of no more than 50 lines of code. So when these students start their core curriculum they might get overwhelmed by a project like `ft_printf`, which requires a >100 lines of code, and has a lot of moving parts. To help with students with tackling such a relatively complex project the Spark Sessions were created. By exploring a couple relevant concepts in a structured way, we hope to jumpstart the designer mindset that is needed for building larger projects. +## Why +Students in the first rings of the core curriculum may get overwhelmed by the relatively big complexity of the projects. Spark Sessions aim help with this. -Each session consists of a couple exercises to help students understand some core functionality of a given Intra project. For example: the get_next_line spark session covers the use of static variables. +## Spark session PDFs +All the projects in the 42 curriculum are meant to teach a student software development by introducing one or more programming concepts per subject.\ +All the Spark Sessions are meant to teach beginning students how to tackle a larger (curriculum) project and splitting its contents in manageable pieces.\ +Spark Sessions meant to be a class in how to finish its associated projects. They are meant to teach general design and structure concepts that can be applied to any software project. -Each session is hosted by 1 or more moderators, they are students that are very comfortable with the project. They help the attendees run through the exercises. +## Moderators +Moderator are students that have done, and are very comfortable with the project. +They help to guide the attendees with going through the PDF. +A moderator moderates no more than 5 attendees, as to allow for more personal help and avoiding a "teacher before an class" situation. + +The moderator is not a teacher, they may give pointers on where to look for an answer or might correct some misinterpretations. Ideally they can use a attendees' mistake to explain a advise or best practice that applies to all software development. +Every moderator has their own style, some like to do a lot of talking and going through the exercises as an group, some just try to get the attendees to work together and let them figure it out. -See `./philosophy/` for more information. ## Development Install `NodeJS >= 16.x` Run: -``` +```bash git clone --recursive git@github.com:codam-coding-college/spark-sessions.git cd spark-sessions -chmod -R 777 hooks/ -cp hooks/* .git/hooks/ + +node main.js ``` diff --git a/hooks/pre-commit b/hooks/pre-commit deleted file mode 100755 index 7528052..0000000 --- a/hooks/pre-commit +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -# set -x -set -e - -MOD_FILES=$(git diff --diff-filter=d --cached --name-only) -DATE=$(date +%d\\/%m\\/%Y) -while IFS= read -r FILE_NAME; do - if [[ "${FILE_NAME}" =~ \.md ]] && [[ ! "${FILE_NAME}" = "README.md" ]] && [[ "${FILE_NAME}" == spark-sessions* ]] && [[ "$(basename "${FILE_NAME}")" == SparkSession* ]]; then - if [[ "$OSTYPE" == "linux-gnu"* ]]; then - sed -i "2s/.*/\*updated\: $DATE\*/" "$FILE_NAME" - else - sed -i '' "2s/.*/\*updated\: $DATE\*/" "$FILE_NAME" - fi - echo "Updated date to: ${DATE}" - git add "$FILE_NAME" - if [ ! -f "markdown-to-pdf/build/app.js" ]; then - CURRENT_PWD=$(pwd) - cd markdown-to-pdf/ - npm install - npm run build - cd "$CURRENT_PWD" - fi - FILE=$(basename "$FILE_NAME") - NAME="${FILE%.*}.pdf" - node markdown-to-pdf/build/app.js "$FILE_NAME" "pdfs/$NAME" - git add "pdfs/$NAME" - echo - fi -done <<< "$MOD_FILES" - -exit 0 diff --git a/main.js b/main.js new file mode 100644 index 0000000..4b58dec --- /dev/null +++ b/main.js @@ -0,0 +1,41 @@ +/* ************************************************************************** */ +/* */ +/* :::::::: */ +/* main.js :+: :+: */ +/* +:+ */ +/* By: W2Wizard +#+ */ +/* +#+ */ +/* Created: 2022/09/06 14:20:06 by fbes #+# #+# */ +/* Updated: 2022/09/07 11:04:37 by W2Wizard ######## odam.nl */ +/* */ +/* ************************************************************************** */ + +// Run this file with `node main.js` + +/////////////////////////////////////////////////////////////////////////////// + +const fs = require('fs'); +const { execSync } = require('child_process'); + +/////////////////////////////////////////////////////////////////////////////// + +const MTPDdir = `${__dirname}/markdown-to-pdf`; + +// Create the directory if it does not exist. +if (!fs.existsSync(MTPDdir)) + fs.mkdirSync(MTPDdir); + +// Pull and build +console.log(execSync('git submodule update --init --recursive').toString()) +console.log(execSync('npm install && npm run build', { cwd: MTPDdir }).toString()) + +// Find all the directories with the markdown files in them +const dirs = execSync(`find spark-session -name 'SparkSession*.md'`).toString().trimEnd() + +// Convert each file to PDF +for (const dir of dirs.split('\n')) { + const out = dir.split('/').slice(-1)[0].replace(/\.md$/i, '.pdf') + console.log(execSync(`node markdown-to-pdf/build/app.js '${dir}' 'pdfs/${out}'`).toString()) +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/pdfs/SparkSession - GNL.pdf b/pdfs/SparkSession - GNL.pdf deleted file mode 100644 index c7de330..0000000 Binary files a/pdfs/SparkSession - GNL.pdf and /dev/null differ diff --git a/pdfs/SparkSession - MLX42 - mod.pdf b/pdfs/SparkSession - MLX42 - mod.pdf deleted file mode 100644 index 5a9547b..0000000 Binary files a/pdfs/SparkSession - MLX42 - mod.pdf and /dev/null differ diff --git a/pdfs/SparkSession - MLX42 - setup.pdf b/pdfs/SparkSession - MLX42 - setup.pdf deleted file mode 100644 index a2846c4..0000000 Binary files a/pdfs/SparkSession - MLX42 - setup.pdf and /dev/null differ diff --git a/pdfs/SparkSession - MLX42.pdf b/pdfs/SparkSession - MLX42.pdf deleted file mode 100644 index 8706f2f..0000000 Binary files a/pdfs/SparkSession - MLX42.pdf and /dev/null differ diff --git a/pdfs/SparkSession - ft_printf.pdf b/pdfs/SparkSession - ft_printf.pdf deleted file mode 100644 index 5160f5b..0000000 Binary files a/pdfs/SparkSession - ft_printf.pdf and /dev/null differ diff --git a/pdfs/SparkSession - ft_server - mod.pdf b/pdfs/SparkSession - ft_server - mod.pdf deleted file mode 100644 index 2936f09..0000000 Binary files a/pdfs/SparkSession - ft_server - mod.pdf and /dev/null differ diff --git a/pdfs/SparkSession - ft_server.pdf b/pdfs/SparkSession - ft_server.pdf deleted file mode 100644 index 3e22c02..0000000 Binary files a/pdfs/SparkSession - ft_server.pdf and /dev/null differ diff --git a/pdfs/SparkSession - libasm - mod.pdf b/pdfs/SparkSession - libasm - mod.pdf deleted file mode 100644 index 855ea75..0000000 Binary files a/pdfs/SparkSession - libasm - mod.pdf and /dev/null differ diff --git a/pdfs/SparkSession - libasm.pdf b/pdfs/SparkSession - libasm.pdf deleted file mode 100644 index 731ec8d..0000000 Binary files a/pdfs/SparkSession - libasm.pdf and /dev/null differ diff --git a/pdfs/SparkSession - minilibx - mod.pdf b/pdfs/SparkSession - minilibx - mod.pdf deleted file mode 100644 index 16f96f3..0000000 Binary files a/pdfs/SparkSession - minilibx - mod.pdf and /dev/null differ diff --git a/pdfs/SparkSession - minilibx setup.pdf b/pdfs/SparkSession - minilibx setup.pdf deleted file mode 100644 index e2604d2..0000000 Binary files a/pdfs/SparkSession - minilibx setup.pdf and /dev/null differ diff --git a/pdfs/SparkSession - minilibx.pdf b/pdfs/SparkSession - minilibx.pdf deleted file mode 100644 index 2016805..0000000 Binary files a/pdfs/SparkSession - minilibx.pdf and /dev/null differ diff --git a/pdfs/SparkSession - minishell - mod.pdf b/pdfs/SparkSession - minishell - mod.pdf deleted file mode 100644 index de192e8..0000000 Binary files a/pdfs/SparkSession - minishell - mod.pdf and /dev/null differ diff --git a/pdfs/SparkSession - minishell.pdf b/pdfs/SparkSession - minishell.pdf deleted file mode 100644 index d72f809..0000000 Binary files a/pdfs/SparkSession - minishell.pdf and /dev/null differ diff --git a/pdfs/SparkSession - pipex - mod.pdf b/pdfs/SparkSession - pipex - mod.pdf deleted file mode 100644 index f0fe03f..0000000 Binary files a/pdfs/SparkSession - pipex - mod.pdf and /dev/null differ diff --git a/pdfs/SparkSession - pipex.pdf b/pdfs/SparkSession - pipex.pdf deleted file mode 100644 index ae2c875..0000000 Binary files a/pdfs/SparkSession - pipex.pdf and /dev/null differ diff --git a/philosophy/philosophy.md b/philosophy/philosophy.md deleted file mode 100644 index 2787078..0000000 --- a/philosophy/philosophy.md +++ /dev/null @@ -1,19 +0,0 @@ -# philosophy -> Read the `README.md` for an introduction\ -> This document details some of the considerations for the Spark Sessions - -## Why -Students in the first rings of the core curriculum may get overwhelmed by the relatively big complexity of the projects. Spark Sessions aim help with this. - -## Spark session PDFs -All the projects in the 42 curriculum are meant to teach a student software development by introducing one or more programming concepts per subject.\ -All the Spark Sessions are meant to teach beginning students how to tackle a larger (curriculum) project and splitting its contents in manageable pieces.\ -Spark Sessions meant to be a class in how to finish its associated projects. They are meant to teach general design and structure concepts that can be applied to any software project. - -## Moderators -Moderator are students that have done, and are very comfortable with the project. -They help to guide the attendees with going through the PDF. -A moderator moderates no more than 5 attendees, as to allow for more personal help and avoiding a "teacher before an class" situation. - -The moderator is not a teacher, they may give pointers on where to look for an answer or might correct some misinterpretations. Ideally they can use a attendees' mistake to explain a advise or best practice that applies to all software development. -Every moderator has their own style, some like to do a lot of talking and going through the exercises as an group, some just try to get the attendees to work together and let them figure it out. diff --git a/render-all-pdfs.js b/render-all-pdfs.js deleted file mode 100644 index 617d6ad..0000000 --- a/render-all-pdfs.js +++ /dev/null @@ -1,17 +0,0 @@ -const fs = require('fs') -const { execSync } = require('child_process') - -// check if markdown-to-pdf was pulled correctly -const MTPDFdir = `${__dirname}/markdown-to-pdf` -const contentsOfMTPDFdir = fs.readdirSync(MTPDFdir) -if (contentsOfMTPDFdir.length == 0) { - console.log(execSync('git submodule update --init --recursive').toString()) - console.log(execSync('npm install && npm run build', { cwd: MTPDFdir }).toString()) -} - -const dirs = execSync(`find spark-sessions -name 'SparkSession*.md'`).toString().trimEnd() - -for (const dir of dirs.split('\n')) { - const out = dir.split('/').slice(-1)[0].replace(/\.md$/i, '.pdf') - console.log(execSync(`node markdown-to-pdf/build/app.js '${dir}' 'pdfs/${out}'`).toString()) -} diff --git a/spark-session/MLX42/SparkSession - MLX42.md b/spark-session/MLX42/SparkSession - MLX42.md new file mode 100644 index 0000000..3f12534 --- /dev/null +++ b/spark-session/MLX42/SparkSession - MLX42.md @@ -0,0 +1,93 @@ +
+ Logo +
+ +### Project description: + +This spark session focuses on the usage of MLX42. In the end you will have a good understanding of how to use the graphics library and how to work with pixel data in general. + +## Topics + +1. **Introduction** +2. **Setup** +3. **Creating a basic image** +6. **Bonus** + +## Introduction + +In case this is your first spark session then a warm welcome! +You might ask yourself: "What is a spark session? What is the point?" + +A spark session simply serves as a way of achieving that initial 'spark' for solving on how to complete a certain project. It's goal is not a step-by-step guide / tutorial on how to complete the project. + +They main goal is to teach you self sufficiency by showing and giving you tasks that encourage peer-to-peer interactions as well as being able to find the answer on your own. + +To achieve this, a spark session composes of bite sized objectives that should help you give an easy footing into solving certain problems. Additionally, spark sessions are a hand-on approach with peer-to-peer in mind, for this spark session it's a bit different, this session 'tests' your ability to be self sufficient. + +So you will mostly work on your own however you have your peers to help you out! + +During the spark-session the moderator's purpose is to clear up confusion and work with the students in answering some but not all questions. They are much like C.A.T's in a way that they can help you with some technical stuff but not with actually solving the problems. + +_NOTE: When given an exercise each person should do their own work, however work together to solve problems!_ + +--- + +  +  +  + +### Setup + +During the Spark Session, we'll be writing a little program that can open a window, draw some pixels, and even move pixels! +Before we can do that, we need to set up the MLX42 library and link the compiled library with our source code. + +All the necessary dependencies are already installed on the computers for MLX42 to work. So you won't need to +read the README file on github + +Prepare a folder where you will create your project and simply it clone the library. +You git clone MLX42 [here.](https://github.com/codam-coding-college/MLX42). + +Create a `makefile` that will compile your program and MLX42. +You need to read the provided documentation on either the Wikipage or in the `/docs` folder in the root of MLX42. There you will find all the necessary steps. + +*Tip: Don't forget to define the header location. Hint: -I ``* + +### Our first steps + +Now that our program compiles nicely for us to work with, our very first basic step is to create a window + +If you did read the documentation thoroughly, our first step is to call `mlx_init`: +- Create a window 800 in width and 400 in height. +- For now at least, we don't want the window to be resizable. + - If you do want to have to it resizable however later, you can look into `mlx_set_settings()`. +- What is the return type of this function, what exactly does it represent ? +- Compile and run your program! + - What do you notice ? + +_NOTE: READ the documentation! Don't just skim over it or brute force your way through the problem!_ + +If you noticed that the window didn't open or did appear for a very brief moment you will quickly realize +that the program exist instantly. However we need to keep the program running as long as the window is open. + +- Look into the docs as what is missing to keep the window open. +- What do we do afterwards ? How do we exit properly ? + +Once you have found the answer, or already did if you read the documenation as recommend, your window will now stay open! Hooray! + +### Images & Pixels + +So far our window is pretty empty and not much to look at, lets change that! +Using this graphics library is much like being a painter drawing on a canvas. + +First research the following: +- What are `Images` & `Textures` ? + - What are the differences between the two ? + - How do we create either of them ? + - What is the return type of either function that creates them ? + +Now that you sort of understand what either are create an image that is the same size as the window + +### Moar Pixels + + +### Hooks diff --git a/spark-session/ft_printf/SparkSession - ft_printf.md b/spark-session/ft_printf/SparkSession - ft_printf.md new file mode 100644 index 0000000..d27b9d1 --- /dev/null +++ b/spark-session/ft_printf/SparkSession - ft_printf.md @@ -0,0 +1,98 @@ +
+ Logo +
+ +### Project description: + +This project is pretty straightforward, you have to recode printf. You will learn what is and how to implement variadic functions. Once you validate it, you will reuse this function in your future projects. + +## Topics + +1. Introduction +2. Flow charting +3. Variadic Arguments +4. Function Pointers +5. Putting it into practice + +## Introduction + +In case this is your first spark session then a warm welcome! +You might ask yourself: "What is a spark session? What is the point?" + +A spark session simply serves as a way of achieving that initial 'spark' for solving on how to complete a certain project. It's goal is not a step-by-step guide / tutorial on how to complete the project. + +They main goal is to teach you self sufficiency by showing and giving you tasks that encourage peer-to-peer interactions as well as being able to find the answer on your own. + +To achieve this, a spark session composes of bite sized objectives that should help you give an easy footing into solving certain problems. Additionally, spark sessions are a hand-on approach with peer-to-peer in mind, so you will form in groups of 2-3 to work together and solve the following problems. + +During the spark-session the moderator's purpose is to clear up confusion and work with the students in answering some but not all questions. They are much like C.A.T's in a way that they can help you with some technical stuff but not with actually solving the problems. + +_NOTE: When given an exercise each person should do their own work, however work together to solve problems!_ + +--- + +  +  +  + +### Flow charting + +Though `ft_printf` is a relatively small project, you will see that later on in larger projects such as `webserv` it will be very handy to properly plan out the *flow* of your program. As in, the chain of events of your program. + +As the projects grows in size it will be difficult to maintain an overall view as to what is happening in your program. However a common practice in computer science is to use a flow chart diagram to map out the possile paths of execution in your program. + +With your peers complete the following: +- Discuss and design a flow chart for `ft_printf`. + - You can use tools such as `draw.io` to do this chart. + + +### Variadic Arguments + +Variadic arguments can be quite handy for more dynamic functions, enabling you do quite a few flexible things. +However what can one do with them and how can we actually use these functions to our advantage. + +Well guess what, youre about to find out! With your peers: +- Research and find the functions used to work with `variadic arguments` + - How do we gain access to the variables passed to *va_args* +- What are the most important macros regarding the usage of variadic arguments. + +Lets put this into practice with a small example: +- Start of simple with `ft_sum`: + - It should be prototypes as follows: `ìnt ft_sum(int n, ...)` + - It should provide the sum of the *n* provided arguments. + +- Implement the function `ft_freen`. + - Imagine we have a few allocated pointers, we don't want to call `free` for each pointer. + - It should be prototypes as follows: `void ft_freen(int n, ...)` + - It will free *n* amount of pointers provided via `...`. + +- Finally, implement the function `ft_join`. + - It should be prototypes as follows: `char* ft_join(int n, char* delim, ...)` + - It will join *n* amount of strings together provided via `...`. + - Each string should be separated with the provided delimiter, which is another string. + - Rember to handle errors, in case anything goes wrong, return `NULL`. + +### Function Pointers + +Function pointers are an essential data type and a common concept beyond C as well. +Being able to pass a function to another function via an argument provides a lot of flexibility to modify the behaviour of the given function itself. + +Research the following: +- What is the syntax of a function pointer ? + - How can we use the `typedef` keyword to simplify this syntax ? +- How do they differ from pointers regarding data ? + - Can we just treat them like pointers in general ? + - What about arrays ? +- Look up what a `jump table` is and discuss its usefulness. + +Time to practice! You know the drill, implement these functions to get a good understanding regarding them: +- Write a function that transforms a string with a given func: + - `char* ft_transform(upper, "hello world!");` -> "HELLO WORLD!" + - `char* ft_transform(lower, "HELLO WORLD!");` -> "hello world!" + - It should return an allocated string, remember to handle errors accordingly! + +- Write a function that prints strings where marked: + - The function should print the % with their corresponding arument. + - It should be prototypes as followed: + - `void ft_putsf("Hello %, my name is % !", world, jimmy);` + diff --git a/spark-session/gnl/SparkSession - GNL.md b/spark-session/gnl/SparkSession - GNL.md new file mode 100644 index 0000000..b4995a0 --- /dev/null +++ b/spark-session/gnl/SparkSession - GNL.md @@ -0,0 +1,107 @@ +
+ Logo +
+ +### Project description: + +May it be a file, stdin, or even later a network connection, you will always need a way to read content line by line. It is time to start working on this function, which will be essential for your future projects. + +## Topics + +1. Introduction +2. Dealing with files +3. Understanding static +4. Divide and conquering + +## Introduction + +In case this is your first spark session then a warm welcome! +You might ask yourself: "What is a spark session? What is the point?" + +A spark session simply serves as a way of achieving that initial 'spark' for solving on how to complete a certain project. It's goal is not a step-by-step guide / tutorial on how to complete the project. + +They main goal is to teach you self sufficiency by showing and giving you tasks that encourage peer-to-peer interactions as well as being able to find the answer on your own. + +To achieve this, a spark session composes of bite sized objectives that should help you give an easy footing into solving certain problems. Additionally, spark sessions are a hand-on approach with peer-to-peer in mind, so you will form in groups of 2-3 to work together and solve the following problems. + +During the spark-session the moderator's purpose is to clear up confusion and work with the students in answering some but not all questions. They are much like C.A.T's in a way that they can help you with some technical stuff but not with actually solving the problems. + +_NOTE: When given an exercise each person should do their own work, however work together to solve problems!_ + +--- + +  +  +  + +### Handling files + +Before we start tackling our root problem, we first need to make sure we understand what were dealing with. + +Handling files is one of the most common tasks in any program, you will and want to use it to store any sort of data for caching stuff or for reading say a configuration file. So, being able to understand how to use, access, read and write to files is crucial step! + +- With your peers find the answer to the follwing: + - Look up the following functions: + - `open`, `close`, `read`, `write`. + - What do they do ? How to they behave + - What even is a *syscall* ? + - In regards to `open`, identify the different flags and what they do. + - What is a `fd` ? How they work under the hood ? + - What happens when you pass an `fd` to `close` ? + - Is it possible to lose a `fd` ? + +Now that we are a bit familiar with how to work with `fd`'s, lets put that knowledge to use! + +- Implement the following: + - Write the function `ft_readfile` that will read the contents of an entire file. + - Make sure to handle errors from the *syscalls* you use! + - It only has a single parameter, `int fd`. + - On success it returns the contents of the file as an *allocated* string. + - On failure it simply returns `NULL`. + +--- + +  +  +  + +### Understanding static + +Now that we got files out of our way lets go and focus on static next. In `get_next_line` you are aksed to use the static keyword, now on its own it might not sound like much. But the keyword itself literally does just that what its called. + +At first the static keyword might seem a bit archaic but you will quite quickly find it to be very straight forward. +So time to figure it all out, work with you peers to complete the following. + +- The task is simple: + - Use the internet to find the definition of a static variable + - Identify its unique characteristics depending on how it is used. + +Now that you hopefully understand the keyword... + +- Implement the following: + - Write a function that declares a `static int`, its initial value will be 0. + - Afterwards in increments this value by 1. + - Prints out the value to `stdout` using `write`. + - Execute this function *7* times. + - Take note of the behaviour of this function. + +--- + +  +  +  + +### Divide and conquering + +It might seem difficult to imagine how someone could implement a complex projects. +"Where do I even start with this?" is the very first common question one asks themselves when trying do a project. + +For instance how would one create a graphical program that displays a 3D rotating cube? At first you have no idea where to even look for information. However if you split up your tasks / problems into smaller and smaller pieces it becomes much more easier to see what it takes to complete the bigger picture. + +This section does not ask for much but it will be a bit of brain storming with your peers to figure out what it takes to complete `get_next_line`. + +So to put this into practice, with your peers: +- Work out how you would split up the main problem of implementing `get_next_line` + - Which functions do you need ? + - How can we use static to our advantage ? + - ... diff --git a/spark-session/libft/SparkSession - libft.md b/spark-session/libft/SparkSession - libft.md new file mode 100644 index 0000000..0c22fb8 --- /dev/null +++ b/spark-session/libft/SparkSession - libft.md @@ -0,0 +1,170 @@ +
+ Logo +
+ +### Project description: + +This project is about coding a C library. It will contain a lot of general purpose functions your programs will rely upon. + +## Topics + +1. **Introduction** +2. **Information gathering** +3. **Hitch hiker's guide to Makefiles** +4. **Importance of testing** +5. **Putting it into practice** +6. **Bonus** + +## Introduction + +In case this is your first spark session then a warm welcome! +You might ask yourself: "What is a spark session? What is the point?" + +A spark session simply serves as a way of achieving that initial 'spark' for solving on how to complete a certain project. It's goal is not a step-by-step guide / tutorial on how to complete the project. + +They main goal is to teach you self sufficiency by showing and giving you tasks that encourage peer-to-peer interactions as well as being able to find the answer on your own. + +To achieve this, a spark session composes of bite sized objectives that should help you give an easy footing into solving certain problems. Additionally, spark sessions are a hand-on approach with peer-to-peer in mind, so you will form in groups of 2-3 to work together and solve the following problems. + +During the spark-session the moderator's purpose is to clear up confusion and work with the students in answering some but not all questions. They are much like C.A.T's in a way that they can help you with some technical stuff but not with actually solving the problems. + +_NOTE: When given an exercise each person should do their own work, however work together to solve problems!_ + +--- + +  +  +  + +### Information gathering + +One of the most imporant things in programming is to find information. Usually you will encounter problems with quite the lack of documenation others with an abundance of information. Another challenge is to formulate your problems into a meaningful query not just for a search engine, but also to your peers. + +So to start of easy, lets look into the following problems, work together in order to find the answers. You are free to share them with others! + +- Research and solve the following with your peers: + - Passing `0` to `malloc` + - Passing `""` to `strdup` + - What is the difference between a `Bus Error` and `Segmentation fault (core dumped)` + - Is using `malloc` and not using `free` in `main()` a leak? What about doing that outside of `main()`? + - Is a `char*` an array or a reference to a `char`? What about `char**`? + +- Discuss with other groups upon your results + - Did everyone come to the same conclusion? + - Where did they find the answers to the questions? + - Did they google it ? + - Did they test it with actual code ? + - Did they just assume the answer ? + +With the end of the first chapter you should now have some understanding as to how to research your problems and into how to come to a conclusion when facing difficult problems. + +--- + +### Makefiles + +Now that we have some way of figuring out our problems lets take our first dip into +doing some actual useful research. Makefiles! + +Makefiles lets us build our C Projects in a much more convenient way than manually typing `gcc -Wextra -Werror -Wall ...` for each file. However their syntax and features are somewhat tricky for beginners and numerous. + +So in this chapter we will focus creating a Makefile that will take a `.c` file and compile it into a `42.out` executable. + +1. Start with looking for a good source of documentation regarding makefiles: + - What are `recipes`, `rules`, `targets`? + - Should we create a `recipe` for each file or make a generic case for our `.c` files? + - What is `.PHONY`? Why is it important / useful to have ? + - What is relinking and how do we prevent it? + - Why should we prevent this ? + +  + +2. Lets do something practical now: + - Create the following files `main.c`, `ft_putstr.c`, `ft_lib.h`. + - Simply write a small program that uses the `ft_putstr()` function in the respective file. + - Use the header file to be able to use the `ft_putstr()` function in `main.c` + + - Create the necessary instructions in your `Makefile` to: + - Compile each file into a `.o` file. + - Make it so that all the `.o` files get linked together to maek an executable. + - Also implement the required `recipes`: + - `all`: Compiles all the files. + - `re`: Re-compiles the files. + - `fclean`: Deletes all the `.o` files as well as the executable. + - `clean`: Only delete all the `.o` files. + - Make sure it does not re-link when you use the `make` command. + +--- + +### Importance of testing + +Of course we should ensure that the code we write actually does what it is supposed to do. +For instance a `ft_putstr()` that results in a segfault when you use it doesn't sound like a thing +that should happen. + +However with larger projects and many files, functions and all the likes working together it becomes +harder and harder to make sure that what we wrote actually works. Testing is therefor the cure to not +result in a heap of problems in the end. + +You can go **really** deep in terms of testing all the way from automatic test checking +before commiting, to scheduled testing on **github** or on a remote server. Even to the method of +testing there are many angles. We encourage you to explore all the different possibilites, as the +more tests you do, the fewer bugs and issues you will have later. But is that always the case ? + +1. As group discuss the following: + - What types of tests exist, which ones suit best for ? + - Should we test our functions as we write them ? + - Does doing this actually benefit us ? + - What about the amount of time spent coding vs testing ? + - Do we do our tests only once we're done with our project? + - How many tests should one employ for a given function ? + - Should one overrely on a tester from github? + +--- + +### Putting it into practice + +Its time we combine everything we learned into a singular library, your very own `mini-libft`! + +Your goal now is a mix of practical and research exercises, for you `mini-libft` you must research +the `libc` equivalent functions and implement their behaviour. Once you implemented those functions +you should test those functions. + +This is library will just give you a glimpse into what you could do later for your actual `libft` project. +So we will not go crazy here and keep it simple. Later on you can look into how you can implement it differently. + +1. Implement these functions, research with your peers on how they behave: + - `memcpy` + - `strdup` + - `puts` + - `memset` + - `strlen` + +2. Create test cases for each function: + - For testing, simply write a function that runs your own version and prints out the difference with the `libc` equivalent. + - Making it easy and convenient to execute these tests and the rule `make test` that compiles a `test.c` file. + - Make sure to compile this file with your `mini-libft` library file. + - This file should have a `main()` function that runs your test functions. + - Implement as many test cases as you deem necessary. + - If something doesn't match, fix it and run `make test` again! + +--- + +### Bonus / Extra Work + +We have reached the end of this spark-session! However if you have some more time to spend it would still +be nice to use the remaining time for something useful. + +This is a relatively short section but the exercise below is optional: + +1. Research what structs are and what their syntax is like. +2. Implement a `string` struct to use as an alternative to `char*`. + - It should contain a `char*` and a `size_t` variable for storing the data and size of the string. + - Create the following functions for: + - Creating a new string. + - Printing the string. + - Deleting a string. + - Updating the string to a new value. + - Feel free to name these functions as you desire. +3. Add all of this to your mini-libft. + +--- diff --git a/spark-sessions/ft_server/SparkSession - ft_server - mod.md b/spark-sessions/ft_server/SparkSession - ft_server - mod.md deleted file mode 100644 index b5b725e..0000000 --- a/spark-sessions/ft_server/SparkSession - ft_server - mod.md +++ /dev/null @@ -1,183 +0,0 @@ -# Spark Session: ft_server -*updated: 18/03/2021* - -Project description: -> Discover Docker and set up your first web server. - -## Topics -1. Virtual Machines -2. Containers -3. Docker Tutorial - -## Virtual Machines -1. Traditionally, organizations would run applications on physical servers. This was both expensive to maintain and did not scale well.\ - Virtualization was a solution to this. What is virtualization and what issues does it solve? (15 mins) - > Virtualization is technology that allows you to divide a system's resources more - > efficiently by running multiple Virtual Machines (VMs) on a single physical server's CPU. - > - > Advantages: security (isolation between VMs); flexibility (applications aren't limited by - > host hardware); cost and time efficiency (instantly create VMs, less machines needed); - > more portable (self-contained machines, so can be moved across different servers as needed - > without concern for hardware) - > - > [What is virtualization](https://www.redhat.com/en/topics/virtualization/what-is-virtualization) -2. What is a virtual machine (VM)? What is a hypervisor? (5 mins) - > VMs run applications inside a guest Operating System, which runs on virtual hardware - > powered by the server’s host OS. - > - > Hypervisor is software that takes the host computer's physical resources - such as memory - > and processing - and divides them up to support multiple guest VMs - -## Containers -1. VMs are, however, still resource-heavy as each VM is its own full machine with its own resource needs. - Containerization provides a more lightweight alternative to that. What are containers? How are they similar yet different from VMs? (20 mins) - > A container packages up the code and all its dependencies so that it can be deployed - > quickly and reliably. - > Any changes/processes in these containers are isolated from other containers and the host - > machine. - > - > Similarity: flexibility - VMs allow multiple operating systems regardless of hardware ,containers allow applications regardless of OS. - > - > Difference: containers are more lightweight because they use only what they need of the host\ - > system's resources, unlike VMs that reserve resources; containers are less isolated - > - > [Traditional vs VMs vs Container deployment](https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/) - > - > [What is a container and comparison with VMs](https://www.docker.com/resources/what-container) -2. Where does Docker fit into this picture? Why would you use container technology like Docker? (10 mins) - > Docker is one implementation of container-technology out of [many](https://opencontainers.org/) - > - > Why: standardization (bundling applications with all their dependencies makes it extremely - > portable), security (isolated), low overhead (fast and lightweight) - - ![VM vs Docker](https://i.imgur.com/dlMiZUA.jpg) - -*Break (5 mins)* - -## Docker Tutorial -### Set Up -For this session, we'll be using **Play with Docker** to follow Docker's own [tutorial](https://www.docker.com/101-tutorial)! Here's how to set it up: (10 mins) -1. If you don't have it already, register for a Docker ID: https://docs.docker.com/docker-id/ -2. Go to https://labs.play-with-docker.com/ and log in. -3. Type the following command in the black PWD terminal:\ - `docker run -dp 80:80 docker/getting-started:pwd` *(Copy-paste hint: Shift+Insert)* -4. Wait for it to start the container and click the **port 80** badge above the terminal. - -### Introduction to Docker -Tutorial page: Getting Started -1. Let's break down that `docker run -d -p 80:80 docker/getting-started:pwd` command you just executed. What does each part mean? (10 mins) -2. Why port 80? What is HTTP? (15 mins) - > Port 80 is the port number assigned to HTTP, an internet communication protocol. - > It is the port used by a Web/HTTP client (e.g. a Web browser) to send and receive requested > Web pages from a server. - > - > [Simple explanation](https://medium.com/@odyuzaki/the-importance-of-port-80-243e5953cbd9) - > - > Hypertext Transfer Protocol is the format that is used to structure requests and responses for effective communication between a client and a server over the internet. The message - > that is sent by a client to a server is what is known as an HTTP request. - > - > [Explanation with fun analogy](https://www.codecademy.com/articles/http-requests) -3. We've talked about what containers are. But what is a container image? (10 mins) - > An image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings. - -*Break (5 mins)* - -### Building an Image and Running a Container -Tutorial page: Our Application - -Let's start learning how to use Docker by getting a little to-do list page up and running! (20 mins) -1. Follow the instructions under **"Getting our App into PWD"** to get the application source code into your Play with Docker environment. - - When you're done, do you see the files in `app/` as shown in the tutorial? -2. Now create a Dockerfile in the PWD environment with the code from the tutorial (you can use vim) - - What is a Dockerfile? -3. Build the container image using `docker build -t docker-101 .` - - What happened when you ran that command? -4. Start up the container using `docker run -dp 3000:3000 docker-101` - - Recall what we discussed earlier. What does each component of this command mean? -5. Click on the port **3000** badge at the top of your PWD interface. Do you see your lovely empty to-do list? Try adding an item or two! - -### Stopping and Removing Containers -Tutorial page: Updating our App - -Let's see how we can implement a change in our image. (15 mins) -1. Change the code on line **56** in `app.js` as specified in the tutorial. -2. Let's try building this newest version of the image with\ - `docker build -t docker-101 .` -3. What happens if you execute `docker run -dp 3000:3000 docker-101` like last time? - > Will error because only one process can listen to a specific port at a time and the old container is still running -4. So we first need to stop our old container that still has the outdated code. - - How can we see the currently running containers? - - How can we stop a specific container?\ - *(Hint: do you have to type the entire container ID?)* -5. Now that we've stopped the old container, we have to remove it. Execute the `docker rm` command shown in the tutorial. - - There's also a way to stop and remove a container using a single command. What is it? -6. Try running your updated application again. Do you see your updated text? - - But notice that all of the items you added to your previous list are gone! Let's address that next. - -### Persisting the Data -Tutorial page: Persisting our DB - -Now let's learn about why our application data is wiped each time we launch it and how to prevent that. (30 mins) -1. Each container has its own filesystem and gets their own space to create and manipulate these files.\ - Follow the instructions on the tutorial page to see how a file created in one container isn't available in another. - - Look at the first `docker run` command. What does each part do? Run it to start the ubuntu container and create the `data.txt` file. - - Run the `docker exec` command shown, using the container ID *(hint: `docker ps`)*. Do you see a random number? - - **Extra step:** now try running `docker exec ls`. Do you see the data.txt file? - - Let's run a second container using the same image. Execute the `docker run` command shown. Notice that there's no `data.txt` file here. - - You can remove the first container now using `docker rm -f `. -2. What are container volumes? -3. Let's try creating and using a **named volume**. Do you know what that is? Run the `docker volume create` command shown. -4. Start your `docker-101` container again, but this time specifying a volume mount with the `-v` flag. - - What does the `docker run` command shown do exactly? -5. Once your to-do list is up, try adding a few items to the list. -6. Now remove the container using `docker rm -f`.\ -7. Start a new container using the same `docker run` command you ran in step 4.\ - If you open the app again, do you see the list items you added earlier? -8. Now let's see where Docker stores that data when you use a named volume.\ - Run the `docker volume inspect` command shown in the tutorial. - - What is a mountpoint? - -Congratulations, now you know how to use volumes in Docker to preserve your application data! -**Named volumes** are one of the main types of volumes in Docker, **bind mounts** being the other.\ -Feel free to try out the tutorial section on bind mounts on your own to see how you can rapidly implement changes in your source code by specifying the mountpoint. - -### Docker Environment Variables -Docker's `ENV` instruction lets you use **environment variables** to set and modify configuration information to running containers. This way, you don't have to manually edit your configuration files! -1. First off, how would you declare an environment variable in your Dockerfile? (5 mins) - > `ENV variable_name=variable_value` -2. Now let's try to change the "New Item" placeholder text in your to-do list! Using environment variables, we can dynamically change what text is displayed. (15 mins)\ - Go into your Dockerfile and add the following things: - - declare an environment variable called `NEW` and give it a default value ("What would you like to do today?", for example) - - replace the last `CMD` instruction with this: *(make sure the instruction is on one line)* - ``` - CMD sed -i "99 s/New Item/${NEW}/g" /app/src/static/js/app.js && node /app/src/index.js - ``` -3. Build your image with `docker build -t docker-101 .` and run it with\ - `docker run -dp 3000:3000 docker-101`. Is the New Item text now replaced with the default value you gave `$NEW`? (5 mins) -4. Now stop the container. Say we want to change the empty message to something else, "Please give me something to do" for example. How can we do that through `docker run`, **without changing the Dockerfile or rebuilding**? (15 mins)\ - Did you manage to change your placeholder text? - ![placeholder](https://i.imgur.com/tZKfgKf.jpg) - > `docker run -e NEW="Please give me something to do" -dp 3000:3000 docker-101` -5. Let's go back to that `CMD sed` instruction I helpfully provided earlier. (15 mins) - - What does each part of the `sed` command mean? - > sed {don't print to stdout} "{line no.} s/{change this}/{to this}/g" - - - Why did I use `CMD` and not `RUN`? - > RUN is executed at build-time, whereas CMD is at run-time. Using `RUN sed` would mean - > `$NEW` always equates to the default value. - - - Can you have multiple `CMD` instructions in a single Dockerfile? - - > There can only be one CMD command in the Dockerfile. So either commands need to be - > chained with `&&` or executed through a bash script. - -Now you know how to use environment variables to allow for more dynamic environment configuration. This will come in handy for certain parts of the project that require toggling settings or passing credentials. - -``` -#example Dockerfile -FROM node:10-alpine -ENV NEW="What would you like to do today?" -WORKDIR /app -COPY . . -RUN yarn install --production -CMD sed -i "99 s/New Item/${NEW}/g" /app/src/static/js/app.js && node /app/src/index.js -``` \ No newline at end of file diff --git a/spark-sessions/ft_server/SparkSession - ft_server.md b/spark-sessions/ft_server/SparkSession - ft_server.md deleted file mode 100644 index 716b7a7..0000000 --- a/spark-sessions/ft_server/SparkSession - ft_server.md +++ /dev/null @@ -1,122 +0,0 @@ -# Spark Session: ft_server -*updated: 18/03/2021* - -Project description: -> Discover Docker and set up your first web server. - -## Topics -1. Virtual Machines -2. Containers -3. Docker Tutorial - -## Virtual Machines -1. Traditionally, organizations would run applications on physical servers. This was both expensive to maintain and did not scale well.\ - Virtualization was a solution to this. What is virtualization and what issues does it solve? (15 mins) -2. What is a virtual machine (VM)? What is a hypervisor? (5 mins) - -## Containers -1. VMs are, however, still resource-heavy as each VM is its own full machine with its own resource needs.\ - Containerization provides a more lightweight alternative to that. What are containers? How are they similar yet different from VMs? (20 mins) -2. Where does Docker fit into this picture? Why would you use container technology like Docker? (10 mins) - - ![VM vs Docker](https://i.imgur.com/dlMiZUA.jpg) - -*Break (5 mins)* - -## Docker Tutorial -### Set Up -For this session, we'll be using **Play with Docker** to follow Docker's own [tutorial](https://www.docker.com/101-tutorial)! Here's how to set it up: (10 mins) -1. If you don't have it already, register for a Docker ID: https://docs.docker.com/docker-id/ -2. Go to https://labs.play-with-docker.com/ and log in. -3. Type the following command in the black PWD terminal:\ - `docker run -dp 80:80 docker/getting-started:pwd` *(Copy-paste hint: Shift+Insert)* -4. Wait for it to start the container and click the **port 80** badge above the terminal. - -### Introduction to Docker -Tutorial page: Getting Started -1. Let's break down that `docker run -d -p 80:80 docker/getting-started:pwd` command you just executed. What does each part mean? (10 mins) -2. Why port 80? What is HTTP? (15 mins) -3. We've talked about what containers are. But what is a container image? (10 mins) - -*Break (5 mins)* - -### Building an Image and Running a Container -Tutorial page: Our Application - -Let's start learning how to use Docker by getting a little to-do list page up and running! (20 mins) -1. Follow the instructions under **"Getting our App into PWD"** to get the application source code into your Play with Docker environment.\ - When you're done, do you see the files in `app/` as shown in the tutorial? -2. Now create a Dockerfile in the PWD environment with the code from the tutorial (you can use vim) - - What is a Dockerfile? -3. Build the container image using `docker build -t docker-101 .` - - What happened when you ran that command? -4. Start up the container using `docker run -dp 3000:3000 docker-101` - - Recall what we discussed earlier. What does each component of this command mean? -5. Click on the port **3000** badge at the top of your PWD interface. Do you see your lovely empty to-do list? Try adding an item or two! - -### Making Changes in our Container -Tutorial page: Updating our App - -Let's see how we can implement a change in our image. (15 mins) -1. Change the code on line **56** in `app.js` as specified in the tutorial. -2. Let's try building this newest version of the image with\ - `docker build -t docker-101 .` -3. What happens if you execute `docker run -dp 3000:3000 docker-101` like last time? -4. So we first need to stop our old container that still has the outdated code. - - How can we see the currently running containers? - - How can we stop a specific container?\ - *(Hint: do you have to type the entire container ID?)* -5. Now that we've stopped the old container, we have to remove it. Execute the `docker rm` command shown in the tutorial. - - There's also a way to stop and remove a container using a single command. What is it? -6. Try running your updated application again. Do you see your updated text? - - But notice that all of the items you added to your previous list are gone! Let's address that next. - -### Persisting the Data -Tutorial page: Persisting our DB - -Now let's learn about why our application data is wiped each time we launch it and how to prevent that. (30 mins) -1. Each container has its own filesystem and gets their own space to create and manipulate these files.\ - Follow the instructions on the tutorial page to see how a file created in one container isn't available in another. - - Look at the first `docker run` command. What does each part do? Run it to start the ubuntu container and create the `data.txt` file. - - Run the `docker exec` command shown, using the container ID *(hint: `docker ps`)*. Do you see a random number? - - **Extra step:** now try running `docker exec ls`. Do you see the data.txt file? - - Let's run a second container using the same image. Execute the `docker run` command shown. Notice that there's no `data.txt` file here. - - You can remove the first container now using `docker rm -f `. -2. What are container volumes? -3. Let's try creating and using a **named volume**. Do you know what that is? Run the `docker volume create` command shown. -4. Start your `docker-101` container again, but this time specifying a volume mount with the `-v` flag. - - What does the `docker run` command shown do exactly? -5. Once your to-do list is up, try adding a few items to the list. -6. Now remove the container using `docker rm -f`. -7. Start a new container using the same `docker run` command you ran in step 4.\ - If you open the app again, do you see the list items you added earlier? -8. Now let's see where Docker stores that data when you use a named volume.\ - Run the `docker volume inspect` command shown in the tutorial. - - What is a mountpoint? - -Congratulations, now you know how to use volumes in Docker to preserve your application data! -**Named volumes** are one of the main types of volumes in Docker, **bind mounts** being the other.\ -Feel free to try out the tutorial section on bind mounts on your own to see how you can rapidly implement changes in your source code by specifying the mountpoint. - -### Docker Environment Variables -Docker's `ENV` instruction lets you use **environment variables** to set and modify configuration information to running containers. This way, you don't have to manually edit your configuration files! -1. First off, how would you declare an environment variable in your Dockerfile? (5 mins) -2. Now let's try to change the "New Item" placeholder text in your to-do list! Using environment variables, we can dynamically change what text is displayed. (15 mins)\ - Go into your Dockerfile and add the following things: - - declare an environment variable called `NEW` and give it a default value ("What would you like to do today?", for example) - - replace the last `CMD` instruction with this: *(make sure the instruction is on one line)* - ``` - CMD sed -i "99 s/New Item/${NEW}/g" /app/src/static/js/app.js && node /app/src/index.js - ``` -3. Build your image with `docker build -t docker-101 .` and run it with\ - `docker run -dp 3000:3000 docker-101`. Is the New Item text now replaced with the default value you gave `$NEW`? (5 mins) -4. Now stop the container. Say we want to change the empty message to something else, "Please give me something to do" for example. How can we do that through `docker run`, **without changing the Dockerfile or rebuilding**? (15 mins)\ - Did you manage to change your placeholder text? - ![placeholder](https://i.imgur.com/tZKfgKf.jpg) -5. Let's go back to that `CMD sed` instruction I helpfully provided earlier. (15 mins) - - What does each part of the `sed` command mean? - - Why did I use `CMD` and not `RUN`? - - Can you have multiple `CMD` instructions in a single Dockerfile? - -Now you know how to use environment variables to allow for more dynamic environment configuration.\ -This will come in handy for certain parts of the project that require toggling settings or passing credentials. diff --git a/spark-sessions/libasm/SparkSession - libasm - mod.md b/spark-sessions/libasm/SparkSession - libasm - mod.md deleted file mode 100644 index abac285..0000000 --- a/spark-sessions/libasm/SparkSession - libasm - mod.md +++ /dev/null @@ -1,188 +0,0 @@ -# Spark Session: libasm -*updated: 01/04/2021* - -Project description: -> Get familiar with assembly language - -This tutorial was written with help from Thijs Bruineman (tbruinem) and [this great tutorial series](https://youtube.com/playlist?list=PLetF-YjXm-sCH6FrTz4AQhfH6INDQvQSn). - -## Topics -1. nasm -2. Registers -3. Instructions -4. Syscall -5. Sections -6. Stack Alignment - -## Setting Up nasm -For libasm, we'll be using the [Netwide Assembler](https://www.nasm.us/) (`nasm`) to compile our assembly code. -If you haven't already, install `nasm` on your system using the following command: -- For Linux: `sudo apt update && sudo apt install nasm` -- For macOS: [install Homebrew](https://brew.sh/) if you don't have it yet, then run `brew install nasm` - -## Registers -1. Registers are internal memory storage locations in the processor that temporarily hold memory.\ - In x86_64 architecture, we have access to 64-bit registers. What does that "64-bit" mean? (5 mins) - > that the registers can hold 64 bits of data -2. We won't go into details about all the registers. Broadly, some registers are used for specific purposes - such as segment registers and the Flags register - while some are for general use. These latter ones are called **General Purpose Registers** and there are **16** of them in 64-bit x86 architecture. What are the registers? (10 mins) - > rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8-r15 -3. You're not limited to working with these registers in their 64-bit entirety though. You can access smaller "sections" of these registers through identifiers. For example, the least significant 2 bytes (16 bits) of RAX can be treated as a 16-bit register called AX. (15 mins) - ![register breakdown](https://i.imgur.com/nToJ8HY.jpg) - - **Question**: what is AL in this case? - > the least significant byte of the accumulator register - bits 0 to 7 of RAX - - - Here's a table showing the breakdown of every general purpose register: - ![gpr](https://i.imgur.com/4UQtei7.jpg?1) - [source, with many other helpful tips](https://aaronbloomfield.github.io/pdr/book/x86-64bit-asm-chapter.pdf) - - **Question**: how would you access the lowest 8 bits of R8? - > r8b - - - **Question**: are these "sub"registers independent? For example, will modifying `al` affect `ax`? - > No, they're not independent registers. Modifying `al` will affect `ax`, but modifying `ah` won't affect `al`. - -4. In 64-bit architecture, most registers no longer serve the special purposes for which they're named - like "accumulator" (rax) and "counter" (rcx). But there's still some things to keep in mind about certain registers. (15 mins) - - RAX is commonly used to store ___ when functions are called within assembly code. - > function return values. Also used to pass system call numbers such as for calls to `write` - - - RSP is called the Stack Pointer and RBP is the Base Pointer. What do they do? - > RSP points to the topmost element in the stack, RBP points to base of stack - - - Lastly, 6 specific registers are used to pass parameters to functions. What are they and which arguments do they correspond to? - > 1st: RDI, 2nd: RSI, 3rd: RDX, 4th: RCX, 5th: R8, 6th: R9 -5. There are certain registers whose values are preserved across function calls - **callee-saved registers** - and registers whose values are not preserved and must be saved if you want to make sure your values aren't changed - **caller-saved registers**. (10 mins) - - Which are the callee-saved registers? - > RBX, RSP, RBP, and R12–R15 - - - Note that which registers are caller/callee-saved vary by system, thus ["calling conventions"](https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions). - - The convention states that the original values within callee-saved registers should be saved by the called function (the callee). The expectation is that those registers hold the same value after the called function returns. What does this mean if we wish to use a callee-saved register? - > must save and restore original value by pushing at start and popping at end (which we'll get into in a bit) - - - Here's a handy table with the registers and their usages: [link](https://stackoverflow.com/questions/18024672/what-registers-are-preserved-through-a-linux-x86-64-function-call) - -***Break (5 mins)*** - -## Instructions -There are [a lot](https://en.wikipedia.org/wiki/X86_instruction_listings) of instructions you can use in x86 assembly, but we're going to focus on a few key ones. -1. Figure out what the following instructions do: (30 mins) - - | instruction | example | - |---|---| - | mov | mov rax, rbx | - | push | push rax | - | pop | pop rax | - | add | add rax, 42 | - | sub | sub rax, 42 | - | inc | inc rax | - | dec | dec rax | - | cmp | cmp rax, 42 | - | jmp | jmp _main | - | je/jne/jl/jg | je _done | - | call | call _printf | - | ret | ret | - - - Make sure you know which operand is affecting which! - > e.g. `add rax, 42` -> rax = rax + 42 -2. What effect would the following `jmp` instruction have? (5 mins) - ``` - _main: - jmp _main - ``` - > program would be stuck in an infinite loop -3. Registers can also serve as pointers. Putting square brackets - `[]` - around registers allows you to access the value the register is **pointing to**, rather than the value of the register itself. (5 mins) - - What's the difference then between `mov rax, rbx` and `mov rax, [rbx]`? - > first instruction loads value in rbx register into rax, second loads value rbx is pointing to into rax - -## Syscall -Now let's look at system calls or **syscall**s, which allow a program to request a service from the kernel. -1. All syscalls have an associated ID. This ID is what you pass into RAX within your assembly code to call on a system function. These IDs vary by operating system. (5 mins) - - For Linux: [syscall IDs as found in unistd_64.h](https://code.woboq.org/linux/linux/arch/x86/include/generated/uapi/asm/unistd_64.h.html) - - For macOS: [syscall IDs in syscalls.master](https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master). Note: You'll need to add `0x200000` before each ID. - - What are the syscall IDs for `write()` and `exit()` for your system? - > `write` is 1 (Linux) and 0x2000004 (mac), `exit` is 60 (Linux) and 0x2000001 (mac) -2. As with normal functions, syscalls can take arguments. Just like in C, `write` takes an fd, a buffer, and the number of bytes to write. We talked about parameter registers earlier. If you want to call `write`, which parameters would you pass to which register? (10 mins)\ - Here's a convenient table for you: - | syscall | rax | rdi | rsi | rdx | rcx (r10 for Linux) | r8 | r9 | - |---|---|---|---|---|---|---|---| - | write | | | | | | | | - > RAX: 1 or 0x2000004, RDI: fd, RSI: address of string, RDX: byte count -3. Let's write a simple assembly program that calls `exit()` with `0` as an argument. This should make the program exit with a status of 0, indicating no errors. (15 mins) - - First, create a `.s` file. Here's what the first part of your code should look like: - ``` - section .text ; this is the section for code - global _main ; this tells the kernel where the program begins - _main: - ``` - - Note: for Linux users, you'll need to remove the `_` before `_main`. - - To make a system call, you need to: - 1. pass the syscall ID into RAX - 2. pass any arguments for the syscall - 3. use the `syscall` instruction - - Go ahead and turn those steps into assembly code. - > `mov rax, 60` ; or 0x2000001 for mac - > `mov rdi, 0` - > `syscall` - - - Although you'll be creating a library for libasm, today we're just going to make a standalone program. So the compilation steps will be different. To compile your `.s` file, run these commands: - - **Note**: remember to change `myfile.s` & `myfile.o` to your actual file name - - For Linux: `nasm -felf64 myfile.s && gcc myfile.o` - - For mac: `nasm -fmacho64 myfile.s && gcc myfile.o` - - Run `./a.out` and then `echo $?`. Does it output `0`? - -***Break (5 mins)*** - -## Sections -1. Assembly files can be divided into 3 **sections** (there are [more](https://www.nasm.us/doc/nasmdoc7.html#section-7.1.3) but we won't get into them now): `.data`, `.bss`, and `.text` (as seen earlier). What are each of these sections meant for? (5 mins) - > data: where data is defined (initialized data) before compilation, bss: where data is allocated for future use (uninitialized data), text: executable instructions -2. Let's make use of a new section: the `.data` section. We're going to write a program that outputs "Hello, world!" followed by a newline. (20 mins) - - Let's start by declaring a new section (section order doesn't matter in an assembly file): - ``` - section .data - text db "Hello, world!", 10 - ``` - - What is "text" here? And what does "db" mean? What is the "10" at the end? - > `text` is a label we can use in our instructions, it's a pointer to the memory address of the string. `start` is also a label. they are essentially variable names. - > `db` stands for define bytes, it means we are going to define some raw bytes (each character in our string is a byte). - > `10` is ascii for the newline character. - - - Next declare your `.text` section as you did in the previous exercise. - - For this exercise, we're going to call `write()` first to output our `text` onto **stdout**. Remember the instruction order for calling `exit()` earlier. What values do you need to move into which registers? - > ; code example for Linux - > main: - > mov rax, 1 ; write for Linux - > mov rdi, 1 ; stdout - > mov rsi, text - > mov rdx, 14 ; len of text - > syscall - - - Finish with a call to `exit()` again like you did earlier. - - Compile with the same commands you used earlier. Do you get "Hello, world!"? - - Note: your compiler may throw up some warnings, but if your program was compiled successfully, just ignore it! - -## Stack Alignment -Stack alignment can be a tricky thing to understand. What you need to know is that x86_64 requires that your stack be aligned on a 16-byte boundary. -- When your program starts at `main` (or `start`), `rsp` (the stack pointer) is 16-byte aligned. -- If you make an external function call, however, such as `call printf` or `call myownfunction`, an **8-byte return address** is pushed onto the stack. Because we're temporarily leaving this function and we need to know where to come back to, right? -- But this means the stack is now misaligned by **8 bytes**. So how do we re-align our stack? (15 mins) - > By either pushing something that is 8 bytes, like a register (e.g. rbx), and popping it after function call or `sub rsp, 8` at beginning and `add rsp, 8` at end. - > good clear explanation about alignment: [link](https://link.springer.com/chapter/10.1007/978-1-4842-5076-1_13) - -You can find more detailed explanations of the stack and alignment online.\ -Here's a helpful link for later: [what does it mean to align the stack](https://stackoverflow.com/questions/4175281/what-does-it-mean-to-align-the-stack/4176397#4176397) - -## Bonus -1. Write a `compare` function in assembly that compares 2 integers and returns: - - `1` if `a > b`; - - `-1` if `a < b`; - - `0` if `a == b`. - - The function prototype is `int compare(int64_t a, int64_t b)`. - -2. You'll also make a test main C file. It should: - - include for the `int64_t` types; - - have your `compare` function prototype; - - call your `compare` function with a variety of inputs (positive, negative, 0s) and print the return. -3. If you're on macOS, you'll probably need to prefix your function with `_` in your assembly code, so that it's `global _compare` and `_compare:`. Because [reasons](https://stackoverflow.com/questions/5908568/what-is-the-reason-function-names-are-prefixed-with-an-underscore-by-the-compile). Linux does not require this. -4. Compile and run it to see if your function is working correctly. - - For Linux: `nasm -felf64 compare.s && gcc compare.o main.c` - - For macOS: `nasm -fmacho64 compare.s && gcc compare.o main.c` diff --git a/spark-sessions/libasm/SparkSession - libasm.md b/spark-sessions/libasm/SparkSession - libasm.md deleted file mode 100644 index f6e197d..0000000 --- a/spark-sessions/libasm/SparkSession - libasm.md +++ /dev/null @@ -1,146 +0,0 @@ -# Spark Session: libasm -*updated: 01/04/2021* - -Project description: -> Get familiar with assembly language - -This tutorial was written with help from Thijs Bruineman (tbruinem) and [this great tutorial series](https://youtube.com/playlist?list=PLetF-YjXm-sCH6FrTz4AQhfH6INDQvQSn). - -## Topics -1. nasm -2. Registers -3. Instructions -4. Syscall -5. Sections -6. Stack Alignment - -## Setting Up nasm -For libasm, we'll be using the [Netwide Assembler](https://www.nasm.us/) (`nasm`) to compile our assembly code. -If you haven't already, install `nasm` on your system using the following command: -- For Linux: `sudo apt update && sudo apt install nasm` -- For macOS: [install Homebrew](https://brew.sh/) if you don't have it yet, then run `brew install nasm` - -## Registers -1. Registers are internal memory storage locations in the processor that temporarily hold memory. In x86_64 architecture, we have access to 64-bit registers. What does that "64-bit" mean? (5 mins) -2. We won't go into details about all the registers. Broadly, some registers are used for specific purposes - such as segment registers and the Flags register - while some are for general use. These latter ones are called **General Purpose Registers** and there are **16** of them in 64-bit x86 architecture. What are the registers? (10 mins) -3. You're not limited to working with these registers in their 64-bit entirety though. You can access smaller "sections" of these registers through identifiers. For example, the least significant 2 bytes (16 bits) of RAX can be treated as a 16-bit register called AX. (15 mins) - ![register breakdown](https://i.imgur.com/nToJ8HY.jpg) - - **Question**: what is AL in this case? - - Here's a table showing the breakdown of every general purpose register: - ![gpr](https://i.imgur.com/4UQtei7.jpg?1)\ - [source, with many other helpful tips](https://aaronbloomfield.github.io/pdr/book/x86-64bit-asm-chapter.pdf) - - **Question**: how would you access the lowest 8 bits of R8? - - **Question**: are these "sub"registers independent? For example, will modifying `al` affect `ax`? -4. In 64-bit architecture, most registers no longer serve the special purposes for which they're named - like "accumulator" (rax) and "counter" (rcx). But there's still some things to keep in mind about certain registers. (15 mins) - - RAX is commonly used to store ___ when functions are called within assembly code. - - RSP is called the Stack Pointer and RBP is the Base Pointer. What do they do? - - Lastly, 6 specific registers are used to pass parameters to functions. What are they and which arguments do they correspond to? -5. There are certain registers whose values are preserved across function calls - **callee-saved registers** - and registers whose values are not preserved and must be saved if you want to make sure your values aren't changed - **caller-saved registers**. (10 mins) - - Which are the callee-saved registers? - - Note that which registers are caller/callee-saved vary by system, thus ["calling conventions"](https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions). - - The convention states that the original values within callee-saved registers should be saved by the called function (the callee). The expectation is that those registers hold the same value after the called function returns. What does this mean if we wish to use a callee-saved register? - - Here's a handy table with the registers and their usages: [link](https://stackoverflow.com/questions/18024672/what-registers-are-preserved-through-a-linux-x86-64-function-call) - -***Break (5 mins)*** - -## Instructions -There are [a lot](https://en.wikipedia.org/wiki/X86_instruction_listings) of instructions you can use in x86 assembly, but we're going to focus on a few key ones. -1. Figure out what the following instructions do: (30 mins) - - | instruction | example | - |---|---| - | mov | mov rax, rbx | - | push | push rax | - | pop | pop rax | - | add | add rax, 42 | - | sub | sub rax, 42 | - | inc | inc rax | - | dec | dec rax | - | cmp | cmp rax, 42 | - | jmp | jmp _main | - | je/jne/jl/jg | je _done | - | call | call _printf | - | ret | ret | - - - Make sure you know which operand is affecting which! -2. What effect would the following `jmp` instruction have? (5 mins) - ``` - _main: - jmp _main - ``` -3. Registers can also serve as pointers. Putting square brackets - `[]` - around registers allows you to access the value the register is **pointing to**, rather than the value of the register itself. (5 mins) - - What's the difference then between `mov rax, rbx` and `mov rax, [rbx]`? - -## Syscall -Now let's look at system calls or **syscall**s, which allow a program to request a service from the kernel. -1. All syscalls have an associated ID. This ID is what you pass into RAX within your assembly code to call on a system function. These IDs vary by operating system. (5 mins) - - For Linux: [syscall IDs as found in unistd_64.h](https://code.woboq.org/linux/linux/arch/x86/include/generated/uapi/asm/unistd_64.h.html) - - For macOS: [syscall IDs in syscalls.master](https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master). Note: You'll need to add `0x200000` before each ID. - - What are the syscall IDs for `write()` and `exit()` for your system? -2. As with normal functions, syscalls can take arguments. Just like in C, `write` takes an fd, a buffer, and the number of bytes to write. We talked about parameter registers earlier. If you want to call `write`, which parameters would you pass to which register? (10 mins)\ - Here's a convenient table for you: - | syscall | rax | rdi | rsi | rdx | rcx (r10 for Linux) | r8 | r9 | - |---|---|---|---|---|---|---|---| - | write | | | | | | | | -3. Let's write a simple assembly program that calls `exit()` with `0` as an argument. This should make the program exit with a status of 0, indicating no errors. (15 mins) - - First, create a `.s` file. Here's what the first part of your code should look like: - ``` - section .text ; this is the section for code - global _main ; this tells the kernel where the program begins - _main: - ``` - - Note: for Linux users, you'll need to remove the `_` before `_main`. - - To make a system call, you need to: - 1. pass the syscall ID into RAX - 2. pass any arguments for the syscall - 3. use the `syscall` instruction - - Go ahead and turn those steps into assembly code. - - Although you'll be creating a library for libasm, today we're just going to make a standalone program. So the compilation steps will be different. To compile your `.s` file, run these commands: - - **Note**: remember to change `myfile.s` & `myfile.o` to your actual file name - - For Linux: `nasm -felf64 myfile.s && gcc myfile.o` - - For mac: `nasm -fmacho64 myfile.s && gcc myfile.o` - - Run `./a.out` and then `echo $?`. Does it output `0`? - -***Break (5 mins)*** - -## Sections -1. Assembly files can be divided into 3 **sections** (there are [more](https://www.nasm.us/doc/nasmdoc7.html#section-7.1.3) but we won't get into them now): `.data`, `.bss`, and `.text` (as seen earlier). What are each of these sections meant for? (5 mins) -2. Let's make use of a new section: the `.data` section. We're going to write a program that outputs "Hello, world!" followed by a newline. (20 mins) - - Let's start by declaring a new section (section order doesn't matter in an assembly file): - ``` - section .data - text db "Hello, world!", 10 - ``` - - What is "text" here? And what does "db" mean? What is the "10" at the end? - - Next declare your `.text` section as you did in the previous exercise. - - For this exercise, we're going to call `write()` first to output our `text` onto **stdout**. Remember the instruction order for calling `exit()` earlier. What values do you need to move into which registers? - - Finish with a call to `exit()` again like you did earlier. - - Compile with the same commands you used earlier. Do you get "Hello, world!"? - - Note: your compiler may throw up some warnings, but if your program was compiled successfully, just ignore it! - -## Stack Alignment -Stack alignment can be a tricky thing to understand. What you need to know is that x86_64 requires that your stack be aligned on a 16-byte boundary. -- When your program starts at `main` (or `start`), `rsp` (the stack pointer) is 16-byte aligned. -- If you make an external function call, however, such as `call printf` or `call myownfunction`, an **8-byte return address** is pushed onto the stack. Because we're temporarily leaving this function and we need to know where to come back to, right? -- But this means the stack is now misaligned by **8 bytes**. So how do we re-align our stack? (15 mins) - -You can find more detailed explanations of the stack and alignment online.\ -Here's a helpful link for later: [what does it mean to align the stack](https://stackoverflow.com/questions/4175281/what-does-it-mean-to-align-the-stack/4176397#4176397) - -## Bonus -1. Write a `compare` function in assembly that compares 2 integers and returns: - - `1` if `a > b`; - - `-1` if `a < b`; - - `0` if `a == b`. - - The function prototype is `int compare(int64_t a, int64_t b)`. - -2. You'll also make a test main C file. It should: - - include for the `int64_t` types; - - have your `compare` function prototype; - - call your `compare` function with a variety of inputs (positive, negative, 0s) and print the return. -3. If you're on macOS, you'll probably need to prefix your function with `_` in your assembly code, so that it's `global _compare` and `_compare:`. Because [reasons](https://stackoverflow.com/questions/5908568/what-is-the-reason-function-names-are-prefixed-with-an-underscore-by-the-compile). Linux does not require this. -4. Compile and run it to see if your function is working correctly. - - For Linux: `nasm -felf64 compare.s && gcc compare.o main.c` - - For macOS: `nasm -fmacho64 compare.s && gcc compare.o main.c` diff --git a/spark-sessions/minilibx/SparkSession - minilibx - mod.md b/spark-sessions/minilibx/SparkSession - minilibx - mod.md deleted file mode 100644 index 4a101f9..0000000 --- a/spark-sessions/minilibx/SparkSession - minilibx - mod.md +++ /dev/null @@ -1,243 +0,0 @@ -# Spark Session: minilibx -*updated: 04/03/2021* - -Session description: -> Learn the basics of working with miniLibX - -This tutorial was written with help from Harm Smits and Jelle van Snik's [MiniLibX tutorial](https://harm-smits.github.io/42docs/libs/minilibx). - -## Topics -1. Window Management -2. Pixel Putting -3. More Pixels -4. Events & Hooks - -### Window Management -Our first step will be to open up some windows! (30 mins) -1. In the set-up instructions, I gave you some code for your `main.c` that included a call to `mlx_init`. - But what does it do and what is its prototype? What does it return? (5 mins) - This link might help -> [prototypes](https://harm-smits.github.io/42docs/libs/minilibx/prototypes.html) - > Function: initialises mlx, establishes a connection to the correct graphical system - > Prototype: `void *mlx_init();` - > Return: mlx instance -2. Let's try opening a small empty window. (10 mins) - - What is the prototype for `mlx_new_window` and what does it return? - - How would you declare and initalize it? - - Now create a window with a width of **800**, height of **480**, and a title of **"My first window"**. - > Prototype: `void *mlx_new_window(void *mlx_ptr, int size_x, int size_y, char *title);` - > Return: window instance pointer - > Code: `void *mlx_win = mlx_new_window(mlx, 800, 480, "My first window");` -3. What happens if you compile and run the program at this point? Your window should have only popped up for a moment. - To make it stay longer, we need to use `mlx_loop`. (15 mins) - - What does it do and what is its prototype? - - Once you understand that, add `mlx_loop` to your code. - - Do you now get a window that stays open? Press `Ctrl-C` to close it when you're done admiring your work. - - **Important**: `mlx_loop` should be called last in your code. Do you know why? - > Function: loops over the given mlx pointer - > Prototype: `int mlx_loop (void *mlx_ptr); // return unused` - > If mlx_loop is called before any other function, it won't get there. - -***Break (5 mins)*** - -### Pixel Putting -Time to put something on that empty window. (60 mins) -1. Rather than [inefficiently pushing pixels](https://harm-smits.github.io/42docs/libs/minilibx/getting_started.html#writing-pixels-to-a-image) one by one to the window using `mlx_pixel_put` , we should draw our pixels onto an **image** first, then push that image to our window. So we need `mlx_new_image`. (10 mins) - - What is `mlx_new_image`'s prototype and return? - - Once you understand that, go ahead and initialise an image with a size of **800 x 480**. - > Prototype: `void *mlx_new_image(void *mlx_ptr,int width,int height);` - > Return: image instance pointer - > Code: `void *img_ptr = mlx_new_image(mlx, 800, 480);` -2. In order to know where we can put our pixels, we need to get the **memory address** of our image. That's where `mlx_get_data_addr` comes in. What arguments does it take and what does it return? (10 mins) - > Prototype: `char *mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian);` - > Return: memory address of image -3. Since the function requires a lot of extra variables, let's keep things neat by using a struct for our image data. (10 mins) - ``` - typedef struct s_img - { - void *img_ptr; - char *address; - int bits_per_pixel; - int line_size; - int endian; - } t_img; - ``` - - Notice that we shifted the image pointer into the struct. Adjust your initialisation of `mlx_new_image` accordingly. - - Then call `mlx_get_data_addr` and pass it the appropriate arguments/references. -5. As explained in point #1, `mlx_pixel_put` is rather inefficient, so here's a much faster version to use in your code: (10 mins) - ``` - void my_pixel_put(t_img *img, int x, int y, unsigned int colour) - { - char *dst; - int offset; - - offset = y * img->line_size + x * (img->bits_per_pixel / 8); - dst = img->address + offset; - *(unsigned int *)dst = colour; - } - ``` - - What is this function doing? What is `offset`? - > The function calculates the address of a pixel by adding its memory offset to the address of the first pixel (img->address here). - > Offset is necessary because `line_size` returned by `mlx_get_data_addr` is different from actual window width due to the bytes not being aligned. Function then colours that pixel. - > [explanation of formula for offset (see "An image in memory" section)](https://www.collabora.com/news-and-blog/blog/2016/02/16/a-programmers-view-on-digital-images-the-essentials/) -7. Now, using your `my_pixel_put` function, put a **white** pixel in the **middle** of your image. (10 mins) - > Code: `my_pixel_put(&img, 800/2, 480/2, 0xFFFFFF);` -8. Our image is all ready to be shown! Let's look at `mlx_put_image_to_window`. What parameters does it take? - Add the function to your code and see if your little white dot is showing in your window. (10 mins) - > Prototype: `int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y);` - -***Break (5 mins)*** - -### More Pixels -Let's get fancier. Now we're gonna try drawing *lines*. (25 mins) -1. Draw a single horizontal white line running across the middle of the entire screen. You'll need to call `my_pixel_put` in a loop. (15 mins) - ``` - // example code answer - int x = 0; - while (x < screen_width) - { - my_pixel_put(&img, x, screen_height / 2, 0xFFFFFF); - x++; - } - ``` -2. Now draw a single vertical white line down the middle of the entire screen. (10 mins)\ - You should end up with what looks like a crosshair in your window. - ``` - // example code answer - int y = 0; - while (y < screen_height) - { - my_pixel_put(&img, screen_width / 2, y, 0xFFFFFF); - y++; - } - ``` - -### Events & Hooks -Having to do `Ctrl-C` every time is probably getting annoying. Let's learn how to close the window when the 'X' button of your window (not your keyboard) is pressed. (35 mins) -1. Hooks, along with events, are vital to making your program interactive. They allow you to intercept keyboard or mouse events and respond to them. You can think of hooks as functions that get called when an event occurs.\ - What is the prototype for `mlx_hook`? *(Hint: you may have to look it up in mlx.h)* (5 mins) - > Prototype: `int mlx_hook(t_win_list *win, int x_event, int x_mask, int (*funct)(), void *param);` -2. miniLibX uses the event codes and masks set out in the [**X11** library](https://code.woboq.org/qt5/include/X11/X.h.html). What do event codes and masks do? (5 mins) - - Here's something that might help you understand: [event processing](https://tronche.com/gui/x/xlib/events/processing-overview.html) - > Passing event codes & masks allows you to specify which events you want to be notified of -3. What are the **event codes** and **masks** for key presses, key releases, and the 'X' close button? (10 mins) - - Here's a really helpful resource: [handling mouse and keys](https://github.com/VBrazhnik/FdF/wiki/How-to-handle-mouse-buttons-and-key-presses%3F) - - **Watch out**: the Linux event code for the 'X' close button is different than on macOS. Whereas Mac users can use the code for "DestroyNotify", Linux (and WSL) users will need the code for "ClientMessage". - > Codes: press = 2, release = 3, X button = 17 (Mac) or 33 (Linux) - > Masks: press = `1L << 0`, release = `1L << 1`, X button = `1L << 17` -4. Write a function that: (10 mins) - - takes as its argument a **pointer to a struct** containing at least your mlx pointer and window pointer *(either make a new struct or expand your existing one)*; - - destroys your window and exits your program. -5. Add a call to `mlx_hook` in your main that calls this exiting function when the 'X' button is pressed. (5 mins) - - Does your window close now when you press the 'X' close button on your window? - ``` - // example code answer - int exiter(t_data *game) - { - mlx_destroy_window(game->mlx_ptr, game->win); - exit(0); - } - int main() - { - ... - mlx_hook(game.win, 33, 1L << 17, exiter, &game); // replace 33 with 17 for Mac - } - ``` - -### Bonus -Let's get some movement on screen: make your crosshair move in 4 directions! - -First, however, let's make our crosshair smaller, because who needs a crosshair that big? -1. Expand your struct to include **at least** the following variables you'll need for your drawing function: - - object width & height; - - starting x & y positions (i.e. the coordinates of the leftmost pixel of your crosshair). -2. Make a `draw_crosshair` function that: - - accepts your data/game struct as its parameter; - - can render a crosshair of a particular **width** and **height**, instead of only the height/width of the screen; - - renders that crosshair in the **middle of the screen** *(you'll have to do some math using the object dimensions and starting positions, sorry)*; - - calls `mlx_put_image_to_window` at the end. -3. Get a **30 x 30** pixel crosshair onto your window. Did it work? - > Note: there will be different ways of solving this. I've included some example non-optimised solutions on the last page of this document. - > Example formula for computing starting x of crosshair (i.e. the leftmost pixel of the horizontal line): - `start_x = (game.screen_width - game.obj.width) / 2` - -Now let's hook into keyboard events! -1. Add a call to `mlx_hook` in your main that calls a function `keypress` when keys are...well, pressed. -2. Write that `keypress` function that: - - calls your exit function when the `ESC` key is pressed; - - moves the crosshair up, down, left, and right when the corresponding key is pressed. - - you can choose to use the arrow keys or `W`-`A`-`S`-`D` keys - - I've included helpful diagrams below for the keycodes you'll need. -3. Add a call to `mlx_loop_hook` in your main that calls a function to render the new image with the modified object coordinates. -4. Do you now have a crosshair that can move across your screen? - - If you're seeing a trail of crosshairs, you're probably not rendering the background each time. - - If your program is crashing when you hit one of the walls, perhaps you should add checks to your keypress function. - -macOS\ -![macOS key codes](https://i.imgur.com/CNPRkMg.png)\ - -Linux\ -![Linux key codes](https://i.imgur.com/a6yVUhm.png) - -``` -// example code for draw_crosshair & keypress functions -void draw_crosshair(t_data *game) -{ - for (int x = 0; x < game->screen_width; x++) - { - for (int y = 0; y < game->screen_height; y++) - my_pixel_put(game->img, x, y, BLACK); // draw background - } - int x_end = game->obj.start.x + game->obj.width; // end of horizontal line - for (int x = game->obj.start.x; x < x_end; x++) - { - int y = game->obj.start.y; - int y_end = y + 1; - if (x == x_end - game->obj.width / 2) // for vertical line - { - y -= game->obj.height / 2; // top of vertical line - y_end = y + game->obj.height; // bottom of vertical line - } - while (y < y_end) - { - my_pixel_put(game->img, x, y, WHITE); // draw crosshair - y++; - } - mlx_put_image_to_window(game->mlx_ptr, game->win, game->img->img_ptr, 0, 0); - } - - int keypress(int keycode, t_data *game) - { - if (keycode == ESC) - exiter(game); - else if (keycode == MV_UP) - game->obj.start.y -= 10; - else if (keycode == MV_DW) - { - if (game->obj.start.y + (game->obj.height / 2) + 10 - <= game->screen_height) - game->obj.start.y += 10; - } - else if (keycode == MV_LF) - game->obj.start.x -= 10; - else if (keycode == MV_RT) - { - if (game->obj.start.x + game->obj.width + 10 <= game->screen_width) - game->obj.start.x += 10; - } - return (0); - } - - int main(void) - { - ... - game.obj.height = 30, game.obj.width = 30; - game.obj.start.x = (game.screen_width - game.obj.width) / 2; - game.obj.start.y = game.screen_height / 2; - draw_crosshair(&game); - - mlx_hook(game.win, 33, 1L << 17, exiter, &game); // event code on Linux - mlx_hook(game.win, 2, 1L << 0, keypress, &game); - mlx_loop_hook(game.mlx_ptr, &updater, &game); - mlx_loop(game.mlx_ptr); - } -``` \ No newline at end of file diff --git a/spark-sessions/minilibx/SparkSession - minilibx setup.md b/spark-sessions/minilibx/SparkSession - minilibx setup.md deleted file mode 100644 index d995c0d..0000000 --- a/spark-sessions/minilibx/SparkSession - minilibx setup.md +++ /dev/null @@ -1,48 +0,0 @@ -## Setting Up miniLibX -*updated: 04/03/2021* - -During the Spark Session, we'll be writing a little program that can open windows, draw pixels, and maybe even move pixels! - -Before we can do that, we need to set up the miniLibX library and **link** the compiled library with our source code. -1. Download the minilibx library into the **root directory**. - - For macOs: from intra, whichever version (OpenGL/mms_beta) that works with your system - - For Linux: from the [42Paris repo](https://github.com/42Paris/minilibx-linux) - - **The next steps assume you've called the folders `mlx` or `mlx_linux`**. -2. Create a `main.c` in the root directory with the following content (remember to include the **mlx.h header**!): - ``` - int main(void) - { - void *mlx_ptr; - - mlx_ptr = mlx_init(); - return (0); - } - ``` - This will help you check at the end if you're linking your mlx correctly to your source files. -3. Now let's create our own Makefile in the root directory, which will compile our `main.c` into a program (name it whatever you want). - Having a Makefile makes our lives easier. - Add the required rules - `$(NAME)`, `clean`, `fclean`, `re`, `all`. - - Here's a [helpful guide on Makefiles](https://noahloomans.com/tutorials/makefile/) written by another student, Noah Loomans. -4. Compile the mlx library, so that you get a `libmlx.dylib` (if you're using the mms_beta version of mlx) or `libmlx.a` file (for Linux & OpenGL versions). - For macOS mms_beta library: you'll need to move `libmlx.dylib` into the same directory as your build target (as it's a dynamic library). - **Tip: you could have your Makefile do all this too.** *Hint: `make -C dir`* -5. Using miniLibX requires that we link the necessary **internal API’s**. Here's what you should add to your project Makefile: - - Once again, **the following commands assume you've named your mlx folder `mlx` (for Mac) or `mlx_linux`**. Also, `OBJ` here refers to the **object files of your project**, e.g. `main.o`, not the mlx files. - - For macOS: *(make sure the compilation command is on one line)* - ``` - $(NAME): $(OBJ) - $(CC) $(OBJ) -Lmlx -lmlx -framework OpenGL -framework AppKit -o $(NAME) - ``` - - For Linux: first run `sudo apt-get install gcc make xorg libxext-dev libbsd-dev` to install the required `xorg`, `libxext-dev`, and `libbsd-dev` dependencies - ``` - $(NAME): $(OBJ) - $(CC) $(OBJ) -Lmlx_linux -lmlx -lXext -lX11 -lm -lz -o $(NAME) - ``` -6. Additional steps if you’re doing this through **Windows Subsystem for Linux**: you need to install [Xming](https://sourceforge.net/projects/xming/) first. - - Once Xming is installed, exit Xming and launch XLaunch. Choose the following options:\ - `Multiple windows` -> `Start no client` -> Enable `"No access control"` -> `Finish` - - Then execute this command: `export DISPLAY=localhost:0.0` - - You can check if everything’s working by running `sudo apt-get install x11-apps` and then executing `xeyes`. - - **Note**: XLaunch has to be active and the `export DISPLAY` command above must have been run before you can launch graphic programs using miniLibX. -7. Now try running `make` in your project directory. Does your program compile without errors? Your program itself won't do anything for now. If everything works, great! Now you're ready for the SparkSession and your project! \ No newline at end of file diff --git a/spark-sessions/minilibx/SparkSession - minilibx.md b/spark-sessions/minilibx/SparkSession - minilibx.md deleted file mode 100644 index 8a4712f..0000000 --- a/spark-sessions/minilibx/SparkSession - minilibx.md +++ /dev/null @@ -1,122 +0,0 @@ -# Spark Session: minilibx -*updated: 04/03/2021* - -Session description: -> Learn the basics of working with miniLibX - -This tutorial was written with help from Harm Smits and Jelle van Snik's [MiniLibX tutorial](https://harm-smits.github.io/42docs/libs/minilibx). - -## Topics -1. Window Management -2. Pixel Putting -3. More Pixels -4. Events & Hooks - -### Window Management -Our first step will be to open up some windows! (30 mins) -1. In the set-up instructions, I gave you some code for your `main.c` that included a call to `mlx_init`. - But what does it do and what is its prototype? What does it return? (5 mins) - This link might help -> [prototypes](https://harm-smits.github.io/42docs/libs/minilibx/prototypes.html) -2. Let's try opening a small empty window. (10 mins) - - What is the prototype for `mlx_new_window` and what does it return? - - How would you declare and initalize it? - - Now create a window with a width of **800**, height of **480**, and a title of **"My first window"**. -3. What happens if you compile and run the program at this point? Your window should have only popped up for a moment. - To make it stay longer, we need to use `mlx_loop`. (15 mins) - - What does it do and what is its prototype? - - Once you understand that, add `mlx_loop` to your code. - - Do you now get a window that stays open? Press `Ctrl-C` to close it when you're done admiring your work. - - **Important**: `mlx_loop` should be called last in your code. Do you know why? - -***Break (5 mins)*** - -### Pixel Putting -Time to put something on that empty window. (60 mins) -1. Rather than [inefficiently pushing pixels](https://harm-smits.github.io/42docs/libs/minilibx/getting_started.html#writing-pixels-to-a-image) one by one to the window using `mlx_pixel_put` , we should draw our pixels onto an **image** first, then push that image to our window. So we need `mlx_new_image`. (10 mins) - - What is `mlx_new_image`'s prototype and return? - - Once you understand that, go ahead and initialise an image with a size of **800 x 480**. -2. In order to know where we can put our pixels, we need to get the **memory address** of our image. That's where `mlx_get_data_addr` comes in. What arguments does it take and what does it return? (10 mins) -3. Since the function requires a lot of extra variables, let's keep things neat by using a struct for our image data. (10 mins) - ``` - typedef struct s_img - { - void *img_ptr; - char *address; - int bits_per_pixel; - int line_size; - int endian; - } t_img; - ``` - - Notice that we shifted the image pointer into the struct. Adjust your initialisation of `mlx_new_image` accordingly. - - Then call `mlx_get_data_addr` and pass it the appropriate arguments/references. -5. As explained in point #1, `mlx_pixel_put` is rather inefficient, so here's a much faster version to use in your code: (10 mins) - ``` - void my_pixel_put(t_img *img, int x, int y, unsigned int colour) - { - char *dst; - int offset; - - offset = y * img->line_size + x * (img->bits_per_pixel / 8); - dst = img->address + offset; - *(unsigned int *)dst = colour; - } - ``` - - What is this function doing? What is `offset`? -7. Now, using your `my_pixel_put` function, put a **white** pixel in the **middle** of your image. (10 mins) -8. Our image is all ready to be shown! Let's look at `mlx_put_image_to_window`. What parameters does it take? - Add the function to your code and see if your little white dot is showing in your window. (10 mins) - -***Break (5 mins)*** - -### More Pixels -Let's get fancier. Now we're gonna try drawing *lines*. (25 mins) -1. Draw a single horizontal white line running across the middle of the entire screen. You'll need to call `my_pixel_put` in a loop. (15 mins) -2. Now draw a single vertical white line down the middle of the entire screen. You should end up with what looks like a crosshair in your window. (10 mins) - -  -### Events & Hooks -Having to do `Ctrl-C` every time is probably getting annoying. Let's learn how to close the window when the 'X' button of your window (not your keyboard) is pressed. (35 mins) -1. Hooks, along with events, are vital to making your program interactive. They allow you to intercept keyboard or mouse events and respond to them. You can think of hooks as functions that get called when an event occurs. - What is the prototype for `mlx_hook`? *(Hint: you may have to look it up in mlx.h)* (5 mins) -2. miniLibX uses the event codes and masks set out in the [**X11** library](https://code.woboq.org/qt5/include/X11/X.h.html). What do event codes and masks do? (5 mins) - - Here's something that might help you understand: [event processing](https://tronche.com/gui/x/xlib/events/processing-overview.html) -3. What are the **event codes** and **masks** for key presses, key releases, and the 'X' close button? (10 mins) - - Here's a really helpful resource: [handling mouse and keys](https://github.com/VBrazhnik/FdF/wiki/How-to-handle-mouse-buttons-and-key-presses%3F) - - **Watch out**: the Linux event code for the 'X' close button is different than on macOS. Whereas Mac users can use the code for "DestroyNotify", Linux (and WSL) users will need the code for "ClientMessage". -4. Write a function that: (10 mins) - - takes as its argument a **pointer to a struct** containing at least your mlx pointer and window pointer *(either make a new struct or expand your existing one)*; - - destroys your window and exits your program. -5. Add a call to `mlx_hook` in your main that calls this exiting function when the 'X' button is pressed. (5 mins) - - Does your window close now when you press the 'X' close button on your window? - -### Bonus -Let's get some movement on screen: make your crosshair move in 4 directions! - -First, however, let's make our crosshair smaller, because who needs a crosshair that big? -1. Expand your struct to include **at least** the following variables you'll need for your drawing function: - - object width & height; - - starting x & y positions (i.e. the coordinates of the leftmost pixel of your crosshair). -2. Make a `draw_crosshair` function that: - - accepts your data/game struct as its parameter; - - can render a crosshair of a particular **width** and **height**, instead of only the height/width of the screen; - - renders that crosshair in the **middle of the screen** *(you'll have to do some math using the object dimensions and starting positions, sorry)*; - - calls `mlx_put_image_to_window` at the end. -3. Get a **30 x 30** pixel crosshair onto your window. Did it work? - -Now let's hook into keyboard events! -1. Add a call to `mlx_hook` in your main that calls a function `keypress` when keys are...well, pressed. -2. Write that `keypress` function that: - - calls your exit function when the `ESC` key is pressed; - - moves the crosshair up, down, left, and right when the corresponding key is pressed. - - you can choose to use the arrow keys or `W`-`A`-`S`-`D` keys. - - I've included helpful diagrams below for the keycodes you'll need. -3. Add a call to `mlx_loop_hook` in your main that calls a function to render the new image with the modified object coordinates. -4. Do you now have a crosshair that can move across your screen? - - If you're seeing a trail of crosshairs, you're probably not rendering the background each time. - - If your program is crashing when you hit one of the walls, perhaps you should add checks to your keypress function. - -macOS\ -![macOS key codes](https://i.imgur.com/CNPRkMg.png) - -Linux\ -![Linux key codes](https://i.imgur.com/a6yVUhm.png) \ No newline at end of file diff --git a/spark-sessions/minishell/SparkSession - minishell - mod.md b/spark-sessions/minishell/SparkSession - minishell - mod.md deleted file mode 100644 index 8f31a15..0000000 --- a/spark-sessions/minishell/SparkSession - minishell - mod.md +++ /dev/null @@ -1,240 +0,0 @@ -# Spark Session: minishell -*updated: 06/04/2021* - -Project description: -> Create a simple shell - -## Topics -1. Processes -2. fork -3. wait -4. execve -5. dup & dup2 -6. pipe - -### Processes -Before we get into how to work with processes, it's handy to understand what we actually mean by "process". -1. What is a process? (5 mins) - > A process is a program in execution, an entity which implements a set of instructions (given by the program) in the system. - > Processes are identified by their unique process ID (which is recycled after it terminates). - > The `exec` family of functions implements the system call that creates a process. - -A process is its own separate entity with its own defined **memory space**. This memory space is what is duplicated by `fork` and rewritten by `exec`, which we'll get to in a bit.\ - Here's a diagram showing how this memory is divided:\ - ![process memory](https://i.imgur.com/nxmmQl3.png) - -To put it in really simple terms, you can think of a process like a struct — a collection of information bound to an entity. -This information includes the process ID, open files, its status, etc. You can read more about that [here](https://courses.cs.washington.edu/courses/cse451/04wi/section/ab/notes/fork/) later. - -### fork -`fork()` creates a new process — called the **child process** — by duplicating the calling process (**the parent process**). -1. What is the prototype of `fork()`? What does the function return? (10 mins) - > `pid_t fork(void);` - > On success, returns the PID of child process in the parent, and 0 in the child. On failure, -1 is returned in the parent, no child process is created. - - - How could you use the function return to identify if you are in the child or parent process? - > By checking if the PID is 0, you can know if you're in the child process. - - - Is `fork`'s return the same as the child's PID? - > Yes in the parent. Not in the child; child's actual PID can be seen by running `getpid`. -2. Which of the following is copied from the parent process to the child process? Which are not? (10 mins) - - Data (the content of the process' memory space) - - Location in memory - - Process ID - - Open file descriptors - > Data: same data in their respective memory spaces (at time of fork) - > Location in memory: not same - separate memory spaces - > Process ID: not same - child has unique PID that doesn't match any of existing process group or session - > Open FDs: same - child inherits copies of the parent's set of open file descriptors. Each FD in the child refers to the same open FD as the corresponding FD in the parent. -3. Let's see some of these characteristics in action. (20 mins) - - Write a program that: - - initialises an int `x` to **5**; - - calls `fork()` and then prints its return value in a statement `"fork returned: %d\n"`; - - checks for failed forks; - - if in the **child** process: **decrements** `x` by 1, prints `"This line is from child, x is %d\n"`, and then **returns** 0; - - else if in the **parent** process: **increments** `x` by 1 and then prints `"This line is from parent, x is %d\n"`. - - You should see how the data (the variable `x` in this case) starts with the same initial value in both processes, but that changes to this variable in one process **does not affect** the variable in another process.\ - ![example output](https://i.imgur.com/jOTXMCL.png)\ - *example output - your output order may vary* - - Here we've specified that child should `return` when it's done. What happens if we comment that out? Try putting another `"x is %d"` statement at the **end of your main** to see. - - You should see how the child and parent processes then both execute the code that follows, returning to a common point in the program. Whether or not you want that depends on the program's purpose. - -In this case, the parent and the child process execute concurrently. The order of your output might also be jumbled between child and parent, depending on how your OS handles the processes. - -***Break (5 mins)*** - -### wait -It's also possible to have your parent process wait on its child processes to terminate. You do this by calling `wait()` in the parent process. This **[synchronises](https://flylib.com/books/en/1.311.1.42/1/)** the parent and child process. -1. What is the prototype of `wait()`? What information is stored in the `int` whose address we pass as a parameter to the function? (5 mins) - > `pid_t wait(int *wstatus);` - > if not NULL, `wstatus` contains the child process' status information, which can be checked with macros like `WIFSIGNALED` and `WIFEXITED`. -2. Calling `wait()` (or `waitpid()`) in the parent process prevents what's called **"zombie processes"**. What does this mean? (10 mins) - > Calling these functions collects the child's exit status from the kernel process table ("reaps" it), allowing the system to release the child's resources if it has terminated. - > Not performing wait in the parent process leaves the terminated child in a zombie state. Because the kernel continues to keep info about the terminated child process and this takes up space. If you have too many of these, you could run out of space for new processes. -3. Let's add `wait()` to the code you wrote earlier. (10 mins) - - Create an int variable, for example `w_status`, to be passed to `wait()`. - - In the **parent process** code block, call `wait()` before anything else. *Remember to pass it the address of your `w_status` int.* - - Make sure the **child process** is calling `return` when it's done. - - Use one of the macros to check if the child process **terminated normally**. If so, print `"Child process exited with status: %d\n"`. Use one of the other macros to get the exit status. - > if (WIFEXITED(w_status)) { - > printf("Child process exited with status: %d\n", WEXITSTATUS(w_status));} - - Try tweaking the argument you pass to the `return()` call in your child process. Does the output change accordingly? - ![example output](https://i.imgur.com/cFZAZFp.png)\ - *example output* - -Here's a fun short explanation about zombie processes for later: [understanding zombie processes](https://youtu.be/xJ8KenZw2ag) - -### execve -The `exec()` family of functions allows us to **replace** the current process with a new program.\ -No new process is created; the PID remains the same. The functions simply have the existing process execute a new program. -1. What is the prototype of `execve()`? (10 mins) - - Break down each of function parameters. What does each mean? - > `int execve(const char *pathname, char *const argv[], char *const envp[]);` - > pathname: binary executable, for example "/bin/ls" - > argv: an array of pointers to strings to be passed to the program as its arguments. - > envp: the environment variables you want to pass to the new program, which can differ from the env variables passed to the calling process - > extra note: the 'v' in exec**v**e refers to the program args being passed as a vector (aka array). The 'e' in execv**e** refers to the ability to specify the environment. -2. What does `execve()` return? (5 mins) - > On success, it doesn't return. Only returns on failure, giving -1 and setting erno appropriately. -3. To see how to execute a new program from within our child process, let's try using `execv()` (i.e. `execve` without the "e" environment option). (15 mins) - - **Note**: for the purposes of keeping the exercise simple, we won't pass a specific environment. Also we'll hard-code our program arguments. You won't do this in your actual minishell of course. - - At the beginning of your main, declare a `char *argv[3]`. - - Initialize `argv[0]` to `"/bin/ls"`. - - Initialize `argv[1]` to `"-l"`. - - Initialize `argv[2]` to `NULL`. - - *Do you know why these arguments are made in this order?* - > By convention, argv[0] should contain the filename associated with the file - being executed. The argv array must be terminated by a NULL pointer. - - - In your child process, below the `"This line is from child"` statement, **instead of return()** we'll call `execv()`, passing it `"/bin/ls"` and your `argv` array. - - Below the `execv` call, place another print statement, `"This line is from child after execv"`. - - Does your program execute `ls` with the `-l` list option when you run it? Does the "after execv" statement print? - > Contents of folder should be outputted after "This line is from child", but the second print statement is never run because exec doesn't return if successful. - -***Break (5 mins)*** - -### dup & dup2 -`dup()` and `dup2()` create a **copy** of a file descriptor. -1. What is the prototype for `dup()`? What about `dup2()`? What do both return? (5 mins) - > `int dup(int oldfd);` - > `int dup2(int oldfd, int newfd);` - > on success, returns newfd. on failure, -1 -2. What are some differences between `dup` and `dup2` with regards to the new file descriptor? (5 mins) - - Here's a diagram to help you visualize the functions better:\ - ![dup](https://i.imgur.com/iJ7X39I.png) - > `dup2` uses specified newfd, `dup` uses lowest-numbered unused fd. - > `dup2`: If newfd was previously open, it is silently closed before being reused. -3. What do the new and old file descriptor share? (5 mins) - > both refer to the same open file description and thus share file offset and file status flags. the two fds can therefore be used interchangeably. -4. Now let's write a program that: (15 mins) - - opens a `test.txt` file with the following flags: `O_CREAT | O_TRUNC | O_RDWR, 0644` - - Do you understand what these [flags](https://man7.org/linux/man-pages/man2/open.2.html) do? Because this is one of the flag combinations you'll also use in minishell. - - Here's a handy [permissions calculator](http://permissions-calculator.org/). - - saves the `open` return in an int `fd`; - - creates another int, for example `dup_fd`; - - calls `dup()`, giving it `fd` as argument and saving its return in `dup_fd`; - - passes `fd` as the 1st argument to `write()`, with the string `"This will be written to the test file\n"`. - - passes `dup_fd` as the 1st argument to `write()`, with the string `"This will also be written to the test file\n"`.\ - ![example output](https://i.imgur.com/ikWDFXL.png?1) - > `write(fd, "This will be written to the test file\n", 38);` - > `write(dup_fd, "This will also be written to the test file\n", 43);` -5. The real significance of `dup` and `dup2` to minishell is when we use them to **redirect** our output and/or input. For example, when the output of a command is redirected into a file or into a **pipe** (more on that later). (10 mins) - - Now, using `dup2`, turn the fd `1` (that is, stdout) into a copy of our test.txt `fd`. - - Call `write()` again, outputting `"This isn't being printed on stdout\n"` onto stdout (1). - - Does anything get printed onto your terminal when you run the program? - > `dup2(fd, 1);` - > `write(1, "This isn't being printed on stdout\n", 35);` - > string should be printed in test.txt instead of terminal - -## Bonus -### pipe -`pipe()` allows data to be passed from one process to another. -This "pipeline" between processes is **unidirectional**, meaning data flows in one direction. Therefore, you have one end of the pipe that reads data and one end of the pipe that writes data.\ - ![one-way pipe](https://www.tutorialspoint.com/inter_process_communication/images/pipe_with_one.jpg) -1. What is the prototype of `pipe()`? What is being stored in the int array you're passing it? (10 mins) - > `int pipe(int pipefd[2]);` - > pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. -2. If you're using a pipe to pass data from one process to a second process, which `pipefd` would the 1st process **write** to? Which would the 2nd process **read** from? (10 mins)\ -![pipe in out](https://i.imgur.com/DzjZwSl.png) - > the read and write actions defined by a pipe() call are from the perspective of the two processes using the pipe, not the pipe itself. - > therefore, the 1st process would write to `pipefd[1]` and the 2nd process would read from `pipefd[0]`. -3. Let's try using `pipe` in combination with `fork` and `wait`! Write a program that: (15 mins) - - takes command-line arguments (i.e. `int argc, char **argv`); - - creates a **pipe**; - - then **forks** to create a child process; - - the parent process: - - should **close** the pipe end that it doesn't need; - - **writes** argv[1] to the correct end of the pipe; - - **closes** the remaining pipe end; - - **waits** for its child processes to terminate and checks their status. - - the child process: - - should **close** the pipe end that it doesn't need; - - in a loop, **reads** the string from the pipe, one byte at a time; - - calls `toupper()` (include `ctype.h`) on the read char; - - **writes** the converted char to stdout; - - writes a newline to stdout; - - **closes** the remaining pipe end.\ - ![pipe example](https://i.imgur.com/40cUyDo.png) -4. Why do we close the **pipe ends we don't use** at the start? Why do we close the pipe end we used after we're done? - ![closing ends](https://chensunmac.gitbooks.io/csc209-practical-programming/content/assets/pipe2.png) - > If all fds referring to the write end of a pipe are closed, end-of-file is sent to `read`, which then knows to stop reading. - > If all fds referring to the read end of a pipe are closed, attempting `write` on the other end generates a SIGPIPE signal. - > Unnecessary duplicate fds should thus be closed, to ensure that EOF and SIGPIPE are delivered when appropriate, preventing a hanging state. - -That was a simple exercise to show you how data can be passed through pipes and interacted with within child processes. - -Things get even more mind-blowing when you throw `dup`/`dup2` into the mix. - -## Tips -Here's a more detailed explanation about data flows through pipes, with handy diagrams: [pipes, forks, & dups](http://www.rozmichelle.com/pipes-forks-dups/) - -And here's a couple other things that could be helpful to look into for your project: -- abstract syntax tree -- finite state machines - - -```c -// example solution for pipe exercise -#include -#include -#include -#include -#include - -int main(int ac, char **av) { - int pipefd[2]; - char buf; - - if (ac != 2){ - printf("incorrect number of args\n"); - return (1); - } - if (pipe(pipefd) < 0) { - printf("pipe failed\n"); - return (1); - } - pid_t pid = fork(); - if (pid < 0) - printf("fork failed\n"); - else if (pid == 0) { - close(pipefd[1]); - while (read(pipefd[0], &buf, 1) > 0) { - char up = toupper(buf); - write(STDOUT_FILENO, &up, 1); - } - write(STDOUT_FILENO, "\n", 1); - close(pipefd[0]); - return(0); - } - else { - int w_status = 0; - close(pipefd[0]); - write(pipefd[1], av[1], strlen(av[1])); - close(pipefd[1]); - wait(&w_status); - if (WIFEXITED(w_status)) - printf("Child process exited with status: %d\n", WEXITSTATUS(w_status)); - } - return (0); -} -``` diff --git a/spark-sessions/minishell/SparkSession - minishell.md b/spark-sessions/minishell/SparkSession - minishell.md deleted file mode 100644 index 285b3d2..0000000 --- a/spark-sessions/minishell/SparkSession - minishell.md +++ /dev/null @@ -1,146 +0,0 @@ -# Spark Session: minishell -*updated: 30/06/2021* - -Project description: -> Create a simple shell - -## Topics -1. Processes -2. fork -3. wait -4. execve -5. dup & dup2 -6. pipe - -### Processes -Before we get into how to work with processes, it's handy to understand what we actually mean by "process". -1. What is a process? (5 mins) - -A process is its own separate entity with its own defined **memory space**. This memory space is what is duplicated by `fork` and rewritten by `exec`, which we'll get to in a bit.\ - Here's a diagram showing how this memory is divided:\ - ![process memory](https://i.imgur.com/nxmmQl3.png) - -To put it in really simple terms, you can think of a process like a struct — a collection of information bound to an entity. -This information includes the process ID, open files, its status, etc. You can read more about that [here](https://courses.cs.washington.edu/courses/cse451/04wi/section/ab/notes/fork/) later. - -### fork -`fork()` creates a new process — called the **child process** — by duplicating the calling process (**the parent process**). -1. What is the prototype of `fork()`? What does the function return? (10 mins) - - How could you use the function return to identify if you are in the child or parent process? - - Is `fork`'s return the same as the child's PID? -2. Which of the following is copied from the parent process to the child process? Which are not? (10 mins) - - Data (the content of the process' memory space) - - Location in memory - - Process ID - - Open file descriptors -3. Let's see some of these characteristics in action. (20 mins) - - Write a program that: - - initialises an int `x` to **5**; - - calls `fork()` and then prints its return value in a statement `"fork returned: %d\n"`; - - checks for failed forks; - - if in the **child** process: **decrements** `x` by 1, prints `"This line is from child, x is %d\n"`, and then **returns** 0; - - else if in the **parent** process: **increments** `x` by 1 and then prints `"This line is from parent, x is %d\n"`. - - You should see how the data (the variable `x` in this case) starts with the same initial value in both processes, but that changes to this variable in one process **does not affect** the variable in another process.\ - ![example output](https://i.imgur.com/jOTXMCL.png)\ - *example output - your output order may vary* - - Here we've specified that child should `return` when it's done. What happens if we comment that out? Try putting another `"x is %d"` statement at the **end of your main** to see. - - You should see how the child and parent processes then both execute the code that follows, returning to a common point in the program. Whether or not you want that depends on the program's purpose. - -In this case, the parent and the child process execute concurrently. The order of your output might also be jumbled between child and parent, depending on how your OS handles the processes. - -***Break (5 mins)*** - -### wait -It's also possible to have your parent process wait on its child processes to terminate. You do this by calling `wait()` in the parent process. This **[synchronises](https://flylib.com/books/en/1.311.1.42/1/)** the parent and child process. -1. What is the prototype of `wait()`? What information is stored in the `int` whose address we pass as a parameter to the function? (5 mins) -2. Calling `wait()` (or `waitpid()`) in the parent process prevents what's called **"zombie processes"**. What does this mean? (10 mins) -3. Let's add `wait()` to the code you wrote earlier. (10 mins) - - Create an int variable, for example `w_status`, to be passed to `wait()`. - - In the **parent process** code block, call `wait()` before anything else. *Remember to pass it the address of your `w_status` int.* - - Make sure the **child process** is calling `return` when it's done. - - Use one of the macros to check if the child process **terminated normally**. If so, print `"Child process exited with status: %d\n"`. Use one of the other macros to get the exit status. - - Try tweaking the argument you pass to the `return()` call in your child process. Does the output change accordingly? - ![example output](https://i.imgur.com/cFZAZFp.png)\ - *example output* - -Here's a fun short explanation about zombie processes for later: [understanding zombie processes](https://youtu.be/xJ8KenZw2ag) - -### execve -The `exec()` family of functions allows us to **replace** the current process with a new program.\ -No new process is created; the PID remains the same. The functions simply have the existing process execute a new program. -1. What is the prototype of `execve()`? (10 mins) - - Break down each of function parameters. What does each mean? -2. What does `execve()` return? (5 mins) -3. To see how to execute a new program from within our child process, let's try using `execv()` (i.e. `execve` without the "e" environment option). (15 mins) - - **Note**: for the purposes of keeping the exercise simple, we won't pass a specific environment. Also we'll hard-code our program arguments. You won't do this in your actual minishell of course. - - At the beginning of your main, declare a `char *argv[3]`. - - Initialize `argv[0]` to `"/bin/ls"`. - - Initialize `argv[1]` to `"-l"`. - - Initialize `argv[2]` to `NULL`. - - *Do you know why these arguments are made in this order?* - - In your child process, below the `"This line is from child"` statement, **instead of return()** we'll call `execv()`, passing it `"/bin/ls"` and your `argv` array. - - Below the `execv` call, place another print statement, `"This line is from child after execv"`. - - Does your program execute `ls` with the `-l` list option when you run it? Does the "after execv" statement print? - -***Break (5 mins)*** - -### dup & dup2 -`dup()` and `dup2()` create a **copy** of a file descriptor. -1. What is the prototype for `dup()`? What about `dup2()`? What do both return? (5 mins) -2. What are some differences between `dup` and `dup2` with regards to the new file descriptor? (5 mins) - - Here's a diagram to help you visualize the functions better:\ - ![dup](https://i.imgur.com/iJ7X39I.png) -3. What do the new and old file descriptor share? (5 mins) -4. Now let's write a program that: (15 mins) - - opens a `test.txt` file with the following flags: `O_CREAT | O_TRUNC | O_RDWR, 0644` - - Do you understand what these [flags](https://man7.org/linux/man-pages/man2/open.2.html) do? Because this is one of the flag combinations you'll also use in minishell. - - Here's a handy [permissions calculator](http://permissions-calculator.org/). - - saves the `open` return in an int `fd`; - - creates another int, for example `dup_fd`; - - calls `dup()`, giving it `fd` as argument and saving its return in `dup_fd`; - - passes `fd` as the 1st argument to `write()`, with the string `"This will be written to the test file\n"`. - - passes `dup_fd` as the 1st argument to `write()`, with the string `"This will also be written to the test file\n"`.\ - ![example output](https://i.imgur.com/ikWDFXL.png?1) -5. The real significance of `dup` and `dup2` to minishell is when we use them to **redirect** our output and/or input. For example, when the output of a command is redirected into a file or into a **pipe** (more on that later). (10 mins) - - Now, using `dup2`, turn the fd `1` (that is, stdout) into a copy of our test.txt `fd`. - - Call `write()` again, outputting `"This isn't being printed on stdout\n"` onto stdout (1). - - Does anything get printed onto your terminal when you run the program? - -## Bonus -### pipe -`pipe()` allows data to be passed from one process to another.\ -This "pipeline" between processes is **unidirectional**, meaning data flows in one direction. Therefore, you have one end of the pipe that reads data and one end of the pipe that writes data.\ - ![one-way pipe](https://www.tutorialspoint.com/inter_process_communication/images/pipe_with_one.jpg) -1. What is the prototype of `pipe()`? What is being stored in the int array you're passing it? (10 mins) -2. If you're using a pipe to pass data from one process to a second process, which `pipefd` would the 1st process **write** to? Which would the 2nd process **read** from? (10 mins)\ -![pipe in out](https://i.imgur.com/DzjZwSl.png) -3. Let's try using `pipe` in combination with `fork` and `wait`! Write a program that: (15 mins) - - takes command-line arguments (i.e. `int argc, char **argv`); - - creates a **pipe**; - - then **fork** to create a child process; - - the parent process: - - should **close** the pipe end that it doesn't need; - - **writes** argv[1] to the correct end of the pipe; - - **closes** the remaining pipe end; - - **waits** for its child processes to terminate and checks their status. - - the child process: - - should **close** the pipe end that it doesn't need; - - in a loop, **reads** the string from the pipe, one byte at a time; - - calls `toupper()` (include `ctype.h`) on the read char; - - **writes** the converted char to stdout; - - writes a newline to stdout; - - **closes** the remaining pipe end.\ - ![pipe example](https://i.imgur.com/40cUyDo.png) -4. Why do we close the **pipe ends we don't use** at the start? Why do we close the pipe end we used after we're done? - ![closing ends](https://chensunmac.gitbooks.io/csc209-practical-programming/content/assets/pipe2.png) - -That was a simple exercise to show you how data can be passed through pipes and interacted with within child processes. - -Things get even more mind-blowing when you throw `dup`/`dup2` into the mix. - -## Tips -Here's a more detailed explanation about data flows through pipes, with handy diagrams: [pipes, forks, & dups](http://www.rozmichelle.com/pipes-forks-dups/) - -And here's a couple other things that could be helpful to look into for your project: -- abstract syntax tree -- finite state machines