Lab 01: Systems Programming in C
Goals
- Get started in learning systems programming in C.
- Learn to work with a few basic Unix system calls.
Credits
The material developed for this lab was developed by Prof. L. Felipe Perrone. Permission to reuse this material in parts or in its entirety is granted provided that this "credits" note is not removed. Additional students files associated with this lab, as well as any existing solutions can be provided upon request by e-mail to perrone[at]bucknell[dot]edu. Some very minor changes were made by Xiannong Meng.
Setup
- Assume you have set up your Gitlab account, created and shared your csci363-s13 project (directory) with the instructor by now. If you haven't done so, please visit the page about Introduction to Gitlab and go through the steps there to set up your Gitlab account.
- Open a terminal window and run the following sequence of commands (the directory "csci363-s13" has been created when setting up Gitlab):
- cd csci363-s13
- mkdir labs
- mkdir labs/lab01
- If you already created an ssh keypair in the regular Linux network, you can skip ahead directly to step 4. From your terminal, open an ssh session to linuxremote.bucknell.edu, authenticate yourself, and then type ssh-keygen (keep the default location for the key files and choose a passphrase that you can remember easily. Log out from the remote Linux server.
- Now, in your csci363/labs/lab1 directory, create a file to contain answers to the questions in this assignment using the command below.
- touch csci363/labs/lab1/answers.txt
After running this line in your shell, open the file in a text editor and write down the lab information including lab number, your name, and the date of the lab. This is Part 1.1 of the answers.
- Copy the files for this lab to your Lab1 directory
- cp ~cs363/Spring13/student/labs/lab01/* .
Note there is a space and a dot '.' at the end of the command.
Problem 1: Using C File I/O
Consider the C programs in files file-test.c and read-test.c. You will be asked to read the source code, compile and execute the programs, and try to understand what is happening.
In this problem you need to:
- Read the programs file-test.c and read-test.c. If you haven't read about the Unix system calls below yet, read the description of each one in the appropriate man pages while reading the programs:
- open(2), fopen(3)
- close(2), fclose(3)
- read(2), fread(3)
- write(2), fwrite(3)
- perror(3)
- Compile, excute the programs, and observe the results.
- make
- ./file-test
- ./read-test myfile
- ./read-test input-file
Th program file-test creates a data file called myfile. Read the source code and understand the data (myfile) generated by the program. Next, inspect the hex dump of myfile running xxd myfile. Write your answer to the question in answers.txt: (1.2) Do the file contents shown by xxd command match the data that file-test created?
Inspect the results produced by ./read-test myfile and ./read-test input-file. Write your answer to the question in answers.txt: (1.3) If you are told that both files contain the same data, how do you explain the fact that you get very different results running read-test on the two data files?
- Modify read-test.c to validate its inputs, check all return codes from system calls, and print appropriate error messages when necessary. Specifically, you need to check:
a. if the input argument is given (e.g. try to run the program without the required argument);
b. if the file has been opened successfully (e.g. try to run the program with a wrong file name);
c. if the number of bytes actually read from the file is equal to the number of bytes you wanted to read from the file.
If an error condition is found, indicate to the user what happened by printing the appropriate error message. Use perror in the case of a system error (item b), or use printf otherwise (items a and c).
Write your answers to the question in answers.txt (1.4) If you modify the two programs above to use C streams, that is, FILE* instead of low-level file descriptors, will this modification allow you to see identical output from read-test for both input files? Note: You don't have to rewrite your program to use C streams to verify the facts; instead, thinking a little bit about the reasons for the phenomenon would give you the answer.
Deliverables:
- Your modified read-test.c.
- Answers to questions (1.2), (1.3), and (1.4) in answers.txt.
Add and commit to Git
Execute the following commands to save your two files to Git, assuming you are currently in the directory of csci363-s13/labs/lab01.
- git add answers.txt
- git add read-test.c
- git commit -m "Lab 1 Problem 1"
Problem 2: Unix Processes, Pipes, and Timers
Consider the C program in file fork-test.c. Read the source code, compile, and run the program. Try to understand what the program does and how each of the system calls works. If you havent read about the Unix system calls below yet, look at each ones description in the appropriate man pages:
- fork(2)
- pipe(2)
- close(2)
- sleep(3)
- sigaction(2)
- setitimer(2)
Copy fork-test.c into a new file called two-way.c and modify your new file according to the specifications below:
- The program must have two processes called One and Two, respectively similar to the originals source and sink.
- Process One can send messages to process Two. and vice-versa. Since pipes are unidirectional, you will need to set up one more pipe for each process so that both processes can send messages to the other.
- Process One alternates between sending one of two different messages to process Two. The message is always sent one byte at a time. Note that the message is a C-string, that is, a null-terminated array of characters; the null byte is sent on the pipe and used by the receiver to detect where the message ends. The total number of bytes sent is the length of the message plus one (the null byte).
- Process Two prints the time of day when the message is received in full. After this, it sends back to process One the message of three characters "ack" for acknoledgement, one byte at a time (again remember that the message is a C-string, that is, a null-terminated array of characters). Also note that the message consists of three characters without the quotes.
- Process One waits for the response message from process Two to arrive in full, and checks if the text it contains is really "ack". If that is the case, then process One will transmit an alternate message, otherwise it will retransmit the current message. After processing the response message from process Two, process One must wait 0.5 second before transmitting another message. Note that this wait time cannot be achieved with the sleep library call, so you have to use some other mechanism to count times less than 1 second. You'd have to use Linux signal and signal handling to solve this problem. The basic idea is that the process starts a timer when receiving the response message. The process then uses the system call pause to suspend itself. When the previously started timer goes off, a signal is sent to the calling process, i.e., process One, that started the timer. If a timer handler function is defined, the process wakes up and continues. You are asked to read the manual page and use web resources to find out how to set and use this delay mechanism properly.
Deliverables:
A new program called two-way.c which meets the specifications above.
Execute the following commands to save your program to Git, assuming you are currently in the directory of csci363-s13/labs/lab01.
- git add two-way.c
- git commit -m "Lab 1 Problem 2"
- git push
Extra-credit: Once you have completed the lab assignment, a nice addition to two-way.c would be to have process One print to the screen the time elapsed between sending out the first byte of a message and receiving the last byte in the response sent back from process Two. To accomplish this, you will want to look at the following man pages:
If you complete any of the extra credit work, make sure commit and push them to the Gitlab server.
Submission
In this course, you will submit all your work via a Git repository.
Remember that the due date for this lab is the starting time of the next lab session.