Homework 3 Q&AThese Q&As are from last year and address important implementation details for the homework.
Q: Anyone know how to copy and paste from a virtual machine to the host machine?
A: Install Guestadditions on the VM, in Linux. It takes 2-5 min. Check out https://www.virtualbox.org/
manual/ch04.html (if the site is up…).
Otherwise, you can always start Firefox on Linux and upload the files from there. Or, access google
drive or some other sharing service from Firefox on the VM.
Q: it is not clear what programs must be executed in what processes and how the pipe is used.
A: The ps program does not read, it only writes to stdout. sort reads from stdin, sorts the input lines,
then displays them to stdout.
The child doing ps must dup2 the writing end of the pipe while the child doing sort must dup2 the
reading pipe fd.
The order of parameters for dup2() is very important.
The parent exits only after both child procs exit, so it must do 2 wait() or waitpid() calls.
Q: I’m having issues with the wait(NULL); commands. Everything I’m reading is telling me that
the execlp command REPLACES the process that calls it and does not return a value, hence that you
cannot wait on the process to return, because it never does.
This is from the MAN page: The exec() family of functions replaces the current process image with a
new process image.
When I remove the wait(NULL) commands, it works, but when I add them in, it never returns. Any
insight you could give me would be helpful.
A:
exec() replaces the process that calls it with another program. That program (ps or sort) will eventually
call exit(code) and that code would be passed to the parent in a status parameter, if it supplied one, as in
wait(&status). (or waitpid(pid, &status, 0)).
The process that blocks is the parent, while doing wait(). To avoid blocking the parent, make sure you
close all the pipe file descriptors that are not used (pipes[0] or pipes[1]) in the parent and in the child
processes.
Q: Follow-up question: That is exactly what the issue was. As soon as I closed the pipes in the parent,
it worked properly.
A: The reason the parent remains blocked in wait() if it does not call close() on the writing end of the
pipe is that the child process reading from its stdin (dup2ed from the reading end of the pipe) keeps
reading (doing read(). Unless all processes involved close() the writing end of the pipe, the process
reading from it (running sort) does not get EOF. In other words, if you have 3 processes with the
writing end of a pipe open, all three need to call close() on it.
Q: Does anyone understand how to write the result of computePartialSum to the pipe when that
function returns a double and the write function only accepts 2d char arrays as an argument?
A: Treat the double (8 bytes) as a char pointer or char[8].
Use double, not float. No need to convert to/from strings. You can write a binary double to a file
descriptor like this: write(fd, &somevar, sizeof(somevar)). You can read into a double similarly, with
read().
(
L
i If the parent calculates (1/y*x) from 0 to t , and the child adds (1/y*x) + (1/y*x) for each result.
Q:
n is the parent sending N,T to the child?
why
k The child uses N and T to determine the lower and upper bounds for its partial sum.
A:
s
After fork() the child has the same values for local or global variables as the parent. However, I want
t
students
to write the parent code that sends N,T to each child process for practice.
o
a
Q:
n More information on pipes ?
e
A: http://beej.us/guide/bgipc/output/html/multipage/pipes.html
x
t
Q:
e The parent process blocks in wait(). What is the problem?
A:
r To avoid blocking the parent, make sure that after calling dup2() you close all the pipe file
n
descriptors
that are not used (pipes[0] and pipes[1]) in the parent and in the child processes.
a
l
Q: What are the expectations on code comments from the instructor?
A:
s Write non-trivial comments. Explain what each function does, its parameters, and the return value
(its contract).
i
t
Q:
e What debugger do you recommend ?
.
)
A: For the programming assignments is is very useful to learn to use a debugger under Linux: gdb from
emacs is nice, while ddd is much more user-friendly. Just from experience, each additional tool you get
involved with for an assignment will take you more time, so appropriate time management is due.
Before I use a debugger I always add printf() statements with tracing messages through the tricky parts
of my programs. This usually leads to diagnosing the problem.
Q: Why can’t use use “|” in the C program to create a pipe ?
A: One uses pipes from the shell by linking the output from one program ( ps ) to the input of another
(e.g. sort) and relies on the “|” operator, and also on input/output redirection: . The shell program
hides the technical details (and system calls) from the user and converts pipes and file redirection to the
appropriate C library calls.
You can’t do that as elegantly from a C/C++ program. Instead you have to “manually” set up the pipe
file descriptors with pipe() and dup/dup2(), followed by fork and exec calls. (Perl and Python make this
a lot easier.)
COP 4610 Homework 3
Programming Assignment from Chapter 3, Processes
Instructor: Dr. Ionut Cardei
For this assignment you will work with the Linux OS you installed on the VirtualBox VM. Read
Chapter 3 and the programming links listed on the last page.
Since this is a programming assignment attention to detail is critical. Solutions that do not follow
100% all requirements stated in this document will get penalized.
If in doubt, post a question on the homework’s Q&A forum or send the instructor a Canvas message.
As stated in the syllabus, the FAU policy for Academic Integrity is in full effect. Do not submit code
that is not your own work. Do not submit code shared with anyone else. Do not submit code obtained
with the help of Chat GPT, Gemini, or other LLMs. Code written by LLMs tends to be very similar for
all users and it is easy to identify its source.
Write all deliverables to a file homework3_lastname_firstname.doc (or .docx) and then convert to PDF
when finished. Use your own name above.
To get credit, write your name at the top of the file, write the solutions in the problem order (1, 2,…),
and write a heading for each solution (e.g. “Problem 2” before the answer to problem 2).
Submit on Canvas the PDF file and source files.
Remember: you need to make your answers easily readable to get proper credit. Don’t expect a high
grade if your answers are unreadable, nebulous, or obfuscated. It helps to use syntax highlighting, e.g.
paste colorized source code from http://hilite.me .
Preparation:
Before you can start, make sure you install the development tools on Ubuntu Linux, including the g++
GNU C++ compiler. Enter these commands on the shell prompt:
sudo apt update
sudo apt install build-essential
Press “y” to permit installation, as needed.
Problems
1. Pipes (40 pts.)
Write a C program for Linux called pipes.cc (or pipes.c++) that does the following:
In the main() function, it creates a pipe using the pipe() function, then creates two child processes with
fork(). Child 1 redirects stdout to the write end of the pipe and then executes with execlp() the “ps -aux”
command. Child 2 redirects its input from stdin to the read end of the pipe, then it executes the “sort -r n -k 5” command.
Write in a comment at the top of the program what this pipe command actually does.
After creating both children, the parent process waits for them to terminate before it can exit.
Note that you may have to create Child 2 first, followed by Child 1.
The parent program does the same thing as a shell that runs the command “ps -aux | sort -r -n -k 5”.
You must use the fork(), pipe(), dup2(), close(), execlp() functions.
To get help, read carefully the attached Homework 3 Q&A file and follow the Homework 3 Q&A
discussion forum.
Deliverables for problem 1:
• Source code for problem 1 pasted neatly, with proper code formatting and coding style.
• Screenshot of running the pipes program with 20 – 30 lines of output. Not more.
2. Multistage Pipeline (60 pts.)
For this problem we extend the notion of a pipeline to an arbitrary number of computation stages. We
will replicate how a shell (e.g. bash) implements parsing and executing a command line involving a
pipe command.
For example, when you run shell command line “cat /etc/passwd |sort -r |head -n 1” the shell will fork
and execute in parallel programs cat, sort, and head with their given command line options, but, before
that, the shell will use the pipe()/dup() library functions to connect stdout for cat to stdin of sort and
stdout of sort to stdin of head. head’s stdout is unchanged, going straight (and as by default) to the
terminal, where users can read the result.
This problem has two parts:
PART a) : 20 points
Write a C++ program called ops in a source file called ops.cc or ops.c++ that executes simple
arithmetic and filtering operations on floating point numbers read from the terminal (standard input file
using std::cin) and that displays the results to the terminal (stdout file, using std::cout, for example).
The ops program receives from the command line options in main()’s argv[] array argument two
parameters as strings: an operator (arithmetic: +, -, x, / or relational: ==, !=, le, ge, lt, gt) we called @
and a double value we call p. The operators have these meanings:
Operator Mnemonic
Type
Meaning
+
arithmetic
addition
–
arithmetic
subtraction
x
arithmetic
multiplication
/
arithmetic
division
%
arithmetic
modulo
==
relational
equal
!=
relational
not equal
le
relational
less than or equal
lt
relational
less than
ge
relational
greater than or equal
gt
relational
greater than
The shell will store the executable file name in argv[0]. If there are fewer than 3 command line
arguments, ops exits with code 1.
The ops program executes the following loop:
Read a text line from stdin in a loop while stdin has not reached end-of-file and convert the line to a
double number we call q.
Inside the loop: if the operator from its command line is arithmetic, ops performs the corresponding
operation on q using the parameter p and prints to stdout the result of q @ p, followed by newline.
If the @ operator is relational the ops program will evaluate expression q @ p and it will print number
q to stdout only if q @ p is true. Otherwise, it won’t print anything and it will continue the loop.
When EOF is reached, the ops program terminates with code 0.
Examples when running $ from the shell and ops is used in a pipe command. The shell prompt is “$”.
$ echo 20 | ./ops + 10
30
$ echo 20 | ./ops x 0.5
10
$ echo 20 | ./ops x 0.5 | ./ops + 1
11
$ echo -10 | ./ops x -0.5 | ./ops + 2
7
Now, edit a file called numbers.txt with the following content:
4
9
-8
10
Print file content with cat:
$ cat numbers.txt
4
9
-8
10
Do some operations on each number from the file:
$ cat numbers.txt | ./ops + 10 | ./ops x 2
28
38
4
40
Apply a filter to the numbers – print all results less than (“le”) 30:
$ cat numbers.txt | ./ops + 10 | ./ops x 2 | ./ops le 30
28
4
Process only positive numbers from the file:
$ cat numbers.txt |./ops gt 0 | ./ops / 2
2
4.5
5
Deliverables for part a):
• Source file ops.cc. Paste the code, looking nice and readable. May use syntax highlighting to help
the grader.
• Add screenshot(s) running 6 pipe shell command in the Ubuntu Linux shell using your own file of
numbers, that you should call after your firstname.txt. Type exactly 4 random numbers between -20
and 20 in that file. The pipe commands are as follows:
◦ 1 pipe shell commands with 2 stages that don’t use relational operators. (The cat command is
included in the count.)
◦ 2 pipe shell commands with 4 stages that don’t use relational operators.
◦ 1 pipe shell command with 2 stages that use relational operators. (Including cat.)
◦ 2 pipe shell command with 4 stages that use arithmetic and relational operators.
Write a note BEFORE each screenshot explaining what the shell command does.
PART b) : 40 points
Write a C++ program called runpipe that:
I. prints a prompt and then reads a string with a shell pipe command (like those in part a)) from the
terminal, i.e. from standard input (NOT as a command line argument in argv)
II. parses the command line into programs to run (or individual commands + options) as pipeline
stages, separated in the string by the pipe character ‘|’
III. prepares the file pipe descriptors for the pipeline stages, using library functions pipe, dup2, close,
etc.
IV. forks and execs the stage commands with their options
V. waits for all child processes to exit (with waitpid) before it exits with code 0.
• if a child exits prematurely with non-zero code, then runpipe also exits with that same non-zero
code.
Here is how to run runpipe from the shell:
$ ./runpipe
Enter pipe command:
cat numbers.txt | ./ops + 10 | ./ops x 2 | ./ops le 30
28
4
Highlighted in green is the input typed by the user. “Enter pipe command: “ is the input prompt.
Highlighted in orange is the output from the pipe command. (the highlighting is just for explanatory
reason, don’t try to actually change background color in the terminal….)
For this example, runpipe parses the pipe command string “cat numbers.txt | ./ops + 10 | ./ops x 2 | ./ops
le 30” into 4 separate shell commands:
1) cat numbers.txt
2) ./ops + 10
3) ./ops x 2
4) ./ops le 30
It then sets up the pipes needed to connect the 4 processes, and then fork/execs the four programs, with
their command line options. Lastly, runpipe waits for all processes to exit before it exits.
For testing, you should also run the runpipe program with some typical Linux commands. For instance,
the following example shows running a pipe that reads the ops.cc file, filters all lines that start with
#include, and then counts the resulting lines – telling us that my ops.cc file includes 7 header files.
$ ./runpipe
Enter pipe command:
cat ops.cc | grep “^\#include” |wc -l
7
The runpipe program should have the exact same behavior and output (after displaying the prompt) as
running the shell pipe command from the shell prompt.
Deliverables for part b):
• Source file runpipe.cc.
• One screenshot with the Ubuntu Linux shell showing 3 commands with runpipe executing 3 pipe
shell commands of length 4 with cat and the ops program you wrote for part a). Pick your own pipe
examples.
• One screenshot with the Ubuntu Linux shell showing 3 examples with runpipe executing 3 useful
pipe shell commands of length minimum 3 stages with typical Linux shell commands, such as wc,
sort, ps, ls, cat, grep. A command is useful if it prints something nontrivial to the screen.
Write a note BEFORE each screenshot explaining what the pipeline shell comma
Important:
1. The programs must work as required to get full credit.
2. Pasting code found on the web or obtained from ChatGPT & similar is considered cheating.
3. Copying code from a colleague is considered cheating. Submit your own work.
4. Submissions missing the screenshots are not graded.
5. Not following strictly the requirements in this file will cut points.
6. Remember to upload the pdf file and the two .cc source files.
You can edit your program with the gedit application (from a GUI), available from the command line.
Or install emacs using command “sudo apt-get install emacs” and learn to use it. (Command sudo runs
programs as user root.)
Unix programming resources:
Start with: https://www.guru99.com/linux-pipe-grep.html,
https://www.computernetworkingnotes.com/linux-tutorials/pipes-in-linux-explained.html ,
https://www.howtogeek.com/438882/how-to-use-pipes-on-linux/
Continue with
http://www.tldp.org/LDP/lpg/node11.html
google “unix programming pipe dup2 redirection” to get more help
Debugging
If necessary, debug with the GNU debugger gdb or from from emacs with the emacs command “M-X
gdb”. Another nice debugger is ddd.
Check the documents posted on the Unix & Linux Resources Module on Canvas for more links on
Linux/Unix programming.
Compiling and Running Programs:
Work from a Unix terminal or shell. Use gcc to compile your C source files:
g++ -g -o pipes pipes.cc
Compiler option -g enables debugging.