Jshell Notes (2024 version)

Written by Maria Hernandez Rivero

General Instructions

You are allowed (and encouraged) to use the Libfdr libraries.

About argvs:

About Pipes:

Process 1 Process 2 Process 3 Process 4 Process 5 Process 6
write to Pipe 1 -> <- read from Pipe 1, write to Pipe 2 -> <- read from Pipe 2, write to Pipe 3 -> <- read from Pipe 3, write to Pipe 4 -> <- read from Pipe 4, write to Pipe 5-> <- read from Pipe 5

Example of Piping Different Commands:

cat 
< input.txt
head -n 10
sed s/o/oo/g
sort 
> output.txt
END

Assume that input.txt contains the following:

Hello World
Zoo Keeper
Wonderful Day
Football is fun
Solo Traveler
Doors open
Moon and stars
Open door policy
Zoology is cool
Looming shadows
Example line eleven
Example line twelve

After piping the commands above (cat < input.txt | head -n 10 | sed s/o/oo/g | sort | > output.txt), output.txt will contain the following content:

Doooors oopen
Fooootball is fun
Helloo Woorld
Looooming shadoows
Moooon and stars
Open doooor poolicy
Sooloo Traveler
Woonderful Day
Zoooo Keeper
Zooooloogy is cooool

Observations:

argvs or doubly linked list

Structure

typedef struct {
    char *stdin;          /* Filename from which to redirect stdin.  NULL if empty. */
    char *stdout;         /* Filename to which to redirect stdout.  NULL if empty. */
    int append_stdout;    /* Boolean for appending. */
    int wait;             /* Boolean for whether I should wait. */
    int n_commands;       /* The number of commands that I have to execute */
    int *argcs;           /* argcs[i] is argc for the i-th command */
    char ***argvs;        /* argcv[i] is the argv array for the i-th command */
    Dllist comlist;       /* I use this to incrementally read the commands. */
} Command;

A. Main method

  1. Allocate memory for a command instance.
  2. Initialize the fields of the command instance. The field append_stdout should be set to 0 by default and the field wait to 1 by default.
  3. Loop while there is a line to read:

B. Function where you fork children and wait for the children to exit:

NOTE: The way I implemented piping was on-demand. Namely, pipes are only created when needed, right before forking, minimizing the number of open file descriptors at any time. This approach reduces the need to track and close multiple pipe descriptors across different child processes, potentially simplifying code and reducing resource usage.

If you create all the pipes upfront (before the for loop that loops through each set of command), you need to make sure that you close all the pipes not needed in that specific child to avoid resource leakage and prevent deadlocks.

For example, assume that you have 6 processes, you need 5 pipes to perform interprocess communication between these processes. If you create the pipes upfront, then the workflow would look like this:

Now, I will describe the process of opening pipes on-demand:

Body of the function:

  1. Declare:
  2. Loop through all your commands:

Outside the for loop:

NOTE: How does wait work?

wait() is a blocking function. Namely, it will hang the calling process (parent) until ANY of the children exits. Once this happens, the wait function will return the pid of the child that exited. This is the pid that you will look up in your tree and delete if it exists there.

Final Notes: