├── .gitignore ├── .gitmodules ├── README.md ├── SparkSession_-__template_.md ├── hooks └── pre-commit ├── pdfs ├── SparkSession - GNL.pdf ├── SparkSession - MLX42 - mod.pdf ├── SparkSession - MLX42 - setup.pdf ├── SparkSession - MLX42.pdf ├── SparkSession - ft_printf.pdf ├── SparkSession - ft_server - mod.pdf ├── SparkSession - ft_server.pdf ├── SparkSession - libasm - mod.pdf ├── SparkSession - libasm.pdf ├── SparkSession - minilibx - mod.pdf ├── SparkSession - minilibx setup.pdf ├── SparkSession - minilibx.pdf ├── SparkSession - minishell - mod.pdf ├── SparkSession - minishell.pdf ├── SparkSession - pipex - mod.pdf └── SparkSession - pipex.pdf ├── philosophy └── philosophy.md ├── render-all-pdfs.js └── spark-sessions ├── MLX42 ├── SparkSession - MLX42 - mod.md ├── SparkSession - MLX42 - setup.md └── SparkSession - MLX42.md ├── ft_printf └── SparkSession - ft_printf.md ├── ft_server ├── SparkSession - ft_server - mod.md └── SparkSession - ft_server.md ├── gnl └── SparkSession - GNL.md ├── libasm ├── SparkSession - libasm - mod.md └── SparkSession - libasm.md ├── minilibx ├── SparkSession - minilibx - mod.md ├── SparkSession - minilibx setup.md └── SparkSession - minilibx.md ├── minishell ├── SparkSession - minishell - mod.md └── SparkSession - minishell.md └── pipex ├── SparkSession - pipex - mod.md └── SparkSession - pipex.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "markdown-to-pdf"] 2 | path = markdown-to-pdf 3 | url = git@github.com:codam-coding-college/markdown-to-pdf.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spark Sessions 2 | 3 | Educational content for the Spark Sessions 4 | 5 | ## Philosophy 6 | 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. 7 | 8 | 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. 9 | 10 | 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. 11 | 12 | See `./philosophy/` for more information. 13 | 14 | ## Development 15 | Install `NodeJS >= 16.x` 16 | 17 | Run: 18 | ``` 19 | git clone --recursive git@github.com:codam-coding-college/spark-sessions.git 20 | cd spark-sessions 21 | chmod -R 777 hooks/ 22 | cp hooks/* .git/hooks/ 23 | ``` 24 | -------------------------------------------------------------------------------- /SparkSession_-__template_.md: -------------------------------------------------------------------------------- 1 | # Spark Session: [subject] 2 | *updated: [date]* 3 | 4 | Session [description/goal]: 5 | > [description/goal] 6 | 7 | ## Topics 8 | 1. [topic 1] 9 | 2. [topic 2] 10 | 3. [topic 3] 11 | 12 | ### [topic] 13 | [text] 14 | 1. [text] (_ mins) 15 | - [subpoint] 16 | - [sub-subpoint] 17 | 2. This is an example of a point. (42 mins) 18 | - This is an example of a subpoint. 19 | - This is an example of a sub-subpoint. 20 | 3. This is how you **bold** text. 21 | 4. This is how you *italicize* text. 22 | 5. This is how you insert a quote: 23 | > quote 24 | > if you want to do multiple separate lines of quotes, you need to put 2 spaces at the end of the previous line. If not, they'll stick together. 25 | 6. This is how you insert a link: [click here for Markdown formatting cheatsheet](https://www.markdownguide.org/cheat-sheet/). 26 | 7. This is how you insert an image:\ 27 | ![optional image title](https://www.42.fr/images/codam.jpg) 28 | 29 | ***Break (5 mins)*** 30 | 31 | ### [topic] 32 | [text] 33 | 1. [text] (_ mins) 34 | - [subpoint] 35 | - [sub-subpoint] 36 | -------------------------------------------------------------------------------- /hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # set -x 4 | set -e 5 | 6 | MOD_FILES=$(git diff --diff-filter=d --cached --name-only) 7 | DATE=$(date +%d\\/%m\\/%Y) 8 | while IFS= read -r FILE_NAME; do 9 | if [[ "${FILE_NAME}" =~ \.md ]] && [[ ! "${FILE_NAME}" = "README.md" ]] && [[ "${FILE_NAME}" == spark-sessions* ]] && [[ "$(basename "${FILE_NAME}")" == SparkSession* ]]; then 10 | if [[ "$OSTYPE" == "linux-gnu"* ]]; then 11 | sed -i "2s/.*/\*updated\: $DATE\*/" "$FILE_NAME" 12 | else 13 | sed -i '' "2s/.*/\*updated\: $DATE\*/" "$FILE_NAME" 14 | fi 15 | echo "Updated date to: ${DATE}" 16 | git add "$FILE_NAME" 17 | if [ ! -f "markdown-to-pdf/build/app.js" ]; then 18 | CURRENT_PWD=$(pwd) 19 | cd markdown-to-pdf/ 20 | npm install 21 | npm run build 22 | cd "$CURRENT_PWD" 23 | fi 24 | FILE=$(basename "$FILE_NAME") 25 | NAME="${FILE%.*}.pdf" 26 | node markdown-to-pdf/build/app.js "$FILE_NAME" "pdfs/$NAME" 27 | git add "pdfs/$NAME" 28 | echo 29 | fi 30 | done <<< "$MOD_FILES" 31 | 32 | exit 0 33 | -------------------------------------------------------------------------------- /pdfs/SparkSession - GNL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - GNL.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - MLX42 - mod.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - MLX42 - mod.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - MLX42 - setup.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - MLX42 - setup.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - MLX42.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - MLX42.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - ft_printf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - ft_printf.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - ft_server - mod.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - ft_server - mod.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - ft_server.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - ft_server.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - libasm - mod.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - libasm - mod.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - libasm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - libasm.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - minilibx - mod.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - minilibx - mod.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - minilibx setup.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - minilibx setup.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - minilibx.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - minilibx.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - minishell - mod.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - minishell - mod.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - minishell.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - minishell.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - pipex - mod.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - pipex - mod.pdf -------------------------------------------------------------------------------- /pdfs/SparkSession - pipex.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/spark-sessions/7bec964e758762951aac8520cf2941adffb4bafe/pdfs/SparkSession - pipex.pdf -------------------------------------------------------------------------------- /philosophy/philosophy.md: -------------------------------------------------------------------------------- 1 | # philosophy 2 | > Read the `README.md` for an introduction\ 3 | > This document details some of the considerations for the Spark Sessions 4 | 5 | ## Why 6 | 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. 7 | 8 | ## Spark session PDFs 9 | All the projects in the 42 curriculum are meant to teach a student software development by introducing one or more programming concepts per subject.\ 10 | All the Spark Sessions are meant to teach beginning students how to tackle a larger (curriculum) project and splitting its contents in manageable pieces.\ 11 | 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. 12 | 13 | ## Moderators 14 | Moderator are students that have done, and are very comfortable with the project. 15 | They help to guide the attendees with going through the PDF. 16 | A moderator moderates no more than 5 attendees, as to allow for more personal help and avoiding a "teacher before an class" situation. 17 | 18 | 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. 19 | 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. 20 | -------------------------------------------------------------------------------- /render-all-pdfs.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const { execSync } = require('child_process') 3 | 4 | // check if markdown-to-pdf was pulled correctly 5 | const MTPDFdir = `${__dirname}/markdown-to-pdf` 6 | const contentsOfMTPDFdir = fs.readdirSync(MTPDFdir) 7 | if (contentsOfMTPDFdir.length == 0) { 8 | console.log(execSync('git submodule update --init --recursive').toString()) 9 | console.log(execSync('npm install && npm run build', { cwd: MTPDFdir }).toString()) 10 | } 11 | 12 | const dirs = execSync(`find spark-sessions -name 'SparkSession*.md'`).toString().trimEnd() 13 | 14 | for (const dir of dirs.split('\n')) { 15 | const out = dir.split('/').slice(-1)[0].replace(/\.md$/i, '.pdf') 16 | console.log(execSync(`node markdown-to-pdf/build/app.js '${dir}' 'pdfs/${out}'`).toString()) 17 | } 18 | -------------------------------------------------------------------------------- /spark-sessions/MLX42/SparkSession - MLX42 - mod.md: -------------------------------------------------------------------------------- 1 | # Spark Session: MLX42 2 | *updated: 18/05/2022* 3 | 4 | Session description: 5 | > Learn the basics of working with MLX42 6 | 7 | This tutorial was written by de la Hamette Leon Jean Laurenti, [click here](https://github.com/codam-coding-college/MLX42/wiki) for the documentation regarding MLX42. 8 | 9 | **Hint: Most question you will have will most likely be answered by simply reading the documentation or the MLX42.h file!** 10 | > VERY IMPORTANT! PEOPLE REALLY DON'T LIKE READING! 11 | 12 | ## Topics 13 | 1. Window Management 14 | 2. Images & Pixel Putting 15 | 3. More Pixels 16 | 4. Hooks 17 | 4. Bonus! 18 | 19 | ### Window Management 20 | Our first step will be to open up a window! (30 mins) 21 | 22 | 1. In the set-up instructions, Some code was given to you for your `main.c` that included a call to `mlx_init`. 23 | But what does it do and what is its prototype? What does it return? (5 mins) 24 | - What are the variables we are passing to `mlx_init` ? 25 | - Study and understand the return value type and its layout! 26 | - Change the parameters to create a window with a width of **800** and a height of **400**, a title of your choice and make it so that we can **not** resize our window. 27 | > mlx_init creates a connection to the graphics API as well as opening a window using GLFW. 28 | > The pointer returns basically the applications context such as width and height and title. 29 | 30 | 2. What happens if you compile and run the program at this point? Your window should have only popped up for a moment. 31 | To make it stay longer, we need to use `mlx_loop`. (15 mins) 32 | - What does it do and what is its prototype? 33 | - Once you understand that, add `mlx_loop` to your code. 34 | - Do you now get a window that stays open? 35 | > mlx_loop, as the name suggests, makes the program loop until the application receives a request to exit. 36 | > To request an exit `mlx_close_window` can be called which causes the program loop to break. 37 | 38 | 3. How do we properly exit our application? When our application closes, we needs clean up its resources. 39 | To make this possible use `mlx_terminate` (2 mins) 40 | - Understand the purpose of having this function. 41 | - **Important**: `mlx_loop` should be called before this function in your code. Do you know why? 42 | > MLX42 works a bit different to minilibx in that it cleans up after its done with its job, it deletes all images and some internal stuff. Its vital to call this as this also terminates GLFW, the windowing library. 43 | > Not calling this function could cause memory leaks. 44 | 45 | ***Break (5 mins)*** 46 | 47 | ### Images & Pixel Putting 48 | Time to put something on that empty window. (60 mins) 49 | 50 | 1. Let there be colourful pixels! As of now your window is pretty much void of anything. Just like a painter we need a canvas to draw on, its time to learn about images in MLX. 51 | - What exactly is an image in its most basic form? 52 | - Use `mlx_new_image`. What is the prototype and the return value for the function? 53 | - Study and understand the return value! 54 | - Once you understand that, go ahead and initialise an image with a width of **64** and a height of **64**. 55 | > In short: An image is a pixel buffer that stores its dimension and data which when displayed show some form of output. 56 | > In long: An image is like a canvas that one can draw on an display multiple copies of. Images work with instances, that is, exact copies that can be placed throughout the window. The image itself is just the 'painting' while all its instances are the 'replicas'. 57 | 58 | 2. Now, using the `mlx_put_pixel` function, put a **white** pixel in the **middle** of your image. (10 mins) 59 | - How do you define colors? 60 | - Understand how this function works, that is, how does it modify the image itself. 61 | > MLX42 uses RGBA, One byte per channel and all channels must be specified e.g: 0xFF0000FF will display non-transparent red. RGBA is a standart format to display colors. 62 | > Its vital to undertsand the internals of `mlx_put_pixel`. It works by bit shifting out each byte of the integer to retrieve the individual color channels. Each channel is a `uint8`, therfor we shift by powers of 2. 63 | 64 | 3. Our image is all ready to be shown! To do so, use `mlx_image_to_window`. MLX42 works with instances of images, that is, an image is like a 'painting' while an instance is a 'physical copy' of that painting. 65 | - What are the prototypes and the return value for the function? 66 | - What can you do with the return value? 67 | > As the name suggest this puts a 'replica'/copy onto the window. MLX42 dynamically updates the images for you, you can simply modify the buffer at any given time and the changes are reflected instantly. 68 | > Since the instances array inside the image ptr gets reallocted holding a pointer to the instance is a bad idea. Instead putting an image to the window returns the index of where the instance is in the image's instances array. 69 | 70 | ***Break (5 mins)*** 71 | 72 | ### More Pixels 73 | Let's get fancier. Now we're gonna try drawing *lines*. (25 mins) 74 | 75 | 1. Draw a single horizontal white line running across the middle of the image. You'll need to call `mlx_put_pixel` in a loop. (15 mins) 76 | 77 | 2. Now draw a single vertical white line down the middle of the image. You should end up with what looks like a crosshair in your image. (10 mins) 78 | 79 |   80 | ### Hooks 81 | You might have noticed that we can already very easily close our window, however, just because for this exercise, we want to actually intercept the execution when we press the 'X' close button. (35 mins) 82 | 83 | 1. Hooks, 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. MLX provides 2 types of hooks, specialized and generic ones. 84 | - What exactly are generic and specialized hooks ? 85 | - Here's something that might help you understand: [Hooks](https://github.com/codam-coding-college/MLX42/wiki/Hooks) 86 | > Generic: Simple hook that gets executed every frame, can have X amount of them. And execute any type of logic. 87 | > Specialized: Specific hook for a specific task, there can only be one hooked function for each. Have to follow a specific prototype. 88 | 89 | 2. Write a function that: (10 mins) 90 | - Gets executed when we click onto the 'X' close button. 91 | - Prints the window title with a nicely formatted message. 92 | 93 | 3. Add a call to `mlx_close_hook` in your main that calls this exiting function when the 'X' button is pressed. (5 mins) 94 | - Does your window close and print a message when you press the 'X' close button on your window? 95 | 96 | ### Bonus! 97 | Let's get some movement on the screen: Make a rectangle move in 4 directions! 98 | 99 | 1. Make a `ft_draw_rect` function that: 100 | - Takes the MLX pointer. 101 | - Takes a width and height value. 102 | - Takes a color value for the rectangle. 103 | - Creates a new image of that width and height. 104 | - Sets every pixel in the image to the specified color. 105 | - Returns the image pointer. 106 | 107 | 2. Get a **30 x 30** blue rectangle onto your window. 108 | 109 | Now let's hook into keyboard events! 110 | 1. Add a call to `mlx_loop_hook` in your main that calls a function `ft_on_key` so we can handle key presses. 111 | - You can also use `mlx_key_hook`. 112 | 113 | 2. Write the `ft_on_key` function so that: 114 | - It closes the window/exits when the `ESC` key is pressed. 115 | - Moves the rectangle, up, down, left, and right when the corresponding key is pressed. 116 | - You can choose to use the arrow keys or `W`-`A`-`S`-`D` keys. 117 | 118 | 3. Regarding hooking onto the keyboard, MLX provides a header file that neatly displays all the different keycodes. Which are set out 119 | in the [**GLFW** library](https://www.glfw.org/docs/3.3/group__keys.html). 120 | 121 | 4. Add a call to `mlx_loop_hook` in your main that calls a function to move the new rectangle, of course don't forget to create it first! 122 | -------------------------------------------------------------------------------- /spark-sessions/MLX42/SparkSession - MLX42 - setup.md: -------------------------------------------------------------------------------- 1 | ## Setting Up MLX42 2 | *updated: 18/05/2022* 3 | 4 | During the Spark Session, we'll be writing a little program that can open a window, draw some pixels, and even move pixels! 5 | 6 | Before we can do that, we need to set up the MLX42 library and **link** the compiled library with our source code. 7 | 8 | 1. Download the MLX42 library into the **root directory**. 9 | - [Download here](https://github.com/codam-coding-college/MLX42) 10 | - **For the next steps we will assume you've called the folders `MLX42` or `MLX`**. 11 | 12 | 2. Using MLX42 requires that we link the necessary **internal API’s**. 13 | 14 | - For macOS: Install [Homebrew42](https://github.com/kube/42homebrew). Then run the following in the terminal: 15 | ```bash 16 | brew update 17 | brew install glfw 18 | ``` 19 | 20 | - For Linux: Simply run this to install the required dependencies: 21 | ```bash 22 | sudo apt update 23 | sudo apt install build-essential libx11-dev libglfw3-dev libglfw3 xorg-dev 24 | ``` 25 | 26 | - For Windows: Please refer to the installation guide on [github](https://github.com/codam-coding-college/MLX42). 27 | 28 | 3. Create a `main.c` in the root directory with the following content (remember to include **MLX42/MLX42.h**): 29 | ``` 30 | int main(void) 31 | { 32 | mlx_t *mlx; 33 | 34 | mlx = mlx_init(256, 256, "Hello World!", false); 35 | return (0); 36 | } 37 | ``` 38 | Do not worry yet what the parameters are, we will cover them later. 39 | This will help you check at the end if you're linking your MLX42 correctly to your source files. 40 | 41 | 4. Now let's create our own Makefile in the root directory, which will compile our `main.c` into a program. 42 | Add the required rules - `$(NAME)`, `clean`, `fclean`, `re`, `all`. 43 | Here's a [helpful guide on Makefiles](https://noahloomans.com/tutorials/makefile/) written by another student, Noah Loomans. 44 | 45 | 5. Compile the MLX42 library, so that you get a `libmlx42.a` file. 46 | **Tip: You could have your Makefile do all this too.** *Hint: `make -C dir`* 47 | 48 | 6. Now we finally come to combining everything together and link everything properly. 49 | 50 | - For macOS: 51 | ``` 52 | $(NAME): $(OBJ) 53 | $(CC) $(OBJ) libmlx42.a -lglfw -L "/Users/$USER/.brew/opt/glfw/lib/" -o $(NAME) 54 | ``` 55 | 56 | - For Linux: 57 | ``` 58 | $(NAME): $(OBJ) 59 | $(CC) $(OBJ) libmlx42.a -ldl -lglfw -o $(NAME) 60 | ``` 61 | 62 | - For Windows: 63 | ``` 64 | $(NAME): $(OBJ) 65 | $(CC) $(OBJ) libmlx42.a -lglfw3 -lopengl32 -lgdi32 -o $(NAME) 66 | ``` 67 | 68 | **Tip: Don't forget to define the header location.** *Hint: `-I `* 69 | 70 | 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! 71 | -------------------------------------------------------------------------------- /spark-sessions/MLX42/SparkSession - MLX42.md: -------------------------------------------------------------------------------- 1 | # Spark Session: MLX42 2 | *updated: 18/05/2022* 3 | 4 | Session description: 5 | > Learn the basics of working with MLX42 6 | 7 | This tutorial was written by de la Hamette Leon Jean Laurenti, [click here](https://github.com/codam-coding-college/MLX42/wiki) for the documentation regarding MLX42. 8 | 9 | **Hint: Most question you will have will most likely be answered by simply reading the documentation or the MLX42.h file!** 10 | 11 | ## Topics 12 | 1. Window Management 13 | 2. Images & Pixel Putting 14 | 3. More Pixels 15 | 4. Hooks 16 | 4. Bonus! 17 | 18 | ### Window Management 19 | Our first step will be to open up a window! (30 mins) 20 | 21 | 1. In the set-up instructions, Some code was given to you for your `main.c` that included a call to `mlx_init`. 22 | But what does it do and what is its prototype? What does it return? (5 mins) 23 | - What are the variables we are passing to `mlx_init` ? 24 | - Study and understand the return value type and its layout! 25 | - Change the parameters to create a window with a width of **800** and a height of **400**, a title of your choice and make it so that we can **not** resize our window. 26 | 27 | 2. What happens if you compile and run the program at this point? Your window should have only popped up for a moment. 28 | To make it stay longer, we need to use `mlx_loop`. (15 mins) 29 | - What does it do and what is its prototype? 30 | - Once you understand that, add `mlx_loop` to your code. 31 | - Do you now get a window that stays open? 32 | 33 | 3. How do we properly exit our application? When our application closes, we needs clean up its resources. 34 | To make this possible use `mlx_terminate` (2 mins) 35 | - Understand the purpose of having this function. 36 | - **Important**: `mlx_loop` should be called before this function in your code. Do you know why? 37 | 38 | ***Break (5 mins)*** 39 | 40 | ### Images & Pixel Putting 41 | Time to put something on that empty window. (60 mins) 42 | 43 | 1. Let there be colourful pixels! As of now your window is pretty much void of anything. Just like a painter we need a canvas to draw on, its time to learn about images in MLX. 44 | - What exactly is an image in its most basic form? 45 | - Use `mlx_new_image`. What is the prototype and the return value for the function? 46 | - Study and understand the return value! 47 | - Once you understand that, go ahead and initialise an image with a width of **64** and a height of **64**. 48 | 49 | 2. Now, using the `mlx_put_pixel` function, put a **white** pixel in the **middle** of your image. (10 mins) 50 | - How do you define colors? 51 | - Understand how this function works, that is, how does it modify the image itself. 52 | 53 | 3. Our image is all ready to be shown! To do so, use `mlx_image_to_window`. MLX42 works with instances of images, that is, an image is like a 'painting' while an instance is a 'physical copy' of that painting. 54 | - What are the prototypes and the return value for the function? 55 | - What can you do with the return value? 56 | 57 | ***Break (5 mins)*** 58 | 59 | ### More Pixels 60 | Let's get fancier. Now we're gonna try drawing *lines*. (25 mins) 61 | 62 | 1. Draw a single horizontal white line running across the middle of the image. You'll need to call `mlx_put_pixel` in a loop. (15 mins) 63 | 64 | 2. Now draw a single vertical white line down the middle of the image. You should end up with what looks like a crosshair in your image. (10 mins) 65 | 66 |   67 | ### Hooks 68 | You might have noticed that we can already very easily close our window, however, just because for this exercise, we want to actually intercept the execution when we press the 'X' close button. (35 mins) 69 | 70 | 1. Hooks, 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. MLX provides 2 types of hooks, specialized and generic ones. 71 | - What exactly are generic and specialized hooks ? 72 | - Here's something that might help you understand: [Hooks](https://github.com/codam-coding-college/MLX42/wiki/Hooks) 73 | 74 | 2. Write a function that: (10 mins) 75 | - Gets executed when we click onto the 'X' close button. 76 | - Prints the window title with a nicely formatted message. 77 | 78 | 3. Add a call to `mlx_close_hook` in your main that calls this exiting function when the 'X' button is pressed. (5 mins) 79 | - Does your window close and print a message when you press the 'X' close button on your window? 80 | 81 | ### Bonus! 82 | Let's get some movement on the screen: Make a rectangle move in 4 directions! 83 | 84 | 1. Make a `ft_draw_rect` function that: 85 | - Takes the MLX pointer. 86 | - Takes a width and height value. 87 | - Takes a color value for the rectangle. 88 | - Creates a new image of that width and height. 89 | - Sets every pixel in the image to the specified color. 90 | - Returns the image pointer. 91 | 92 | 2. Get a **30 x 30** blue rectangle onto your window. 93 | 94 | Now let's hook into keyboard events! 95 | 1. Add a call to `mlx_loop_hook` in your main that calls a function `ft_on_key` so we can handle key presses. 96 | - You can also use `mlx_key_hook`. 97 | 98 | 2. Write the `ft_on_key` function so that: 99 | - It closes the window/exits when the `ESC` key is pressed. 100 | - Moves the rectangle, up, down, left, and right when the corresponding key is pressed. 101 | - You can choose to use the arrow keys or `W`-`A`-`S`-`D` keys. 102 | 103 | 3. Regarding hooking onto the keyboard, MLX provides a header file that neatly displays all the different keycodes. Which are set out 104 | in the [**GLFW** library](https://www.glfw.org/docs/3.3/group__keys.html). 105 | 106 | 4. Add a call to `mlx_loop_hook` in your main that calls a function to move the new rectangle, of course don't forget to create it first! 107 | -------------------------------------------------------------------------------- /spark-sessions/ft_printf/SparkSession - ft_printf.md: -------------------------------------------------------------------------------- 1 | # Spark Session: ft_printf 2 | *updated: 27/01/2021* 3 | 4 | Project description: 5 | > Recode printf 6 | 7 | ## Topics 8 | 9 | 1. Variadic Arguments 10 | 2. Function Pointers 11 | 12 | ### Variadic Arguments 13 | 14 | 1. Variadic functions add flexibility to your code by allowing an unknown number of arguments. (30 mins) 15 | - What would its prototype look like? (5 mins) 16 | - Identify the 4 macros that allow you to access these arguments. (25 mins) 17 | - What are the argument types? For example, what exactly is the 2nd argument to va_start? 18 | - What are default argument promotions? 19 | 2. Let's practice accessing and carrying out operations on a variable argument list! (30 mins) 20 | - Write a variadic function that: 21 | - has a prototype of `function(const int n, ...)` 22 | **n** being the number of arguments in the list, 23 | - returns the **sum** of the integers in that list. 24 | - Write the accompanying main to test your function. 25 | Example test: does `yourfunction(3, 40, 5, -3)` return `42`? 26 | 27 | *Break (5 mins)* 28 | 29 | ### Function Pointers 30 | 31 | 1. Just as we can have pointers to data (char *, int *), we can have pointers to functions. (45 mins) 32 | - How do we declare a pointer to a function? Pay attention to bracket placement! (10 mins) 33 | - Let's break down the syntax. What does each part of the declaration mean? 34 | - Is there a difference between `void (*fn)` and `void *fn`? 35 | - What's happening when we assign the function pointer to a function? 36 | What information does the function pointer hold? (5 mins) 37 | - Like normal pointers, we can also have an array of function pointers. 38 | What is their syntax? (10 mins) 39 | - When can function pointers come in handy? (10 mins) 40 | - What is a typedef and how can it be used with function pointers? (10 mins) 41 | 2. Let's practice using a function pointer! (30 mins) 42 | - Write a function that: (10 mins) 43 | - takes an integer **n** as argument, 44 | - prints "Hello" **n** times, 45 | - returns nothing. 46 | - Now write an accompanying main that: (20 mins) 47 | - declares a pointer to a function that takes an int and returns nothing, 48 | - initialises that pointer to the Hello function you just wrote, 49 | - prints "Hello" 3 times **using the function pointer**. 50 | 51 | *Break (5 mins)* 52 | 53 | 3. Now let's try doing something cooler with an array of function pointers. (20 mins) 54 | - Here's some code to get you started: 55 | ``` 56 | enum e_op 57 | { 58 | PLUS = 0, MINUS 59 | }; 60 | 61 | void operation_add(int a, int b) 62 | { 63 | printf("%d + %d = %d\n", a, b, a + b); 64 | } 65 | 66 | void operation_minus(int a, int b) 67 | { 68 | printf("%d - %d = %d\n", a, b, a - b); 69 | } 70 | ``` 71 | - Write a main that: 72 | - declares an array of 2 function pointers, taking 2 ints and returning nothing, 73 | - assigns the first array element to `operation_add` and the second element to `operation_minus`, 74 | - calls each function at least once through the array. 75 | *Hint: enums can make indexing easier.* 76 | 77 | ### Bonus 78 | 1. Here's some code to get you started again: 79 | ``` 80 | typedef void (*printfunct)(va_list list); 81 | 82 | void print_char(va_list list) 83 | { 84 | printf("%c\n", va_arg(list, int)); 85 | } 86 | 87 | void print_string(va_list list) 88 | { 89 | printf("%s\n", va_arg(list, char *)); 90 | } 91 | 92 | void print_digit(va_list list) 93 | { 94 | printf("%d\n", va_arg(list, int)); 95 | } 96 | ``` 97 | Write a **variadic function** that: 98 | - has a prototype of `function(char *str, char *filler, ...)`, 99 | - has an **array of function pointers** assigned to the 3 `print_` functions above, 100 | - for every valid option in `str`, calls the corresponding function from the function pointer array, 101 | - valid options: `'c'` should trigger `print_char`, `'d'` triggers `print_digit`, and `'s'` triggers `print_string` 102 | - invalid options: print the `filler` string and then continue onto the next character in `str` 103 | 2. Write the accompanying main. Test it with the following input: `yourprintfunc("csdcx", "REJECTED", 'k', "hello", 42, 'f')`. 104 | 3. *Bonus bonus*: how can you avoid using a bunch of if-else statements in this exercise? 105 | -------------------------------------------------------------------------------- /spark-sessions/ft_server/SparkSession - ft_server - mod.md: -------------------------------------------------------------------------------- 1 | # Spark Session: ft_server 2 | *updated: 18/03/2021* 3 | 4 | Project description: 5 | > Discover Docker and set up your first web server. 6 | 7 | ## Topics 8 | 1. Virtual Machines 9 | 2. Containers 10 | 3. Docker Tutorial 11 | 12 | ## Virtual Machines 13 | 1. Traditionally, organizations would run applications on physical servers. This was both expensive to maintain and did not scale well.\ 14 | Virtualization was a solution to this. What is virtualization and what issues does it solve? (15 mins) 15 | > Virtualization is technology that allows you to divide a system's resources more 16 | > efficiently by running multiple Virtual Machines (VMs) on a single physical server's CPU. 17 | > 18 | > Advantages: security (isolation between VMs); flexibility (applications aren't limited by 19 | > host hardware); cost and time efficiency (instantly create VMs, less machines needed); 20 | > more portable (self-contained machines, so can be moved across different servers as needed 21 | > without concern for hardware) 22 | > 23 | > [What is virtualization](https://www.redhat.com/en/topics/virtualization/what-is-virtualization) 24 | 2. What is a virtual machine (VM)? What is a hypervisor? (5 mins) 25 | > VMs run applications inside a guest Operating System, which runs on virtual hardware 26 | > powered by the server’s host OS. 27 | > 28 | > Hypervisor is software that takes the host computer's physical resources - such as memory 29 | > and processing - and divides them up to support multiple guest VMs 30 | 31 | ## Containers 32 | 1. VMs are, however, still resource-heavy as each VM is its own full machine with its own resource needs. 33 | Containerization provides a more lightweight alternative to that. What are containers? How are they similar yet different from VMs? (20 mins) 34 | > A container packages up the code and all its dependencies so that it can be deployed 35 | > quickly and reliably. 36 | > Any changes/processes in these containers are isolated from other containers and the host 37 | > machine. 38 | > 39 | > Similarity: flexibility - VMs allow multiple operating systems regardless of hardware ,containers allow applications regardless of OS. 40 | > 41 | > Difference: containers are more lightweight because they use only what they need of the host\ 42 | > system's resources, unlike VMs that reserve resources; containers are less isolated 43 | > 44 | > [Traditional vs VMs vs Container deployment](https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/) 45 | > 46 | > [What is a container and comparison with VMs](https://www.docker.com/resources/what-container) 47 | 2. Where does Docker fit into this picture? Why would you use container technology like Docker? (10 mins) 48 | > Docker is one implementation of container-technology out of [many](https://opencontainers.org/) 49 | > 50 | > Why: standardization (bundling applications with all their dependencies makes it extremely 51 | > portable), security (isolated), low overhead (fast and lightweight) 52 | 53 | ![VM vs Docker](https://i.imgur.com/dlMiZUA.jpg) 54 | 55 | *Break (5 mins)* 56 | 57 | ## Docker Tutorial 58 | ### Set Up 59 | 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) 60 | 1. If you don't have it already, register for a Docker ID: https://docs.docker.com/docker-id/ 61 | 2. Go to https://labs.play-with-docker.com/ and log in. 62 | 3. Type the following command in the black PWD terminal:\ 63 | `docker run -dp 80:80 docker/getting-started:pwd` *(Copy-paste hint: Shift+Insert)* 64 | 4. Wait for it to start the container and click the **port 80** badge above the terminal. 65 | 66 | ### Introduction to Docker 67 | Tutorial page: Getting Started 68 | 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) 69 | 2. Why port 80? What is HTTP? (15 mins) 70 | > Port 80 is the port number assigned to HTTP, an internet communication protocol. 71 | > 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. 72 | > 73 | > [Simple explanation](https://medium.com/@odyuzaki/the-importance-of-port-80-243e5953cbd9) 74 | > 75 | > 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 76 | > that is sent by a client to a server is what is known as an HTTP request. 77 | > 78 | > [Explanation with fun analogy](https://www.codecademy.com/articles/http-requests) 79 | 3. We've talked about what containers are. But what is a container image? (10 mins) 80 | > 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. 81 | 82 | *Break (5 mins)* 83 | 84 | ### Building an Image and Running a Container 85 | Tutorial page: Our Application 86 | 87 | Let's start learning how to use Docker by getting a little to-do list page up and running! (20 mins) 88 | 1. Follow the instructions under **"Getting our App into PWD"** to get the application source code into your Play with Docker environment. 89 | - When you're done, do you see the files in `app/` as shown in the tutorial? 90 | 2. Now create a Dockerfile in the PWD environment with the code from the tutorial (you can use vim) 91 | - What is a Dockerfile? 92 | 3. Build the container image using `docker build -t docker-101 .` 93 | - What happened when you ran that command? 94 | 4. Start up the container using `docker run -dp 3000:3000 docker-101` 95 | - Recall what we discussed earlier. What does each component of this command mean? 96 | 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! 97 | 98 | ### Stopping and Removing Containers 99 | Tutorial page: Updating our App 100 | 101 | Let's see how we can implement a change in our image. (15 mins) 102 | 1. Change the code on line **56** in `app.js` as specified in the tutorial. 103 | 2. Let's try building this newest version of the image with\ 104 | `docker build -t docker-101 .` 105 | 3. What happens if you execute `docker run -dp 3000:3000 docker-101` like last time? 106 | > Will error because only one process can listen to a specific port at a time and the old container is still running 107 | 4. So we first need to stop our old container that still has the outdated code. 108 | - How can we see the currently running containers? 109 | - How can we stop a specific container?\ 110 | *(Hint: do you have to type the entire container ID?)* 111 | 5. Now that we've stopped the old container, we have to remove it. Execute the `docker rm` command shown in the tutorial. 112 | - There's also a way to stop and remove a container using a single command. What is it? 113 | 6. Try running your updated application again. Do you see your updated text? 114 | - But notice that all of the items you added to your previous list are gone! Let's address that next. 115 | 116 | ### Persisting the Data 117 | Tutorial page: Persisting our DB 118 | 119 | Now let's learn about why our application data is wiped each time we launch it and how to prevent that. (30 mins) 120 | 1. Each container has its own filesystem and gets their own space to create and manipulate these files.\ 121 | Follow the instructions on the tutorial page to see how a file created in one container isn't available in another. 122 | - 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. 123 | - Run the `docker exec` command shown, using the container ID *(hint: `docker ps`)*. Do you see a random number? 124 | - **Extra step:** now try running `docker exec ls`. Do you see the data.txt file? 125 | - 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. 126 | - You can remove the first container now using `docker rm -f `. 127 | 2. What are container volumes? 128 | 3. Let's try creating and using a **named volume**. Do you know what that is? Run the `docker volume create` command shown. 129 | 4. Start your `docker-101` container again, but this time specifying a volume mount with the `-v` flag. 130 | - What does the `docker run` command shown do exactly? 131 | 5. Once your to-do list is up, try adding a few items to the list. 132 | 6. Now remove the container using `docker rm -f`.\ 133 | 7. Start a new container using the same `docker run` command you ran in step 4.\ 134 | If you open the app again, do you see the list items you added earlier? 135 | 8. Now let's see where Docker stores that data when you use a named volume.\ 136 | Run the `docker volume inspect` command shown in the tutorial. 137 | - What is a mountpoint? 138 | 139 | Congratulations, now you know how to use volumes in Docker to preserve your application data! 140 | **Named volumes** are one of the main types of volumes in Docker, **bind mounts** being the other.\ 141 | 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. 142 | 143 | ### Docker Environment Variables 144 | 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! 145 | 1. First off, how would you declare an environment variable in your Dockerfile? (5 mins) 146 | > `ENV variable_name=variable_value` 147 | 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)\ 148 | Go into your Dockerfile and add the following things: 149 | - declare an environment variable called `NEW` and give it a default value ("What would you like to do today?", for example) 150 | - replace the last `CMD` instruction with this: *(make sure the instruction is on one line)* 151 | ``` 152 | CMD sed -i "99 s/New Item/${NEW}/g" /app/src/static/js/app.js && node /app/src/index.js 153 | ``` 154 | 3. Build your image with `docker build -t docker-101 .` and run it with\ 155 | `docker run -dp 3000:3000 docker-101`. Is the New Item text now replaced with the default value you gave `$NEW`? (5 mins) 156 | 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)\ 157 | Did you manage to change your placeholder text? 158 | ![placeholder](https://i.imgur.com/tZKfgKf.jpg) 159 | > `docker run -e NEW="Please give me something to do" -dp 3000:3000 docker-101` 160 | 5. Let's go back to that `CMD sed` instruction I helpfully provided earlier. (15 mins) 161 | - What does each part of the `sed` command mean? 162 | > sed {don't print to stdout} "{line no.} s/{change this}/{to this}/g" 163 | 164 | - Why did I use `CMD` and not `RUN`? 165 | > RUN is executed at build-time, whereas CMD is at run-time. Using `RUN sed` would mean 166 | > `$NEW` always equates to the default value. 167 | 168 | - Can you have multiple `CMD` instructions in a single Dockerfile? 169 | 170 | > There can only be one CMD command in the Dockerfile. So either commands need to be 171 | > chained with `&&` or executed through a bash script. 172 | 173 | 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. 174 | 175 | ``` 176 | #example Dockerfile 177 | FROM node:10-alpine 178 | ENV NEW="What would you like to do today?" 179 | WORKDIR /app 180 | COPY . . 181 | RUN yarn install --production 182 | CMD sed -i "99 s/New Item/${NEW}/g" /app/src/static/js/app.js && node /app/src/index.js 183 | ``` -------------------------------------------------------------------------------- /spark-sessions/ft_server/SparkSession - ft_server.md: -------------------------------------------------------------------------------- 1 | # Spark Session: ft_server 2 | *updated: 18/03/2021* 3 | 4 | Project description: 5 | > Discover Docker and set up your first web server. 6 | 7 | ## Topics 8 | 1. Virtual Machines 9 | 2. Containers 10 | 3. Docker Tutorial 11 | 12 | ## Virtual Machines 13 | 1. Traditionally, organizations would run applications on physical servers. This was both expensive to maintain and did not scale well.\ 14 | Virtualization was a solution to this. What is virtualization and what issues does it solve? (15 mins) 15 | 2. What is a virtual machine (VM)? What is a hypervisor? (5 mins) 16 | 17 | ## Containers 18 | 1. VMs are, however, still resource-heavy as each VM is its own full machine with its own resource needs.\ 19 | Containerization provides a more lightweight alternative to that. What are containers? How are they similar yet different from VMs? (20 mins) 20 | 2. Where does Docker fit into this picture? Why would you use container technology like Docker? (10 mins) 21 | 22 | ![VM vs Docker](https://i.imgur.com/dlMiZUA.jpg) 23 | 24 | *Break (5 mins)* 25 | 26 | ## Docker Tutorial 27 | ### Set Up 28 | 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) 29 | 1. If you don't have it already, register for a Docker ID: https://docs.docker.com/docker-id/ 30 | 2. Go to https://labs.play-with-docker.com/ and log in. 31 | 3. Type the following command in the black PWD terminal:\ 32 | `docker run -dp 80:80 docker/getting-started:pwd` *(Copy-paste hint: Shift+Insert)* 33 | 4. Wait for it to start the container and click the **port 80** badge above the terminal. 34 | 35 | ### Introduction to Docker 36 | Tutorial page: Getting Started 37 | 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) 38 | 2. Why port 80? What is HTTP? (15 mins) 39 | 3. We've talked about what containers are. But what is a container image? (10 mins) 40 | 41 | *Break (5 mins)* 42 | 43 | ### Building an Image and Running a Container 44 | Tutorial page: Our Application 45 | 46 | Let's start learning how to use Docker by getting a little to-do list page up and running! (20 mins) 47 | 1. Follow the instructions under **"Getting our App into PWD"** to get the application source code into your Play with Docker environment.\ 48 | When you're done, do you see the files in `app/` as shown in the tutorial? 49 | 2. Now create a Dockerfile in the PWD environment with the code from the tutorial (you can use vim) 50 | - What is a Dockerfile? 51 | 3. Build the container image using `docker build -t docker-101 .` 52 | - What happened when you ran that command? 53 | 4. Start up the container using `docker run -dp 3000:3000 docker-101` 54 | - Recall what we discussed earlier. What does each component of this command mean? 55 | 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! 56 | 57 | ### Making Changes in our Container 58 | Tutorial page: Updating our App 59 | 60 | Let's see how we can implement a change in our image. (15 mins) 61 | 1. Change the code on line **56** in `app.js` as specified in the tutorial. 62 | 2. Let's try building this newest version of the image with\ 63 | `docker build -t docker-101 .` 64 | 3. What happens if you execute `docker run -dp 3000:3000 docker-101` like last time? 65 | 4. So we first need to stop our old container that still has the outdated code. 66 | - How can we see the currently running containers? 67 | - How can we stop a specific container?\ 68 | *(Hint: do you have to type the entire container ID?)* 69 | 5. Now that we've stopped the old container, we have to remove it. Execute the `docker rm` command shown in the tutorial. 70 | - There's also a way to stop and remove a container using a single command. What is it? 71 | 6. Try running your updated application again. Do you see your updated text? 72 | - But notice that all of the items you added to your previous list are gone! Let's address that next. 73 | 74 | ### Persisting the Data 75 | Tutorial page: Persisting our DB 76 | 77 | Now let's learn about why our application data is wiped each time we launch it and how to prevent that. (30 mins) 78 | 1. Each container has its own filesystem and gets their own space to create and manipulate these files.\ 79 | Follow the instructions on the tutorial page to see how a file created in one container isn't available in another. 80 | - 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. 81 | - Run the `docker exec` command shown, using the container ID *(hint: `docker ps`)*. Do you see a random number? 82 | - **Extra step:** now try running `docker exec ls`. Do you see the data.txt file? 83 | - 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. 84 | - You can remove the first container now using `docker rm -f `. 85 | 2. What are container volumes? 86 | 3. Let's try creating and using a **named volume**. Do you know what that is? Run the `docker volume create` command shown. 87 | 4. Start your `docker-101` container again, but this time specifying a volume mount with the `-v` flag. 88 | - What does the `docker run` command shown do exactly? 89 | 5. Once your to-do list is up, try adding a few items to the list. 90 | 6. Now remove the container using `docker rm -f`. 91 | 7. Start a new container using the same `docker run` command you ran in step 4.\ 92 | If you open the app again, do you see the list items you added earlier? 93 | 8. Now let's see where Docker stores that data when you use a named volume.\ 94 | Run the `docker volume inspect` command shown in the tutorial. 95 | - What is a mountpoint? 96 | 97 | Congratulations, now you know how to use volumes in Docker to preserve your application data! 98 | **Named volumes** are one of the main types of volumes in Docker, **bind mounts** being the other.\ 99 | 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. 100 | 101 | ### Docker Environment Variables 102 | 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! 103 | 1. First off, how would you declare an environment variable in your Dockerfile? (5 mins) 104 | 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)\ 105 | Go into your Dockerfile and add the following things: 106 | - declare an environment variable called `NEW` and give it a default value ("What would you like to do today?", for example) 107 | - replace the last `CMD` instruction with this: *(make sure the instruction is on one line)* 108 | ``` 109 | CMD sed -i "99 s/New Item/${NEW}/g" /app/src/static/js/app.js && node /app/src/index.js 110 | ``` 111 | 3. Build your image with `docker build -t docker-101 .` and run it with\ 112 | `docker run -dp 3000:3000 docker-101`. Is the New Item text now replaced with the default value you gave `$NEW`? (5 mins) 113 | 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)\ 114 | Did you manage to change your placeholder text? 115 | ![placeholder](https://i.imgur.com/tZKfgKf.jpg) 116 | 5. Let's go back to that `CMD sed` instruction I helpfully provided earlier. (15 mins) 117 | - What does each part of the `sed` command mean? 118 | - Why did I use `CMD` and not `RUN`? 119 | - Can you have multiple `CMD` instructions in a single Dockerfile? 120 | 121 | Now you know how to use environment variables to allow for more dynamic environment configuration.\ 122 | This will come in handy for certain parts of the project that require toggling settings or passing credentials. 123 | -------------------------------------------------------------------------------- /spark-sessions/gnl/SparkSession - GNL.md: -------------------------------------------------------------------------------- 1 | # Spark Session: get_next_line 2 | *updated: 30/03/2022* 3 | 4 | Project description: 5 | > Write a function which returns a line read from a file descriptor, without the newline. 6 | 7 | ## Topics 8 | 9 | 1. File Handling 10 | 2. Static Variables 11 | 12 | ### File Handling 13 | 14 | 1. File handling involves 4 major operations that you must understand perfectly 15 | - Identify these 4 operations, their corresponding system calls, and man pages. (10 mins) 16 | - Discuss and make sure everyone understands their prototypes and return values. 17 | Pay attention to the following: (15 mins) 18 | - The types of the arguments and the return values; 19 | - The differences between opening a file in **append**, **truncate**, or **default** mode; 20 | - File descriptors and the 3 special values reserved by the system. 21 | 22 | *Break (5 mins)* 23 | 24 | 2. Now that we're comfortable with these 4 operations in theory, let's give them a try! 25 | - Create a text file anywhere on your filesystem that contains a few lines of text using your favorite editor or the command `echo`. 26 | - Let's practice reading from a file. Write a program that: (15 mins) 27 | - opens that file you made in **read-only** mode, 28 | - reads the complete contents of the file using a buffer smaller than the file content, 29 | - writes the contents of that buffer onto standard output, 30 | - closes the file. 31 | - Now let's practice writing to a file. Write a program that: (10 mins) 32 | - opens that file you made in **write-only** and **append** mode, 33 | - writes some additional characters to the file, 34 | - closes the file. 35 | - Then display the content of your text file in the terminal using the `cat` command. 36 | 37 | *Break (5 mins)* 38 | 39 | ### Static Variables 40 | 41 | 1. What is a static variable? 42 | - Use the internet to find the definition of a static variable and its unique characteristics. 43 | - Discuss the following points together and make sure everyone understands: when might you use a static variable? Where is it allocated in memory? What are the disadvantages when it comes to memory and reusability? 44 | 45 | 2. Let's practice! 46 | - Write a function that: (10 mins) 47 | - declares a **static int**, 48 | - initializes that int to 0, 49 | - increments it by 1, 50 | - then returns the int value. 51 | - Write the accompanying main that calls that function in a loop 9 times and outputs the returned value using **write()** to the standard output on each iteration. What happens to the return value? (10 mins) 52 | - As a closing step, discuss whether it's possible or not to restore a static variable to its initial value. 53 | -------------------------------------------------------------------------------- /spark-sessions/libasm/SparkSession - libasm - mod.md: -------------------------------------------------------------------------------- 1 | # Spark Session: libasm 2 | *updated: 01/04/2021* 3 | 4 | Project description: 5 | > Get familiar with assembly language 6 | 7 | This tutorial was written with help from Thijs Bruineman (tbruinem) and [this great tutorial series](https://youtube.com/playlist?list=PLetF-YjXm-sCH6FrTz4AQhfH6INDQvQSn). 8 | 9 | ## Topics 10 | 1. nasm 11 | 2. Registers 12 | 3. Instructions 13 | 4. Syscall 14 | 5. Sections 15 | 6. Stack Alignment 16 | 17 | ## Setting Up nasm 18 | For libasm, we'll be using the [Netwide Assembler](https://www.nasm.us/) (`nasm`) to compile our assembly code. 19 | If you haven't already, install `nasm` on your system using the following command: 20 | - For Linux: `sudo apt update && sudo apt install nasm` 21 | - For macOS: [install Homebrew](https://brew.sh/) if you don't have it yet, then run `brew install nasm` 22 | 23 | ## Registers 24 | 1. Registers are internal memory storage locations in the processor that temporarily hold memory.\ 25 | In x86_64 architecture, we have access to 64-bit registers. What does that "64-bit" mean? (5 mins) 26 | > that the registers can hold 64 bits of data 27 | 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) 28 | > rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8-r15 29 | 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) 30 | ![register breakdown](https://i.imgur.com/nToJ8HY.jpg) 31 | - **Question**: what is AL in this case? 32 | > the least significant byte of the accumulator register - bits 0 to 7 of RAX 33 | 34 | - Here's a table showing the breakdown of every general purpose register: 35 | ![gpr](https://i.imgur.com/4UQtei7.jpg?1) 36 | [source, with many other helpful tips](https://aaronbloomfield.github.io/pdr/book/x86-64bit-asm-chapter.pdf) 37 | - **Question**: how would you access the lowest 8 bits of R8? 38 | > r8b 39 | 40 | - **Question**: are these "sub"registers independent? For example, will modifying `al` affect `ax`? 41 | > No, they're not independent registers. Modifying `al` will affect `ax`, but modifying `ah` won't affect `al`. 42 | 43 | 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) 44 | - RAX is commonly used to store ___ when functions are called within assembly code. 45 | > function return values. Also used to pass system call numbers such as for calls to `write` 46 | 47 | - RSP is called the Stack Pointer and RBP is the Base Pointer. What do they do? 48 | > RSP points to the topmost element in the stack, RBP points to base of stack 49 | 50 | - Lastly, 6 specific registers are used to pass parameters to functions. What are they and which arguments do they correspond to? 51 | > 1st: RDI, 2nd: RSI, 3rd: RDX, 4th: RCX, 5th: R8, 6th: R9 52 | 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) 53 | - Which are the callee-saved registers? 54 | > RBX, RSP, RBP, and R12–R15 55 | 56 | - 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). 57 | - 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? 58 | > must save and restore original value by pushing at start and popping at end (which we'll get into in a bit) 59 | 60 | - 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) 61 | 62 | ***Break (5 mins)*** 63 | 64 | ## Instructions 65 | 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. 66 | 1. Figure out what the following instructions do: (30 mins) 67 | 68 | | instruction | example | 69 | |---|---| 70 | | mov | mov rax, rbx | 71 | | push | push rax | 72 | | pop | pop rax | 73 | | add | add rax, 42 | 74 | | sub | sub rax, 42 | 75 | | inc | inc rax | 76 | | dec | dec rax | 77 | | cmp | cmp rax, 42 | 78 | | jmp | jmp _main | 79 | | je/jne/jl/jg | je _done | 80 | | call | call _printf | 81 | | ret | ret | 82 | 83 | - Make sure you know which operand is affecting which! 84 | > e.g. `add rax, 42` -> rax = rax + 42 85 | 2. What effect would the following `jmp` instruction have? (5 mins) 86 | ``` 87 | _main: 88 | jmp _main 89 | ``` 90 | > program would be stuck in an infinite loop 91 | 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) 92 | - What's the difference then between `mov rax, rbx` and `mov rax, [rbx]`? 93 | > first instruction loads value in rbx register into rax, second loads value rbx is pointing to into rax 94 | 95 | ## Syscall 96 | Now let's look at system calls or **syscall**s, which allow a program to request a service from the kernel. 97 | 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) 98 | - 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) 99 | - 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. 100 | - What are the syscall IDs for `write()` and `exit()` for your system? 101 | > `write` is 1 (Linux) and 0x2000004 (mac), `exit` is 60 (Linux) and 0x2000001 (mac) 102 | 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)\ 103 | Here's a convenient table for you: 104 | | syscall | rax | rdi | rsi | rdx | rcx (r10 for Linux) | r8 | r9 | 105 | |---|---|---|---|---|---|---|---| 106 | | write | | | | | | | | 107 | > RAX: 1 or 0x2000004, RDI: fd, RSI: address of string, RDX: byte count 108 | 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) 109 | - First, create a `.s` file. Here's what the first part of your code should look like: 110 | ``` 111 | section .text ; this is the section for code 112 | global _main ; this tells the kernel where the program begins 113 | _main: 114 | ``` 115 | - Note: for Linux users, you'll need to remove the `_` before `_main`. 116 | - To make a system call, you need to: 117 | 1. pass the syscall ID into RAX 118 | 2. pass any arguments for the syscall 119 | 3. use the `syscall` instruction 120 | 121 | Go ahead and turn those steps into assembly code. 122 | > `mov rax, 60` ; or 0x2000001 for mac 123 | > `mov rdi, 0` 124 | > `syscall` 125 | 126 | - 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: 127 | - **Note**: remember to change `myfile.s` & `myfile.o` to your actual file name 128 | - For Linux: `nasm -felf64 myfile.s && gcc myfile.o` 129 | - For mac: `nasm -fmacho64 myfile.s && gcc myfile.o` 130 | - Run `./a.out` and then `echo $?`. Does it output `0`? 131 | 132 | ***Break (5 mins)*** 133 | 134 | ## Sections 135 | 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) 136 | > data: where data is defined (initialized data) before compilation, bss: where data is allocated for future use (uninitialized data), text: executable instructions 137 | 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) 138 | - Let's start by declaring a new section (section order doesn't matter in an assembly file): 139 | ``` 140 | section .data 141 | text db "Hello, world!", 10 142 | ``` 143 | - What is "text" here? And what does "db" mean? What is the "10" at the end? 144 | > `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. 145 | > `db` stands for define bytes, it means we are going to define some raw bytes (each character in our string is a byte). 146 | > `10` is ascii for the newline character. 147 | 148 | - Next declare your `.text` section as you did in the previous exercise. 149 | - 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? 150 | > ; code example for Linux 151 | > main: 152 | > mov rax, 1 ; write for Linux 153 | > mov rdi, 1 ; stdout 154 | > mov rsi, text 155 | > mov rdx, 14 ; len of text 156 | > syscall 157 | 158 | - Finish with a call to `exit()` again like you did earlier. 159 | - Compile with the same commands you used earlier. Do you get "Hello, world!"? 160 | - Note: your compiler may throw up some warnings, but if your program was compiled successfully, just ignore it! 161 | 162 | ## Stack Alignment 163 | 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. 164 | - When your program starts at `main` (or `start`), `rsp` (the stack pointer) is 16-byte aligned. 165 | - 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? 166 | - But this means the stack is now misaligned by **8 bytes**. So how do we re-align our stack? (15 mins) 167 | > 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. 168 | > good clear explanation about alignment: [link](https://link.springer.com/chapter/10.1007/978-1-4842-5076-1_13) 169 | 170 | You can find more detailed explanations of the stack and alignment online.\ 171 | 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) 172 | 173 | ## Bonus 174 | 1. Write a `compare` function in assembly that compares 2 integers and returns: 175 | - `1` if `a > b`; 176 | - `-1` if `a < b`; 177 | - `0` if `a == b`. 178 | 179 | The function prototype is `int compare(int64_t a, int64_t b)`. 180 | 181 | 2. You'll also make a test main C file. It should: 182 | - include for the `int64_t` types; 183 | - have your `compare` function prototype; 184 | - call your `compare` function with a variety of inputs (positive, negative, 0s) and print the return. 185 | 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. 186 | 4. Compile and run it to see if your function is working correctly. 187 | - For Linux: `nasm -felf64 compare.s && gcc compare.o main.c` 188 | - For macOS: `nasm -fmacho64 compare.s && gcc compare.o main.c` 189 | -------------------------------------------------------------------------------- /spark-sessions/libasm/SparkSession - libasm.md: -------------------------------------------------------------------------------- 1 | # Spark Session: libasm 2 | *updated: 01/04/2021* 3 | 4 | Project description: 5 | > Get familiar with assembly language 6 | 7 | This tutorial was written with help from Thijs Bruineman (tbruinem) and [this great tutorial series](https://youtube.com/playlist?list=PLetF-YjXm-sCH6FrTz4AQhfH6INDQvQSn). 8 | 9 | ## Topics 10 | 1. nasm 11 | 2. Registers 12 | 3. Instructions 13 | 4. Syscall 14 | 5. Sections 15 | 6. Stack Alignment 16 | 17 | ## Setting Up nasm 18 | For libasm, we'll be using the [Netwide Assembler](https://www.nasm.us/) (`nasm`) to compile our assembly code. 19 | If you haven't already, install `nasm` on your system using the following command: 20 | - For Linux: `sudo apt update && sudo apt install nasm` 21 | - For macOS: [install Homebrew](https://brew.sh/) if you don't have it yet, then run `brew install nasm` 22 | 23 | ## Registers 24 | 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) 25 | 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) 26 | 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) 27 | ![register breakdown](https://i.imgur.com/nToJ8HY.jpg) 28 | - **Question**: what is AL in this case? 29 | - Here's a table showing the breakdown of every general purpose register: 30 | ![gpr](https://i.imgur.com/4UQtei7.jpg?1)\ 31 | [source, with many other helpful tips](https://aaronbloomfield.github.io/pdr/book/x86-64bit-asm-chapter.pdf) 32 | - **Question**: how would you access the lowest 8 bits of R8? 33 | - **Question**: are these "sub"registers independent? For example, will modifying `al` affect `ax`? 34 | 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) 35 | - RAX is commonly used to store ___ when functions are called within assembly code. 36 | - RSP is called the Stack Pointer and RBP is the Base Pointer. What do they do? 37 | - Lastly, 6 specific registers are used to pass parameters to functions. What are they and which arguments do they correspond to? 38 | 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) 39 | - Which are the callee-saved registers? 40 | - 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). 41 | - 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? 42 | - 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) 43 | 44 | ***Break (5 mins)*** 45 | 46 | ## Instructions 47 | 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. 48 | 1. Figure out what the following instructions do: (30 mins) 49 | 50 | | instruction | example | 51 | |---|---| 52 | | mov | mov rax, rbx | 53 | | push | push rax | 54 | | pop | pop rax | 55 | | add | add rax, 42 | 56 | | sub | sub rax, 42 | 57 | | inc | inc rax | 58 | | dec | dec rax | 59 | | cmp | cmp rax, 42 | 60 | | jmp | jmp _main | 61 | | je/jne/jl/jg | je _done | 62 | | call | call _printf | 63 | | ret | ret | 64 | 65 | - Make sure you know which operand is affecting which! 66 | 2. What effect would the following `jmp` instruction have? (5 mins) 67 | ``` 68 | _main: 69 | jmp _main 70 | ``` 71 | 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) 72 | - What's the difference then between `mov rax, rbx` and `mov rax, [rbx]`? 73 | 74 | ## Syscall 75 | Now let's look at system calls or **syscall**s, which allow a program to request a service from the kernel. 76 | 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) 77 | - 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) 78 | - 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. 79 | - What are the syscall IDs for `write()` and `exit()` for your system? 80 | 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)\ 81 | Here's a convenient table for you: 82 | | syscall | rax | rdi | rsi | rdx | rcx (r10 for Linux) | r8 | r9 | 83 | |---|---|---|---|---|---|---|---| 84 | | write | | | | | | | | 85 | 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) 86 | - First, create a `.s` file. Here's what the first part of your code should look like: 87 | ``` 88 | section .text ; this is the section for code 89 | global _main ; this tells the kernel where the program begins 90 | _main: 91 | ``` 92 | - Note: for Linux users, you'll need to remove the `_` before `_main`. 93 | - To make a system call, you need to: 94 | 1. pass the syscall ID into RAX 95 | 2. pass any arguments for the syscall 96 | 3. use the `syscall` instruction 97 | 98 | Go ahead and turn those steps into assembly code. 99 | - 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: 100 | - **Note**: remember to change `myfile.s` & `myfile.o` to your actual file name 101 | - For Linux: `nasm -felf64 myfile.s && gcc myfile.o` 102 | - For mac: `nasm -fmacho64 myfile.s && gcc myfile.o` 103 | - Run `./a.out` and then `echo $?`. Does it output `0`? 104 | 105 | ***Break (5 mins)*** 106 | 107 | ## Sections 108 | 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) 109 | 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) 110 | - Let's start by declaring a new section (section order doesn't matter in an assembly file): 111 | ``` 112 | section .data 113 | text db "Hello, world!", 10 114 | ``` 115 | - What is "text" here? And what does "db" mean? What is the "10" at the end? 116 | - Next declare your `.text` section as you did in the previous exercise. 117 | - 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? 118 | - Finish with a call to `exit()` again like you did earlier. 119 | - Compile with the same commands you used earlier. Do you get "Hello, world!"? 120 | - Note: your compiler may throw up some warnings, but if your program was compiled successfully, just ignore it! 121 | 122 | ## Stack Alignment 123 | 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. 124 | - When your program starts at `main` (or `start`), `rsp` (the stack pointer) is 16-byte aligned. 125 | - 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? 126 | - But this means the stack is now misaligned by **8 bytes**. So how do we re-align our stack? (15 mins) 127 | 128 | You can find more detailed explanations of the stack and alignment online.\ 129 | 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) 130 | 131 | ## Bonus 132 | 1. Write a `compare` function in assembly that compares 2 integers and returns: 133 | - `1` if `a > b`; 134 | - `-1` if `a < b`; 135 | - `0` if `a == b`. 136 | 137 | The function prototype is `int compare(int64_t a, int64_t b)`. 138 | 139 | 2. You'll also make a test main C file. It should: 140 | - include for the `int64_t` types; 141 | - have your `compare` function prototype; 142 | - call your `compare` function with a variety of inputs (positive, negative, 0s) and print the return. 143 | 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. 144 | 4. Compile and run it to see if your function is working correctly. 145 | - For Linux: `nasm -felf64 compare.s && gcc compare.o main.c` 146 | - For macOS: `nasm -fmacho64 compare.s && gcc compare.o main.c` 147 | -------------------------------------------------------------------------------- /spark-sessions/minilibx/SparkSession - minilibx - mod.md: -------------------------------------------------------------------------------- 1 | # Spark Session: minilibx 2 | *updated: 04/03/2021* 3 | 4 | Session description: 5 | > Learn the basics of working with miniLibX 6 | 7 | This tutorial was written with help from Harm Smits and Jelle van Snik's [MiniLibX tutorial](https://harm-smits.github.io/42docs/libs/minilibx). 8 | 9 | ## Topics 10 | 1. Window Management 11 | 2. Pixel Putting 12 | 3. More Pixels 13 | 4. Events & Hooks 14 | 15 | ### Window Management 16 | Our first step will be to open up some windows! (30 mins) 17 | 1. In the set-up instructions, I gave you some code for your `main.c` that included a call to `mlx_init`. 18 | But what does it do and what is its prototype? What does it return? (5 mins) 19 | This link might help -> [prototypes](https://harm-smits.github.io/42docs/libs/minilibx/prototypes.html) 20 | > Function: initialises mlx, establishes a connection to the correct graphical system 21 | > Prototype: `void *mlx_init();` 22 | > Return: mlx instance 23 | 2. Let's try opening a small empty window. (10 mins) 24 | - What is the prototype for `mlx_new_window` and what does it return? 25 | - How would you declare and initalize it? 26 | - Now create a window with a width of **800**, height of **480**, and a title of **"My first window"**. 27 | > Prototype: `void *mlx_new_window(void *mlx_ptr, int size_x, int size_y, char *title);` 28 | > Return: window instance pointer 29 | > Code: `void *mlx_win = mlx_new_window(mlx, 800, 480, "My first window");` 30 | 3. What happens if you compile and run the program at this point? Your window should have only popped up for a moment. 31 | To make it stay longer, we need to use `mlx_loop`. (15 mins) 32 | - What does it do and what is its prototype? 33 | - Once you understand that, add `mlx_loop` to your code. 34 | - Do you now get a window that stays open? Press `Ctrl-C` to close it when you're done admiring your work. 35 | - **Important**: `mlx_loop` should be called last in your code. Do you know why? 36 | > Function: loops over the given mlx pointer 37 | > Prototype: `int mlx_loop (void *mlx_ptr); // return unused` 38 | > If mlx_loop is called before any other function, it won't get there. 39 | 40 | ***Break (5 mins)*** 41 | 42 | ### Pixel Putting 43 | Time to put something on that empty window. (60 mins) 44 | 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) 45 | - What is `mlx_new_image`'s prototype and return? 46 | - Once you understand that, go ahead and initialise an image with a size of **800 x 480**. 47 | > Prototype: `void *mlx_new_image(void *mlx_ptr,int width,int height);` 48 | > Return: image instance pointer 49 | > Code: `void *img_ptr = mlx_new_image(mlx, 800, 480);` 50 | 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) 51 | > Prototype: `char *mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian);` 52 | > Return: memory address of image 53 | 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) 54 | ``` 55 | typedef struct s_img 56 | { 57 | void *img_ptr; 58 | char *address; 59 | int bits_per_pixel; 60 | int line_size; 61 | int endian; 62 | } t_img; 63 | ``` 64 | - Notice that we shifted the image pointer into the struct. Adjust your initialisation of `mlx_new_image` accordingly. 65 | - Then call `mlx_get_data_addr` and pass it the appropriate arguments/references. 66 | 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) 67 | ``` 68 | void my_pixel_put(t_img *img, int x, int y, unsigned int colour) 69 | { 70 | char *dst; 71 | int offset; 72 | 73 | offset = y * img->line_size + x * (img->bits_per_pixel / 8); 74 | dst = img->address + offset; 75 | *(unsigned int *)dst = colour; 76 | } 77 | ``` 78 | - What is this function doing? What is `offset`? 79 | > The function calculates the address of a pixel by adding its memory offset to the address of the first pixel (img->address here). 80 | > 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. 81 | > [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/) 82 | 7. Now, using your `my_pixel_put` function, put a **white** pixel in the **middle** of your image. (10 mins) 83 | > Code: `my_pixel_put(&img, 800/2, 480/2, 0xFFFFFF);` 84 | 8. Our image is all ready to be shown! Let's look at `mlx_put_image_to_window`. What parameters does it take? 85 | Add the function to your code and see if your little white dot is showing in your window. (10 mins) 86 | > Prototype: `int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y);` 87 | 88 | ***Break (5 mins)*** 89 | 90 | ### More Pixels 91 | Let's get fancier. Now we're gonna try drawing *lines*. (25 mins) 92 | 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) 93 | ``` 94 | // example code answer 95 | int x = 0; 96 | while (x < screen_width) 97 | { 98 | my_pixel_put(&img, x, screen_height / 2, 0xFFFFFF); 99 | x++; 100 | } 101 | ``` 102 | 2. Now draw a single vertical white line down the middle of the entire screen. (10 mins)\ 103 | You should end up with what looks like a crosshair in your window. 104 | ``` 105 | // example code answer 106 | int y = 0; 107 | while (y < screen_height) 108 | { 109 | my_pixel_put(&img, screen_width / 2, y, 0xFFFFFF); 110 | y++; 111 | } 112 | ``` 113 | 114 | ### Events & Hooks 115 | 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) 116 | 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.\ 117 | What is the prototype for `mlx_hook`? *(Hint: you may have to look it up in mlx.h)* (5 mins) 118 | > Prototype: `int mlx_hook(t_win_list *win, int x_event, int x_mask, int (*funct)(), void *param);` 119 | 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) 120 | - Here's something that might help you understand: [event processing](https://tronche.com/gui/x/xlib/events/processing-overview.html) 121 | > Passing event codes & masks allows you to specify which events you want to be notified of 122 | 3. What are the **event codes** and **masks** for key presses, key releases, and the 'X' close button? (10 mins) 123 | - 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) 124 | - **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". 125 | > Codes: press = 2, release = 3, X button = 17 (Mac) or 33 (Linux) 126 | > Masks: press = `1L << 0`, release = `1L << 1`, X button = `1L << 17` 127 | 4. Write a function that: (10 mins) 128 | - 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)*; 129 | - destroys your window and exits your program. 130 | 5. Add a call to `mlx_hook` in your main that calls this exiting function when the 'X' button is pressed. (5 mins) 131 | - Does your window close now when you press the 'X' close button on your window? 132 | ``` 133 | // example code answer 134 | int exiter(t_data *game) 135 | { 136 | mlx_destroy_window(game->mlx_ptr, game->win); 137 | exit(0); 138 | } 139 | int main() 140 | { 141 | ... 142 | mlx_hook(game.win, 33, 1L << 17, exiter, &game); // replace 33 with 17 for Mac 143 | } 144 | ``` 145 | 146 | ### Bonus 147 | Let's get some movement on screen: make your crosshair move in 4 directions! 148 | 149 | First, however, let's make our crosshair smaller, because who needs a crosshair that big? 150 | 1. Expand your struct to include **at least** the following variables you'll need for your drawing function: 151 | - object width & height; 152 | - starting x & y positions (i.e. the coordinates of the leftmost pixel of your crosshair). 153 | 2. Make a `draw_crosshair` function that: 154 | - accepts your data/game struct as its parameter; 155 | - can render a crosshair of a particular **width** and **height**, instead of only the height/width of the screen; 156 | - renders that crosshair in the **middle of the screen** *(you'll have to do some math using the object dimensions and starting positions, sorry)*; 157 | - calls `mlx_put_image_to_window` at the end. 158 | 3. Get a **30 x 30** pixel crosshair onto your window. Did it work? 159 | > Note: there will be different ways of solving this. I've included some example non-optimised solutions on the last page of this document. 160 | > Example formula for computing starting x of crosshair (i.e. the leftmost pixel of the horizontal line): 161 | `start_x = (game.screen_width - game.obj.width) / 2` 162 | 163 | Now let's hook into keyboard events! 164 | 1. Add a call to `mlx_hook` in your main that calls a function `keypress` when keys are...well, pressed. 165 | 2. Write that `keypress` function that: 166 | - calls your exit function when the `ESC` key is pressed; 167 | - moves the crosshair up, down, left, and right when the corresponding key is pressed. 168 | - you can choose to use the arrow keys or `W`-`A`-`S`-`D` keys 169 | - I've included helpful diagrams below for the keycodes you'll need. 170 | 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. 171 | 4. Do you now have a crosshair that can move across your screen? 172 | - If you're seeing a trail of crosshairs, you're probably not rendering the background each time. 173 | - If your program is crashing when you hit one of the walls, perhaps you should add checks to your keypress function. 174 | 175 | macOS\ 176 | ![macOS key codes](https://i.imgur.com/CNPRkMg.png)\ 177 | 178 | Linux\ 179 | ![Linux key codes](https://i.imgur.com/a6yVUhm.png) 180 | 181 | ``` 182 | // example code for draw_crosshair & keypress functions 183 | void draw_crosshair(t_data *game) 184 | { 185 | for (int x = 0; x < game->screen_width; x++) 186 | { 187 | for (int y = 0; y < game->screen_height; y++) 188 | my_pixel_put(game->img, x, y, BLACK); // draw background 189 | } 190 | int x_end = game->obj.start.x + game->obj.width; // end of horizontal line 191 | for (int x = game->obj.start.x; x < x_end; x++) 192 | { 193 | int y = game->obj.start.y; 194 | int y_end = y + 1; 195 | if (x == x_end - game->obj.width / 2) // for vertical line 196 | { 197 | y -= game->obj.height / 2; // top of vertical line 198 | y_end = y + game->obj.height; // bottom of vertical line 199 | } 200 | while (y < y_end) 201 | { 202 | my_pixel_put(game->img, x, y, WHITE); // draw crosshair 203 | y++; 204 | } 205 | mlx_put_image_to_window(game->mlx_ptr, game->win, game->img->img_ptr, 0, 0); 206 | } 207 | 208 | int keypress(int keycode, t_data *game) 209 | { 210 | if (keycode == ESC) 211 | exiter(game); 212 | else if (keycode == MV_UP) 213 | game->obj.start.y -= 10; 214 | else if (keycode == MV_DW) 215 | { 216 | if (game->obj.start.y + (game->obj.height / 2) + 10 217 | <= game->screen_height) 218 | game->obj.start.y += 10; 219 | } 220 | else if (keycode == MV_LF) 221 | game->obj.start.x -= 10; 222 | else if (keycode == MV_RT) 223 | { 224 | if (game->obj.start.x + game->obj.width + 10 <= game->screen_width) 225 | game->obj.start.x += 10; 226 | } 227 | return (0); 228 | } 229 | 230 | int main(void) 231 | { 232 | ... 233 | game.obj.height = 30, game.obj.width = 30; 234 | game.obj.start.x = (game.screen_width - game.obj.width) / 2; 235 | game.obj.start.y = game.screen_height / 2; 236 | draw_crosshair(&game); 237 | 238 | mlx_hook(game.win, 33, 1L << 17, exiter, &game); // event code on Linux 239 | mlx_hook(game.win, 2, 1L << 0, keypress, &game); 240 | mlx_loop_hook(game.mlx_ptr, &updater, &game); 241 | mlx_loop(game.mlx_ptr); 242 | } 243 | ``` -------------------------------------------------------------------------------- /spark-sessions/minilibx/SparkSession - minilibx setup.md: -------------------------------------------------------------------------------- 1 | ## Setting Up miniLibX 2 | *updated: 04/03/2021* 3 | 4 | During the Spark Session, we'll be writing a little program that can open windows, draw pixels, and maybe even move pixels! 5 | 6 | Before we can do that, we need to set up the miniLibX library and **link** the compiled library with our source code. 7 | 1. Download the minilibx library into the **root directory**. 8 | - For macOs: from intra, whichever version (OpenGL/mms_beta) that works with your system 9 | - For Linux: from the [42Paris repo](https://github.com/42Paris/minilibx-linux) 10 | - **The next steps assume you've called the folders `mlx` or `mlx_linux`**. 11 | 2. Create a `main.c` in the root directory with the following content (remember to include the **mlx.h header**!): 12 | ``` 13 | int main(void) 14 | { 15 | void *mlx_ptr; 16 | 17 | mlx_ptr = mlx_init(); 18 | return (0); 19 | } 20 | ``` 21 | This will help you check at the end if you're linking your mlx correctly to your source files. 22 | 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). 23 | Having a Makefile makes our lives easier. 24 | Add the required rules - `$(NAME)`, `clean`, `fclean`, `re`, `all`. 25 | 26 | Here's a [helpful guide on Makefiles](https://noahloomans.com/tutorials/makefile/) written by another student, Noah Loomans. 27 | 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). 28 | 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). 29 | **Tip: you could have your Makefile do all this too.** *Hint: `make -C dir`* 30 | 5. Using miniLibX requires that we link the necessary **internal API’s**. Here's what you should add to your project Makefile: 31 | - 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. 32 | - For macOS: *(make sure the compilation command is on one line)* 33 | ``` 34 | $(NAME): $(OBJ) 35 | $(CC) $(OBJ) -Lmlx -lmlx -framework OpenGL -framework AppKit -o $(NAME) 36 | ``` 37 | - 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 38 | ``` 39 | $(NAME): $(OBJ) 40 | $(CC) $(OBJ) -Lmlx_linux -lmlx -lXext -lX11 -lm -lz -o $(NAME) 41 | ``` 42 | 6. Additional steps if you’re doing this through **Windows Subsystem for Linux**: you need to install [Xming](https://sourceforge.net/projects/xming/) first. 43 | - Once Xming is installed, exit Xming and launch XLaunch. Choose the following options:\ 44 | `Multiple windows` -> `Start no client` -> Enable `"No access control"` -> `Finish` 45 | - Then execute this command: `export DISPLAY=localhost:0.0` 46 | - You can check if everything’s working by running `sudo apt-get install x11-apps` and then executing `xeyes`. 47 | - **Note**: XLaunch has to be active and the `export DISPLAY` command above must have been run before you can launch graphic programs using miniLibX. 48 | 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! -------------------------------------------------------------------------------- /spark-sessions/minilibx/SparkSession - minilibx.md: -------------------------------------------------------------------------------- 1 | # Spark Session: minilibx 2 | *updated: 04/03/2021* 3 | 4 | Session description: 5 | > Learn the basics of working with miniLibX 6 | 7 | This tutorial was written with help from Harm Smits and Jelle van Snik's [MiniLibX tutorial](https://harm-smits.github.io/42docs/libs/minilibx). 8 | 9 | ## Topics 10 | 1. Window Management 11 | 2. Pixel Putting 12 | 3. More Pixels 13 | 4. Events & Hooks 14 | 15 | ### Window Management 16 | Our first step will be to open up some windows! (30 mins) 17 | 1. In the set-up instructions, I gave you some code for your `main.c` that included a call to `mlx_init`. 18 | But what does it do and what is its prototype? What does it return? (5 mins) 19 | This link might help -> [prototypes](https://harm-smits.github.io/42docs/libs/minilibx/prototypes.html) 20 | 2. Let's try opening a small empty window. (10 mins) 21 | - What is the prototype for `mlx_new_window` and what does it return? 22 | - How would you declare and initalize it? 23 | - Now create a window with a width of **800**, height of **480**, and a title of **"My first window"**. 24 | 3. What happens if you compile and run the program at this point? Your window should have only popped up for a moment. 25 | To make it stay longer, we need to use `mlx_loop`. (15 mins) 26 | - What does it do and what is its prototype? 27 | - Once you understand that, add `mlx_loop` to your code. 28 | - Do you now get a window that stays open? Press `Ctrl-C` to close it when you're done admiring your work. 29 | - **Important**: `mlx_loop` should be called last in your code. Do you know why? 30 | 31 | ***Break (5 mins)*** 32 | 33 | ### Pixel Putting 34 | Time to put something on that empty window. (60 mins) 35 | 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) 36 | - What is `mlx_new_image`'s prototype and return? 37 | - Once you understand that, go ahead and initialise an image with a size of **800 x 480**. 38 | 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) 39 | 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) 40 | ``` 41 | typedef struct s_img 42 | { 43 | void *img_ptr; 44 | char *address; 45 | int bits_per_pixel; 46 | int line_size; 47 | int endian; 48 | } t_img; 49 | ``` 50 | - Notice that we shifted the image pointer into the struct. Adjust your initialisation of `mlx_new_image` accordingly. 51 | - Then call `mlx_get_data_addr` and pass it the appropriate arguments/references. 52 | 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) 53 | ``` 54 | void my_pixel_put(t_img *img, int x, int y, unsigned int colour) 55 | { 56 | char *dst; 57 | int offset; 58 | 59 | offset = y * img->line_size + x * (img->bits_per_pixel / 8); 60 | dst = img->address + offset; 61 | *(unsigned int *)dst = colour; 62 | } 63 | ``` 64 | - What is this function doing? What is `offset`? 65 | 7. Now, using your `my_pixel_put` function, put a **white** pixel in the **middle** of your image. (10 mins) 66 | 8. Our image is all ready to be shown! Let's look at `mlx_put_image_to_window`. What parameters does it take? 67 | Add the function to your code and see if your little white dot is showing in your window. (10 mins) 68 | 69 | ***Break (5 mins)*** 70 | 71 | ### More Pixels 72 | Let's get fancier. Now we're gonna try drawing *lines*. (25 mins) 73 | 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) 74 | 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) 75 | 76 |   77 | ### Events & Hooks 78 | 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) 79 | 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. 80 | What is the prototype for `mlx_hook`? *(Hint: you may have to look it up in mlx.h)* (5 mins) 81 | 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) 82 | - Here's something that might help you understand: [event processing](https://tronche.com/gui/x/xlib/events/processing-overview.html) 83 | 3. What are the **event codes** and **masks** for key presses, key releases, and the 'X' close button? (10 mins) 84 | - 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) 85 | - **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". 86 | 4. Write a function that: (10 mins) 87 | - 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)*; 88 | - destroys your window and exits your program. 89 | 5. Add a call to `mlx_hook` in your main that calls this exiting function when the 'X' button is pressed. (5 mins) 90 | - Does your window close now when you press the 'X' close button on your window? 91 | 92 | ### Bonus 93 | Let's get some movement on screen: make your crosshair move in 4 directions! 94 | 95 | First, however, let's make our crosshair smaller, because who needs a crosshair that big? 96 | 1. Expand your struct to include **at least** the following variables you'll need for your drawing function: 97 | - object width & height; 98 | - starting x & y positions (i.e. the coordinates of the leftmost pixel of your crosshair). 99 | 2. Make a `draw_crosshair` function that: 100 | - accepts your data/game struct as its parameter; 101 | - can render a crosshair of a particular **width** and **height**, instead of only the height/width of the screen; 102 | - renders that crosshair in the **middle of the screen** *(you'll have to do some math using the object dimensions and starting positions, sorry)*; 103 | - calls `mlx_put_image_to_window` at the end. 104 | 3. Get a **30 x 30** pixel crosshair onto your window. Did it work? 105 | 106 | Now let's hook into keyboard events! 107 | 1. Add a call to `mlx_hook` in your main that calls a function `keypress` when keys are...well, pressed. 108 | 2. Write that `keypress` function that: 109 | - calls your exit function when the `ESC` key is pressed; 110 | - moves the crosshair up, down, left, and right when the corresponding key is pressed. 111 | - you can choose to use the arrow keys or `W`-`A`-`S`-`D` keys. 112 | - I've included helpful diagrams below for the keycodes you'll need. 113 | 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. 114 | 4. Do you now have a crosshair that can move across your screen? 115 | - If you're seeing a trail of crosshairs, you're probably not rendering the background each time. 116 | - If your program is crashing when you hit one of the walls, perhaps you should add checks to your keypress function. 117 | 118 | macOS\ 119 | ![macOS key codes](https://i.imgur.com/CNPRkMg.png) 120 | 121 | Linux\ 122 | ![Linux key codes](https://i.imgur.com/a6yVUhm.png) -------------------------------------------------------------------------------- /spark-sessions/minishell/SparkSession - minishell - mod.md: -------------------------------------------------------------------------------- 1 | # Spark Session: minishell 2 | *updated: 06/04/2021* 3 | 4 | Project description: 5 | > Create a simple shell 6 | 7 | ## Topics 8 | 1. Processes 9 | 2. fork 10 | 3. wait 11 | 4. execve 12 | 5. dup & dup2 13 | 6. pipe 14 | 15 | ### Processes 16 | Before we get into how to work with processes, it's handy to understand what we actually mean by "process". 17 | 1. What is a process? (5 mins) 18 | > A process is a program in execution, an entity which implements a set of instructions (given by the program) in the system. 19 | > Processes are identified by their unique process ID (which is recycled after it terminates). 20 | > The `exec` family of functions implements the system call that creates a process. 21 | 22 | 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.\ 23 | Here's a diagram showing how this memory is divided:\ 24 | ![process memory](https://i.imgur.com/nxmmQl3.png) 25 | 26 | To put it in really simple terms, you can think of a process like a struct — a collection of information bound to an entity. 27 | 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. 28 | 29 | ### fork 30 | `fork()` creates a new process — called the **child process** — by duplicating the calling process (**the parent process**). 31 | 1. What is the prototype of `fork()`? What does the function return? (10 mins) 32 | > `pid_t fork(void);` 33 | > 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. 34 | 35 | - How could you use the function return to identify if you are in the child or parent process? 36 | > By checking if the PID is 0, you can know if you're in the child process. 37 | 38 | - Is `fork`'s return the same as the child's PID? 39 | > Yes in the parent. Not in the child; child's actual PID can be seen by running `getpid`. 40 | 2. Which of the following is copied from the parent process to the child process? Which are not? (10 mins) 41 | - Data (the content of the process' memory space) 42 | - Location in memory 43 | - Process ID 44 | - Open file descriptors 45 | > Data: same data in their respective memory spaces (at time of fork) 46 | > Location in memory: not same - separate memory spaces 47 | > Process ID: not same - child has unique PID that doesn't match any of existing process group or session 48 | > 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. 49 | 3. Let's see some of these characteristics in action. (20 mins) 50 | - Write a program that: 51 | - initialises an int `x` to **5**; 52 | - calls `fork()` and then prints its return value in a statement `"fork returned: %d\n"`; 53 | - checks for failed forks; 54 | - if in the **child** process: **decrements** `x` by 1, prints `"This line is from child, x is %d\n"`, and then **returns** 0; 55 | - else if in the **parent** process: **increments** `x` by 1 and then prints `"This line is from parent, x is %d\n"`. 56 | - 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.\ 57 | ![example output](https://i.imgur.com/jOTXMCL.png)\ 58 | *example output - your output order may vary* 59 | - 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. 60 | - 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. 61 | 62 | 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. 63 | 64 | ***Break (5 mins)*** 65 | 66 | ### wait 67 | 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. 68 | 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) 69 | > `pid_t wait(int *wstatus);` 70 | > if not NULL, `wstatus` contains the child process' status information, which can be checked with macros like `WIFSIGNALED` and `WIFEXITED`. 71 | 2. Calling `wait()` (or `waitpid()`) in the parent process prevents what's called **"zombie processes"**. What does this mean? (10 mins) 72 | > 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. 73 | > 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. 74 | 3. Let's add `wait()` to the code you wrote earlier. (10 mins) 75 | - Create an int variable, for example `w_status`, to be passed to `wait()`. 76 | - In the **parent process** code block, call `wait()` before anything else. *Remember to pass it the address of your `w_status` int.* 77 | - Make sure the **child process** is calling `return` when it's done. 78 | - 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. 79 | > if (WIFEXITED(w_status)) { 80 | > printf("Child process exited with status: %d\n", WEXITSTATUS(w_status));} 81 | - Try tweaking the argument you pass to the `return()` call in your child process. Does the output change accordingly? 82 | ![example output](https://i.imgur.com/cFZAZFp.png)\ 83 | *example output* 84 | 85 | Here's a fun short explanation about zombie processes for later: [understanding zombie processes](https://youtu.be/xJ8KenZw2ag) 86 | 87 | ### execve 88 | The `exec()` family of functions allows us to **replace** the current process with a new program.\ 89 | No new process is created; the PID remains the same. The functions simply have the existing process execute a new program. 90 | 1. What is the prototype of `execve()`? (10 mins) 91 | - Break down each of function parameters. What does each mean? 92 | > `int execve(const char *pathname, char *const argv[], char *const envp[]);` 93 | > pathname: binary executable, for example "/bin/ls" 94 | > argv: an array of pointers to strings to be passed to the program as its arguments. 95 | > envp: the environment variables you want to pass to the new program, which can differ from the env variables passed to the calling process 96 | > 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. 97 | 2. What does `execve()` return? (5 mins) 98 | > On success, it doesn't return. Only returns on failure, giving -1 and setting erno appropriately. 99 | 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) 100 | - **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. 101 | - At the beginning of your main, declare a `char *argv[3]`. 102 | - Initialize `argv[0]` to `"/bin/ls"`. 103 | - Initialize `argv[1]` to `"-l"`. 104 | - Initialize `argv[2]` to `NULL`. 105 | - *Do you know why these arguments are made in this order?* 106 | > By convention, argv[0] should contain the filename associated with the file 107 | being executed. The argv array must be terminated by a NULL pointer. 108 | 109 | - 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. 110 | - Below the `execv` call, place another print statement, `"This line is from child after execv"`. 111 | - Does your program execute `ls` with the `-l` list option when you run it? Does the "after execv" statement print? 112 | > 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. 113 | 114 | ***Break (5 mins)*** 115 | 116 | ### dup & dup2 117 | `dup()` and `dup2()` create a **copy** of a file descriptor. 118 | 1. What is the prototype for `dup()`? What about `dup2()`? What do both return? (5 mins) 119 | > `int dup(int oldfd);` 120 | > `int dup2(int oldfd, int newfd);` 121 | > on success, returns newfd. on failure, -1 122 | 2. What are some differences between `dup` and `dup2` with regards to the new file descriptor? (5 mins) 123 | - Here's a diagram to help you visualize the functions better:\ 124 | ![dup](https://i.imgur.com/iJ7X39I.png) 125 | > `dup2` uses specified newfd, `dup` uses lowest-numbered unused fd. 126 | > `dup2`: If newfd was previously open, it is silently closed before being reused. 127 | 3. What do the new and old file descriptor share? (5 mins) 128 | > 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. 129 | 4. Now let's write a program that: (15 mins) 130 | - opens a `test.txt` file with the following flags: `O_CREAT | O_TRUNC | O_RDWR, 0644` 131 | - 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. 132 | - Here's a handy [permissions calculator](http://permissions-calculator.org/). 133 | - saves the `open` return in an int `fd`; 134 | - creates another int, for example `dup_fd`; 135 | - calls `dup()`, giving it `fd` as argument and saving its return in `dup_fd`; 136 | - passes `fd` as the 1st argument to `write()`, with the string `"This will be written to the test file\n"`. 137 | - passes `dup_fd` as the 1st argument to `write()`, with the string `"This will also be written to the test file\n"`.\ 138 | ![example output](https://i.imgur.com/ikWDFXL.png?1) 139 | > `write(fd, "This will be written to the test file\n", 38);` 140 | > `write(dup_fd, "This will also be written to the test file\n", 43);` 141 | 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) 142 | - Now, using `dup2`, turn the fd `1` (that is, stdout) into a copy of our test.txt `fd`. 143 | - Call `write()` again, outputting `"This isn't being printed on stdout\n"` onto stdout (1). 144 | - Does anything get printed onto your terminal when you run the program? 145 | > `dup2(fd, 1);` 146 | > `write(1, "This isn't being printed on stdout\n", 35);` 147 | > string should be printed in test.txt instead of terminal 148 | 149 | ## Bonus 150 | ### pipe 151 | `pipe()` allows data to be passed from one process to another. 152 | 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.\ 153 | ![one-way pipe](https://www.tutorialspoint.com/inter_process_communication/images/pipe_with_one.jpg) 154 | 1. What is the prototype of `pipe()`? What is being stored in the int array you're passing it? (10 mins) 155 | > `int pipe(int pipefd[2]);` 156 | > pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. 157 | 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)\ 158 | ![pipe in out](https://i.imgur.com/DzjZwSl.png) 159 | > 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. 160 | > therefore, the 1st process would write to `pipefd[1]` and the 2nd process would read from `pipefd[0]`. 161 | 3. Let's try using `pipe` in combination with `fork` and `wait`! Write a program that: (15 mins) 162 | - takes command-line arguments (i.e. `int argc, char **argv`); 163 | - creates a **pipe**; 164 | - then **forks** to create a child process; 165 | - the parent process: 166 | - should **close** the pipe end that it doesn't need; 167 | - **writes** argv[1] to the correct end of the pipe; 168 | - **closes** the remaining pipe end; 169 | - **waits** for its child processes to terminate and checks their status. 170 | - the child process: 171 | - should **close** the pipe end that it doesn't need; 172 | - in a loop, **reads** the string from the pipe, one byte at a time; 173 | - calls `toupper()` (include `ctype.h`) on the read char; 174 | - **writes** the converted char to stdout; 175 | - writes a newline to stdout; 176 | - **closes** the remaining pipe end.\ 177 | ![pipe example](https://i.imgur.com/40cUyDo.png) 178 | 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? 179 | ![closing ends](https://chensunmac.gitbooks.io/csc209-practical-programming/content/assets/pipe2.png) 180 | > 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. 181 | > If all fds referring to the read end of a pipe are closed, attempting `write` on the other end generates a SIGPIPE signal. 182 | > Unnecessary duplicate fds should thus be closed, to ensure that EOF and SIGPIPE are delivered when appropriate, preventing a hanging state. 183 | 184 | That was a simple exercise to show you how data can be passed through pipes and interacted with within child processes. 185 | 186 | Things get even more mind-blowing when you throw `dup`/`dup2` into the mix. 187 | 188 | ## Tips 189 | Here's a more detailed explanation about data flows through pipes, with handy diagrams: [pipes, forks, & dups](http://www.rozmichelle.com/pipes-forks-dups/) 190 | 191 | And here's a couple other things that could be helpful to look into for your project: 192 | - abstract syntax tree 193 | - finite state machines 194 | 195 | 196 | ```c 197 | // example solution for pipe exercise 198 | #include 199 | #include 200 | #include 201 | #include 202 | #include 203 | 204 | int main(int ac, char **av) { 205 | int pipefd[2]; 206 | char buf; 207 | 208 | if (ac != 2){ 209 | printf("incorrect number of args\n"); 210 | return (1); 211 | } 212 | if (pipe(pipefd) < 0) { 213 | printf("pipe failed\n"); 214 | return (1); 215 | } 216 | pid_t pid = fork(); 217 | if (pid < 0) 218 | printf("fork failed\n"); 219 | else if (pid == 0) { 220 | close(pipefd[1]); 221 | while (read(pipefd[0], &buf, 1) > 0) { 222 | char up = toupper(buf); 223 | write(STDOUT_FILENO, &up, 1); 224 | } 225 | write(STDOUT_FILENO, "\n", 1); 226 | close(pipefd[0]); 227 | return(0); 228 | } 229 | else { 230 | int w_status = 0; 231 | close(pipefd[0]); 232 | write(pipefd[1], av[1], strlen(av[1])); 233 | close(pipefd[1]); 234 | wait(&w_status); 235 | if (WIFEXITED(w_status)) 236 | printf("Child process exited with status: %d\n", WEXITSTATUS(w_status)); 237 | } 238 | return (0); 239 | } 240 | ``` 241 | -------------------------------------------------------------------------------- /spark-sessions/minishell/SparkSession - minishell.md: -------------------------------------------------------------------------------- 1 | # Spark Session: minishell 2 | *updated: 30/06/2021* 3 | 4 | Project description: 5 | > Create a simple shell 6 | 7 | ## Topics 8 | 1. Processes 9 | 2. fork 10 | 3. wait 11 | 4. execve 12 | 5. dup & dup2 13 | 6. pipe 14 | 15 | ### Processes 16 | Before we get into how to work with processes, it's handy to understand what we actually mean by "process". 17 | 1. What is a process? (5 mins) 18 | 19 | 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.\ 20 | Here's a diagram showing how this memory is divided:\ 21 | ![process memory](https://i.imgur.com/nxmmQl3.png) 22 | 23 | To put it in really simple terms, you can think of a process like a struct — a collection of information bound to an entity. 24 | 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. 25 | 26 | ### fork 27 | `fork()` creates a new process — called the **child process** — by duplicating the calling process (**the parent process**). 28 | 1. What is the prototype of `fork()`? What does the function return? (10 mins) 29 | - How could you use the function return to identify if you are in the child or parent process? 30 | - Is `fork`'s return the same as the child's PID? 31 | 2. Which of the following is copied from the parent process to the child process? Which are not? (10 mins) 32 | - Data (the content of the process' memory space) 33 | - Location in memory 34 | - Process ID 35 | - Open file descriptors 36 | 3. Let's see some of these characteristics in action. (20 mins) 37 | - Write a program that: 38 | - initialises an int `x` to **5**; 39 | - calls `fork()` and then prints its return value in a statement `"fork returned: %d\n"`; 40 | - checks for failed forks; 41 | - if in the **child** process: **decrements** `x` by 1, prints `"This line is from child, x is %d\n"`, and then **returns** 0; 42 | - else if in the **parent** process: **increments** `x` by 1 and then prints `"This line is from parent, x is %d\n"`. 43 | - 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.\ 44 | ![example output](https://i.imgur.com/jOTXMCL.png)\ 45 | *example output - your output order may vary* 46 | - 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. 47 | - 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. 48 | 49 | 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. 50 | 51 | ***Break (5 mins)*** 52 | 53 | ### wait 54 | 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. 55 | 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) 56 | 2. Calling `wait()` (or `waitpid()`) in the parent process prevents what's called **"zombie processes"**. What does this mean? (10 mins) 57 | 3. Let's add `wait()` to the code you wrote earlier. (10 mins) 58 | - Create an int variable, for example `w_status`, to be passed to `wait()`. 59 | - In the **parent process** code block, call `wait()` before anything else. *Remember to pass it the address of your `w_status` int.* 60 | - Make sure the **child process** is calling `return` when it's done. 61 | - 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. 62 | - Try tweaking the argument you pass to the `return()` call in your child process. Does the output change accordingly? 63 | ![example output](https://i.imgur.com/cFZAZFp.png)\ 64 | *example output* 65 | 66 | Here's a fun short explanation about zombie processes for later: [understanding zombie processes](https://youtu.be/xJ8KenZw2ag) 67 | 68 | ### execve 69 | The `exec()` family of functions allows us to **replace** the current process with a new program.\ 70 | No new process is created; the PID remains the same. The functions simply have the existing process execute a new program. 71 | 1. What is the prototype of `execve()`? (10 mins) 72 | - Break down each of function parameters. What does each mean? 73 | 2. What does `execve()` return? (5 mins) 74 | 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) 75 | - **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. 76 | - At the beginning of your main, declare a `char *argv[3]`. 77 | - Initialize `argv[0]` to `"/bin/ls"`. 78 | - Initialize `argv[1]` to `"-l"`. 79 | - Initialize `argv[2]` to `NULL`. 80 | - *Do you know why these arguments are made in this order?* 81 | - 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. 82 | - Below the `execv` call, place another print statement, `"This line is from child after execv"`. 83 | - Does your program execute `ls` with the `-l` list option when you run it? Does the "after execv" statement print? 84 | 85 | ***Break (5 mins)*** 86 | 87 | ### dup & dup2 88 | `dup()` and `dup2()` create a **copy** of a file descriptor. 89 | 1. What is the prototype for `dup()`? What about `dup2()`? What do both return? (5 mins) 90 | 2. What are some differences between `dup` and `dup2` with regards to the new file descriptor? (5 mins) 91 | - Here's a diagram to help you visualize the functions better:\ 92 | ![dup](https://i.imgur.com/iJ7X39I.png) 93 | 3. What do the new and old file descriptor share? (5 mins) 94 | 4. Now let's write a program that: (15 mins) 95 | - opens a `test.txt` file with the following flags: `O_CREAT | O_TRUNC | O_RDWR, 0644` 96 | - 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. 97 | - Here's a handy [permissions calculator](http://permissions-calculator.org/). 98 | - saves the `open` return in an int `fd`; 99 | - creates another int, for example `dup_fd`; 100 | - calls `dup()`, giving it `fd` as argument and saving its return in `dup_fd`; 101 | - passes `fd` as the 1st argument to `write()`, with the string `"This will be written to the test file\n"`. 102 | - passes `dup_fd` as the 1st argument to `write()`, with the string `"This will also be written to the test file\n"`.\ 103 | ![example output](https://i.imgur.com/ikWDFXL.png?1) 104 | 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) 105 | - Now, using `dup2`, turn the fd `1` (that is, stdout) into a copy of our test.txt `fd`. 106 | - Call `write()` again, outputting `"This isn't being printed on stdout\n"` onto stdout (1). 107 | - Does anything get printed onto your terminal when you run the program? 108 | 109 | ## Bonus 110 | ### pipe 111 | `pipe()` allows data to be passed from one process to another.\ 112 | 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.\ 113 | ![one-way pipe](https://www.tutorialspoint.com/inter_process_communication/images/pipe_with_one.jpg) 114 | 1. What is the prototype of `pipe()`? What is being stored in the int array you're passing it? (10 mins) 115 | 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)\ 116 | ![pipe in out](https://i.imgur.com/DzjZwSl.png) 117 | 3. Let's try using `pipe` in combination with `fork` and `wait`! Write a program that: (15 mins) 118 | - takes command-line arguments (i.e. `int argc, char **argv`); 119 | - creates a **pipe**; 120 | - then **fork** to create a child process; 121 | - the parent process: 122 | - should **close** the pipe end that it doesn't need; 123 | - **writes** argv[1] to the correct end of the pipe; 124 | - **closes** the remaining pipe end; 125 | - **waits** for its child processes to terminate and checks their status. 126 | - the child process: 127 | - should **close** the pipe end that it doesn't need; 128 | - in a loop, **reads** the string from the pipe, one byte at a time; 129 | - calls `toupper()` (include `ctype.h`) on the read char; 130 | - **writes** the converted char to stdout; 131 | - writes a newline to stdout; 132 | - **closes** the remaining pipe end.\ 133 | ![pipe example](https://i.imgur.com/40cUyDo.png) 134 | 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? 135 | ![closing ends](https://chensunmac.gitbooks.io/csc209-practical-programming/content/assets/pipe2.png) 136 | 137 | That was a simple exercise to show you how data can be passed through pipes and interacted with within child processes. 138 | 139 | Things get even more mind-blowing when you throw `dup`/`dup2` into the mix. 140 | 141 | ## Tips 142 | Here's a more detailed explanation about data flows through pipes, with handy diagrams: [pipes, forks, & dups](http://www.rozmichelle.com/pipes-forks-dups/) 143 | 144 | And here's a couple other things that could be helpful to look into for your project: 145 | - abstract syntax tree 146 | - finite state machines 147 | -------------------------------------------------------------------------------- /spark-sessions/pipex/SparkSession - pipex - mod.md: -------------------------------------------------------------------------------- 1 | # Spark Session: pipex 2 | *updated: 12/08/2021* 3 | 4 | Project description: 5 | > Create a simple program that handles redirections and pipes. An introduction to bigger UNIX projects that are to come. 6 | 7 | ## Topics 8 | 1. Processes 9 | 2. fork 10 | 3. wait 11 | 4. execve 12 | 5. dup & dup2 13 | 6. pipe 14 | 15 | ### Processes 16 | Before we get into how to work with processes, it's handy to understand what we actually mean by "process". 17 | 1. What is a process? (5 mins) 18 | > a process is a program in execution, an entity which implements a set of instructions (given by the program) in the system. 19 | > processes are identified by their unique process IDs (which is recycled after a process terminates). 20 | > the two main system calls to create a process are fork and exec. 21 | 22 | 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. 23 | Here's a diagram showing how this memory is divided:\ 24 | ![process memory](https://i.imgur.com/nxmmQl3.png) 25 | 26 | To put it in really simple terms, you can think of a process like a struct — a collection of information bound to an entity. 27 | 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. 28 | 29 | ### fork 30 | `fork()` creates a new process — called the **child process** — by duplicating the calling process (**the parent process**). 31 | 1. What is the prototype of `fork()`? What does the function return? (10 mins) 32 | > `pid_t fork(void);` 33 | > 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. 34 | 35 | - How could you use the function return to identify if you are in the child or parent process? 36 | > By checking if the PID is 0, you can know if you're in the child process. 37 | 38 | - Is `fork`'s return the same as the child's PID? 39 | > Yes in the parent. Not in the child; child's actual PID can be seen by running `getpid`. 40 | 2. Which of the following is copied from the parent process to the child process? Which are not? (10 mins) 41 | - Data (the content of the process' memory space) 42 | - Location in memory 43 | - Process ID 44 | - Open file descriptors 45 | > Data: same data in their respective memory spaces (at time of fork) 46 | > Location in memory: not same - separate memory spaces 47 | > Process ID: not same - child has unique PID that doesn't match any of existing process group or session 48 | > 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. 49 | 3. Let's see some of these characteristics in action. (20 mins) 50 | - Write a program that: 51 | - initialises an int `x` to **5**; 52 | - calls `fork()` and then prints its return value in a statement `"fork returned: %d\n"`; 53 | - checks for failed forks; 54 | - if in the **child** process: **decrements** `x` by 1, prints `"This line is from child, x is %d\n"`, and then **returns** 0; 55 | - else if in the **parent** process: **increments** `x` by 1 and then prints `"This line is from parent, x is %d\n"`. 56 | - 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. 57 | ![example output](https://i.imgur.com/jOTXMCL.png)\ 58 | *example output - your output order may vary* 59 | - 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. 60 | - 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. 61 | 62 | 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. 63 | 64 | ***Break (5 mins)*** 65 | 66 | ### wait 67 | 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. 68 | 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) 69 | > `pid_t wait(int *wstatus);` 70 | > if not NULL, `wstatus` contains the child process' status information, which can be checked with macros like `WIFSIGNALED` and `WIFEXITED`. 71 | 2. Calling `wait()` (or `waitpid()`) in the parent process prevents what's called **"zombie processes"**. What does this mean? (10 mins) 72 | > 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. 73 | > 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. 74 | 3. Let's add `wait()` to the code you wrote earlier. (10 mins) 75 | - Create an int variable, for example `w_status`, to be passed to `wait()`. 76 | - In the **parent process** code block, call `wait()` before anything else. *Remember to pass it the address of your `w_status` int.* 77 | - Make sure the **child process** is calling `return` when it's done. 78 | - 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. 79 | > if (WIFEXITED(w_status)) { 80 | > printf("Child process exited with status: %d\n", WEXITSTATUS(w_status));} 81 | - Try tweaking the argument you pass to the `return()` call in your child process. Does the output change accordingly\ 82 | ![example output](https://i.imgur.com/cFZAZFp.png)\ 83 | *example output* 84 | 85 | Here's a fun short explanation about zombie processes for later: [understanding zombie processes](https://youtu.be/xJ8KenZw2ag) 86 | 87 | ### execve 88 | The `exec()` family of functions allows us to **replace** the current process with a new program. 89 | No new process is created; the PID remains the same. The functions simply have the existing process execute a new program. 90 | 1. What is the prototype of `execve()`? (10 mins) 91 | - Break down each of function parameters. What does each mean? 92 | > `int execve(const char *pathname, char *const argv[], char *const envp[]);` 93 | > pathname: binary executable, for example "/bin/ls" 94 | > argv: an array of pointers to strings to be passed to the program as its arguments. 95 | > envp: the environment variables you want to pass to the new program, which can differ from the env variables passed to the calling process 96 | > 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. 97 | 2. What does `execve()` return? (5 mins) 98 | > On success, it doesn't return. Only returns on failure, giving -1 and setting erno appropriately. 99 | 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) 100 | - **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. 101 | - At the beginning of your main, declare a `char *argv[3]`. 102 | - Initialize `argv[0]` to `"/bin/ls"`. 103 | - Initialize `argv[1]` to `"-l"`. 104 | - Initialize `argv[2]` to `NULL`. 105 | - *Do you know why these arguments are made in this order?* 106 | > By convention, argv[0] should contain the filename associated with the file being executed. The argv array must be terminated by a NULL pointer. 107 | 108 | - 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. 109 | - Below the `execv` call, place another print statement, `"This line is from child after execv"`. 110 | - Does your program execute `ls` with the `-l` list option when you run it? Does the "after execv" statement print? 111 | > 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. 112 | 113 | ***Break (5 mins)*** 114 | 115 | ### dup & dup2 116 | `dup()` and `dup2()` create a **copy** of a file descriptor. 117 | 1. What is the prototype for `dup()`? What about `dup2()`? What do both return? (5 mins) 118 | > `int dup(int oldfd);` 119 | > `int dup2(int oldfd, int newfd);` 120 | > on success, returns newfd. on failure, -1 121 | 2. What are some differences between `dup` and `dup2` with regards to the new file descriptor? (5 mins) 122 | - Here's a diagram to help you visualise the functions better:\ 123 | ![dup](https://i.imgur.com/iJ7X39I.png) 124 | > `dup2` uses specified newfd, `dup` uses lowest-numbered unused fd. 125 | > `dup2`: If newfd was previously open, it is silently closed before being reused. 126 | 3. What do the new and old file descriptor share? (5 mins) 127 | > 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. 128 | 4. Now let's write a program that: (15 mins) 129 | - opens a `test.txt` file with the following flags: `O_CREAT | O_TRUNC | O_RDWR, 0644` 130 | - 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. 131 | - Here's a handy [permissions calculator](http://permissions-calculator.org/). 132 | - saves the `open` return in an int `fd`; 133 | - creates another int, for example `dup_fd`; 134 | - calls `dup()`, giving it `fd` as argument and saving its return in `dup_fd`; 135 | - passes `fd` as the 1st argument to `write()`, with the string `"This will be written to the test file\n"`. 136 | - passes `dup_fd` as the 1st argument to `write()`, with the string `"This will also be written to the test file\n"`.\ 137 | ![example output](https://i.imgur.com/ikWDFXL.png?1) 138 | > `write(fd, "This will be written to the test file\n", 38);` 139 | > `write(dup_fd, "This will also be written to the test file\n", 43);` 140 | 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) 141 | - Now, using `dup2`, turn the fd `1` (that is, stdout) into a copy of our test.txt `fd`. 142 | - Call `write()` again, outputting `"This isn't being printed on stdout\n"` onto stdout (1). 143 | - Does anything get printed onto your terminal when you run the program? 144 | > `dup2(fd, 1);` 145 | > `write(1, "This isn't being printed on stdout\n", 35);` 146 | > string should be printed in test.txt instead of terminal 147 | 148 | ## Bonus 149 | ### pipe 150 | `pipe()` allows data to be passed from one process to another. 151 | This "pipeline" between processes is **unidirectional**, meaning data flows in one direction. 152 | Therefore, you have one end of the pipe that reads data and one end of the pipe that writes data.\ 153 | ![one-way pipe](https://www.tutorialspoint.com/inter_process_communication/images/pipe_with_one.jpg) 154 | 1. What is the prototype of `pipe()`? What is being stored in the int array you're passing it? (10 mins) 155 | > `int pipe(int pipefd[2]);` 156 | > pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. 157 | 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)\ 158 | ![pipe in out](https://i.imgur.com/DzjZwSl.png) 159 | > 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. 160 | > therefore, the 1st process would write to `pipefd[1]` and the 2nd process would read from `pipefd[0]`. 161 | 3. Let's try using `pipe` in combination with `fork` and `wait`! Write a program that: (15 mins) 162 | - takes command-line arguments (i.e. `int argc, char **argv`); 163 | - creates a **pipe**; 164 | - then **forks** to create a child process; 165 | - the parent process: 166 | - should **close** the pipe end that it doesn't need; 167 | - **writes** argv[1] to the correct end of the pipe; 168 | - **closes** the remaining pipe end; 169 | - **waits** for its child processes to terminate and checks their status. 170 | - the child process: 171 | - should **close** the pipe end that it doesn't need; 172 | - in a loop, **reads** the string from the pipe, one byte at a time; 173 | - calls `toupper()` (include `ctype.h`) on the read char; 174 | - **writes** the converted char to stdout; 175 | - writes a newline to stdout; 176 | - **closes** the remaining pipe end.\ 177 | ![pipe example](https://i.imgur.com/40cUyDo.png) 178 | 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? 179 | ![closing ends](https://chensunmac.gitbooks.io/csc209-practical-programming/content/assets/pipe2.png) 180 | > 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. 181 | > If all fds referring to the read end of a pipe are closed, attempting `write` on the other end generates a SIGPIPE signal. 182 | > Unnecessary duplicate fds should thus be closed, to ensure that EOF and SIGPIPE are delivered when appropriate, preventing a hanging state. 183 | 184 | That was a simple exercise to show you how data can be passed through pipes and interacted with within child processes. 185 | 186 | Things get even more mind-blowing when you throw `dup`/`dup2` into the mix. 187 | 188 | ## Tips 189 | Here's a more detailed explanation about data flows through pipes, with handy diagrams: [pipes, forks, & dups](http://www.rozmichelle.com/pipes-forks-dups/) 190 | 191 | And here's a couple other things that could be helpful to look into for your project: 192 | - abstract syntax tree 193 | - finite state machines 194 | 195 | 196 | ``` 197 | // example solution for pipe exercise 198 | #include 199 | #include 200 | #include 201 | #include 202 | #include 203 | 204 | int main(int ac, char **av) { 205 | int pipefd[2]; 206 | char buf; 207 | 208 | if (ac != 2){ 209 | printf("incorrect number of args\n"); 210 | return (1); 211 | } 212 | if (pipe(pipefd) < 0) { 213 | printf("pipe failed\n"); 214 | return (1); 215 | } 216 | pid_t pid = fork(); 217 | if (pid < 0) 218 | printf("fork failed\n"); 219 | else if (pid == 0) { 220 | close(pipefd[1]); 221 | while (read(pipefd[0], &buf, 1) > 0) { 222 | char up = toupper(buf); 223 | write(STDOUT_FILENO, &up, 1); 224 | } 225 | write(STDOUT_FILENO, "\n", 1); 226 | close(pipefd[0]); 227 | return(0); 228 | } 229 | else { 230 | int w_status = 0; 231 | close(pipefd[0]); 232 | write(pipefd[1], av[1], strlen(av[1])); 233 | close(pipefd[1]); 234 | wait(&w_status); 235 | if (WIFEXITED(w_status)) 236 | printf("Child process exited with status: %d\n", WEXITSTATUS(w_status)); 237 | } 238 | return (0); 239 | } 240 | ``` 241 | -------------------------------------------------------------------------------- /spark-sessions/pipex/SparkSession - pipex.md: -------------------------------------------------------------------------------- 1 | # Spark Session: pipex 2 | *updated: 12/08/2021* 3 | 4 | Project description: 5 | > Create a simple program that handles redirections and pipes. An introduction to the bigger UNIX projects that are to come. 6 | ## Topics 7 | 1. Processes 8 | 2. fork 9 | 3. wait 10 | 4. execve 11 | 5. dup & dup2 12 | 6. pipe 13 | 14 | ### Processes 15 | Before we get into how to work with processes, it's handy to understand what we actually mean by "process". 16 | 1. What is a process? (5 mins) 17 | 18 | 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.\ 19 | Here's a diagram showing how this memory is divided:\ 20 | ![process memory](https://i.imgur.com/nxmmQl3.png) 21 | 22 | To put it in really simple terms, you can think of a process like a struct — a collection of information bound to an entity. 23 | 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. 24 | 25 | ### fork 26 | `fork()` creates a new process — called the **child process** — by duplicating the calling process (**the parent process**). 27 | 1. What is the prototype of `fork()`? What does the function return? (10 mins) 28 | - How could you use the function return to identify if you are in the child or parent process? 29 | - Is `fork`'s return the same as the child's PID? 30 | 2. Which of the following is copied from the parent process to the child process? Which are not? (10 mins) 31 | - Data (the content of the process' memory space) 32 | - Location in memory 33 | - Process ID 34 | - Open file descriptors 35 | 3. Let's see some of these characteristics in action. (20 mins) 36 | - Write a program that: 37 | - initialises an int `x` to **5**; 38 | - calls `fork()` and then prints its return value in a statement `"fork returned: %d\n"`; 39 | - checks for failed forks; 40 | - if in the **child** process: **decrements** `x` by 1, prints `"This line is from child, x is %d\n"`, and then **returns** 0; 41 | - else if in the **parent** process: **increments** `x` by 1 and then prints `"This line is from parent, x is %d\n"`. 42 | - 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. 43 | ![example output](https://i.imgur.com/jOTXMCL.png)\ 44 | *example output - your output order may vary* 45 | - 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. 46 | - 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. 47 | 48 | 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. 49 | 50 | ***Break (5 mins)*** 51 | 52 | ### wait 53 | 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. 54 | 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) 55 | 2. Calling `wait()` (or `waitpid()`) in the parent process prevents what's called **"zombie processes"**. What does this mean? (10 mins) 56 | 3. Let's add `wait()` to the code you wrote earlier. (10 mins) 57 | - Create an int variable, for example `w_status`, to be passed to `wait()`. 58 | - In the **parent process** code block, call `wait()` before anything else. *Remember to pass it the address of your `w_status` int.* 59 | - Make sure the **child process** is calling `return` when it's done. 60 | - 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. 61 | - Try tweaking the argument you pass to the `return()` call in your child process. Does the output change accordingly?\ 62 | ![example output](https://i.imgur.com/cFZAZFp.png)\ 63 | *example output* 64 | 65 | Here's a fun short explanation about zombie processes for later: [understanding zombie processes](https://youtu.be/xJ8KenZw2ag) 66 | 67 | ### execve 68 | The `exec()` family of functions allows us to **replace** the current process with a new program.\ 69 | No new process is created; the PID remains the same. The functions simply have the existing process execute a new program. 70 | 1. What is the prototype of `execve()`? (10 mins) 71 | - Break down each of function parameters. What does each mean? 72 | 2. What does `execve()` return? (5 mins) 73 | 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) 74 | - **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. 75 | - At the beginning of your main, declare a `char *argv[3]`. 76 | - Initialize `argv[0]` to `"/bin/ls"`. 77 | - Initialize `argv[1]` to `"-l"`. 78 | - Initialize `argv[2]` to `NULL`. 79 | - *Do you know why these arguments are made in this order?* 80 | - 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. 81 | - Below the `execv` call, place another print statement, `"This line is from child after execv"`. 82 | - Does your program execute `ls` with the `-l` list option when you run it? Does the "after execv" statement print? 83 | 84 | ***Break (5 mins)*** 85 | 86 | ### dup & dup2 87 | `dup()` and `dup2()` create a **copy** of a file descriptor. 88 | 1. What is the prototype for `dup()`? What about `dup2()`? What do both return? (5 mins) 89 | 2. What are some differences between `dup` and `dup2` with regards to the new file descriptor? (5 mins) 90 | - Here's a diagram to help you visualise the functions better:\ 91 | ![dup](https://i.imgur.com/iJ7X39I.png) 92 | 3. What do the new and old file descriptor share? (5 mins) 93 | 4. Now let's write a program that: (15 mins) 94 | - opens a `test.txt` file with the following flags: `O_CREAT | O_TRUNC | O_RDWR, 0644` 95 | - 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. 96 | - Here's a handy [permissions calculator](http://permissions-calculator.org/). 97 | - saves the `open` return in an int `fd`; 98 | - creates another int, for example `dup_fd`; 99 | - calls `dup()`, giving it `fd` as argument and saving its return in `dup_fd`; 100 | - passes `fd` as the 1st argument to `write()`, with the string `"This will be written to the test file\n"`. 101 | - passes `dup_fd` as the 1st argument to `write()`, with the string `"This will also be written to the test file\n"`.\ 102 | ![example output](https://i.imgur.com/ikWDFXL.png?1) 103 | 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) 104 | - Now, using `dup2`, turn the fd `1` (that is, stdout) into a copy of our test.txt `fd`. 105 | - Call `write()` again, outputting `"This isn't being printed on stdout\n"` onto stdout (1). 106 | - Does anything get printed onto your terminal when you run the program? 107 | 108 | ## Bonus 109 | ### pipe 110 | `pipe()` allows data to be passed from one process to another.\ 111 | This "pipeline" between processes is **unidirectional**, meaning data flows in one direction. 112 | Therefore, you have one end of the pipe that reads data and one end of the pipe that writes data. 113 | ![one-way pipe](https://www.tutorialspoint.com/inter_process_communication/images/pipe_with_one.jpg) 114 | 1. What is the prototype of `pipe()`? What is being stored in the int array you're passing it? (10 mins) 115 | 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)\ 116 | ![pipe in out](https://i.imgur.com/DzjZwSl.png) 117 | 3. Let's try using `pipe` in combination with `fork` and `wait`! Write a program that: (15 mins) 118 | - takes command-line arguments (i.e. `int argc, char **argv`); 119 | - creates a **pipe**; 120 | - then **fork**s to create a child process; 121 | - the parent process: 122 | - should **close** the pipe end that it doesn't need; 123 | - **writes** argv[1] to the correct end of the pipe; 124 | - **closes** the remaining pipe end; 125 | - **waits** for its child processes to terminate and checks their status. 126 | - the child process: 127 | - should **close** the pipe end that it doesn't need; 128 | - in a loop, **reads** the string from the pipe, one byte at a time; 129 | - calls `toupper()` (include `ctype.h`) on the read char; 130 | - **writes** the converted char to stdout; 131 | - writes a newline to stdout; 132 | - **closes** the remaining pipe end. 133 | ![pipe example](https://i.imgur.com/40cUyDo.png) 134 | 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? 135 | ![closing ends](https://chensunmac.gitbooks.io/csc209-practical-programming/content/assets/pipe2.png) 136 | 137 | That was a simple exercise to show you how data can be passed through pipes and interacted with within child processes. 138 | 139 | Things get even more mind-blowing when you throw `dup`/`dup2` into the mix. 140 | 141 | ## Tips 142 | Here's a more detailed explanation about data flows through pipes, with handy diagrams: [pipes, forks, & dups](http://www.rozmichelle.com/pipes-forks-dups/) 143 | 144 | And here's a couple other things that could be helpful to look into for your project: 145 | - abstract syntax tree 146 | - finite state machines 147 | --------------------------------------------------------------------------------