An Introduction to UNIX and Programming Under UNIX

Please Note:

This document was originally written a few years ago to support the classes I teach at Penn State Behrend. The laboratories and software have changed since then, so some of the information here does not apply to our current laboratory configuration (e.g., we no longer do dual-booting in W109 because we now have the Linux lab in B77).

Motivation

Why do I want you to use Linux?

In my opinion, no one studying computer engineering or taking a course in computer operating systems, networking, or VLSI should get out without learning about, and working with, UNIX.

Computer engineers should be exposed to as many computing environments as possible so that they can not only understand how the computers they are learning to design may be used, but also so that they can work in these other environments once they graduate.

Many, if not most, of you will be designing computers, hardware, software, and VLSI circuits using software running on UNIX-based systems. For those of you interested in networking, the backbone of the Internet is composed primarily of computers running some variant of UNIX, and many of the routers and intermediate systems connected to the Internet are UNIX-based.

Most of the software packages that are familiar with were originally developed on UNIX systems: ProEngineer and PSPICE are just two that started as UNIX programs.

No matter what anyone else tells you, you will be better off knowing about UNIX and having had some experience with a UNIX-based system before you leave school.

Linux is an open-source version of UNIX which you can download for free; or, if you don't have the time, you can buy a prepackaged distribution of it from your favorite retailer.

There is a definite lack of a solid introduction to UNIX in our current curriculum. To ensure that you are exposed to a UNIX-like operating system, you will be using tools whose origins are in the UNIX world, in the environment they were originally designed for.

To make it easier for you to get your feet wet, I have put together this self-introduction.

Goals

There are several goals to this introduction which are intended to introduce you to different aspects of life with UNIX. After discussing the basics of your UNIX account, we will look at basic UNIX file system handling, editing with the vi text editor, compiling C programs with gcc, and submitting projects using tar, gzip, uuencode, and mail.

UNIX Accounts

On UNIX-based systems, "users" (i.e. people who want to run programs) have "accounts". This means that they have been given a "login name" (which is also known as a "login") and (usually) that they are allowed to store things on a disk on the system. Sometimes systems are connected to something called a "file server". This is a computer on which multiple users have disk space which is made available to other computers.

You will be given an account which you will be able to access from the machines in W109. Usually, this account will have the same login name as your official PSU email address. Depending on exactly how the account is set up, you will be able to log into a UNIX machine and run programs.

Part I: Getting Started

Booting Into Linux

In the W109 lab, you will be given an account with the same login name as your official PSU email address. Once this account is set up, you will be able to use Linux on any of the Linux-capable machines in W109. To do this, you need to reboot the machine into Linux. I don't recall the specifics, but one of the options from Windows' login screen should be "reboot". Choose this, and reboot the machine.

At a certain point in the boot-up process, it will give a choice of operating systems to run. Choose Linux, then wait for the login prompt to appear...

Logging-in from the Console

Once the machine finishes booting into Linux, or when you sit down in front of a machine that is already running Linux, you should see a "login" prompt. For example:
Red Hat Linux release 7.1 (Seawolf)
Kernel 2.4.2-2 on an i686
 
pavil login: _
This indicates that the computer is waiting for a user to type in his or her login name (sometimes just called a "login"). After you do this, the computer will respond by asking for a "password". For example:
Password: _
Each user has a password and is expected to keep it to themselves. You type in your password, then the computer checks to see if what you typed matches its record of your password. If it doesn't, it gives some message, then displays the login prompt again. If the typed and recorded passwords do match, the systems starts a "login shell" for you. A shell is a program which you use to communicate with the computer to try to get it to do what you want it to do. (In Windows, the entire windowing environment is a big, slow, graphical shell).

Setting up Your Account

From within this "shell", you can create and delete files and directories, and run programs such as compilers and editors. But first, you need to change your password. This can be done at any time by running "passwd" as such:
[eetpc20:~/463]$ passwd
Changing password for rfisher
(current) UNIX password:	<- Type in the UNIX password that I gave you.
If you type in the wrong password, you will get an authentication error:
passwd: Authentication failure
Otherwise, it will continue by asking for your new password:
New UNIX password:		<- Type in a new password using a mix
				   of numbers and upper and lower case
				   letters.
If your chosen password is too easy to crack, it will produce a BAD PASSWORD message and tell you why it is bad.
BAD PASSWORD: ...
Otherwise, you should then see:
Retype new UNIX password:	<- Retype your new password.  This
				   helps to ensure that you do not
				   mistype the new password and lock
				   yourself out of the account.
If the passwords match, you should see:
passwd: all authentication tokens updated successfully
Otherwise, you will get a mismatch error and a chance to try again:
Sorry, passwords do not match
If you are unable to change your password, contact me immediately.

You can now start using your UNIX account.

Once your shell starts (I will make it "bash" by default), it will display a "command prompt". You can then type in commands that it will attempt to execute for you. These commands fall into three categories:

  1. Shell commands - Commands that the shell itself performs.
  2. System programs - Names of executable files that are provided as part of the Linux operating system. The shell itself is actually a system program.
  3. Applications programs - Names of executable files that are not part of the operating system, although they may be commonly supplied with it. The debate about Microsoft's Internet Explorer centered around whether it was a system program or an application.

I will introduce you to several commands and programs in this short tutorial. Here are a couple of short, and admittedly incomplete, lists of the ones you may find most useful:

Part II: The File System

UNIX file systems form a tree of "directories". The top directory is called the "root" directory, and the tree grows downward from it. Each directory can contain zero or more "subdiectories" and zero or more "files".

Some directory in this tree will be your "home" directory, and you will store your files in it and the subdirectories below it. You will "own" these files and directories, and can add, remove, or modify these files and directories.

To get a listing of the files in the "current directory" execute the command "ls". You should see the following:

Desktop
"Desktop" is the name of something in your home directory, but what? We can find out by using ls's "-l" switch. This time, type "ls -l". You should now see something like the following:
drwxr-xr-x    5 Yourlogin      users        4096 Sep 11 09:43 Desktop
This shows the "long format" of the listing. The major sections of this are the "permissions", the number of "links", the "owner", the "group", the "size", the creation "date" and "time", and the "name" of the item.

Let's work from left to right. The first letter of the permissions indicate what type of object the item is. Files will have a dash, -, and directories will have a letter 'd'. There are other types of things, but we won't worry about them yet.

Immediately following the type indicator, there are three sets of three characters. These are, respectively, the owner, group, and world "permission sets". The owner's permissions are "rwx" which means that the owner has "read", "write", and "execute" permissions for this directory. This means that the owner, "Yourlogin", can get a listing of the items in the subdirectory "Desktop" (that is, they can read it), change the information stored about the those items such as renaming a file in that directory (that is, they can write to the table which stores the information about the items in the directory), and change the current directory to "Desktop" (i.e. they can execute that directory).

The next set of permissions apply to the group "users". Anyone in that group can read or execute the directory, but they cannot write to it. This means that other people can go into your "Desktop" directory and get a listing of the files there, but they can not write their own files into it or rename yours. If the group had write permissions to "Desktop", they could do these things.

One caveat though, they would have to have execute permission through the entire path from the root to your Desktop directory to go into it. You must always have execute permission to a directory to go into it or any directory below it.

Groups are intended to allow a set of users to be specified which can have less permissions to certain files or directories than the owner(s) of those items, but more than the general class of users. This allows a group of students in a class to look at certain files, or people working on the same project to do so without other users on the system interfering or copying their work.

The final set of permissions is for anyone logged onto the system who isn't the owner or in the group that owns the item.

After the permissions is the number of links. We won't worry about that right now. Following this is the owner's login name or user ID (UID) number, and the name or group ID (GID) of the group which owns the file. You've already seen what these mean.

After this is the size. For a file, this would be the size of the file in bytes, but for a directory, it is the size of the disk are set aside to store its information.

The creation date and time follow the size, and finally, the name of the item itself is listed.

ls has another switch -a which shows "hidden" files. Let's do an "ls -la" or an "ls -al" (either works):

total 44
drwx------    4 rfisher   users        4096 Sep 11 10:55 .
drwx--x--x   16 root     root         4096 Sep 11 09:45 ..
-rw-------    1 rfisher   users           5 Sep 11 10:55 .bash_history
-rw-r--r--    1 rfisher   users          24 Sep 11 09:43 .bash_logout
-rw-r--r--    1 rfisher   users         230 Sep 11 09:43 .bash_profile
-rw-r--r--    1 rfisher   users         124 Sep 11 09:43 .bashrc
-rwxr-xr-x    1 rfisher   users         434 Sep 11 09:43 .emacs
drwxr-xr-x    3 rfisher   users        4096 Sep 11 09:43 .kde
-rw-r--r--    1 rfisher   users         435 Sep 11 09:43 .kderc
-rw-r--r--    1 rfisher   users        3394 Sep 11 09:43 .screenrc
drwxr-xr-x    5 rfisher   users        4096 Sep 11 09:43 Desktop
Now we see that there are several files already in your home directory. These are resource files used to configure your environment and to automatically run things when you log in or log out. You can learn about these on your own.

There are two special files "." and "..". "Dot" is the name given to the current directory. "dot dot", or "double-dot" is the name given to the parent directory of the current directory.

You can move up the tree by changing to the parent directory. You do this by using the "cd" command:

cd ..
Now try ls. You will see the following:
ls: .: Permission denied
This is because you are not in the group that owns the (now) current directory, and the world permissions do not include reading.

Okay, let's look at where we are, then take a short trip before heading back home. The "pwd" command lists the path from the root, named "/" to the current working directory (pwd=path to working directory). If you run it you will see:

/home/students
Now let's go to the root. We could do this by executing "cd ../..", but let's give an "absolute" pathname:
cd /
pwd will now show just a "/". Let's now go into your Desktop directory. We can do this in one of several ways:
  1. cd /home/students/Yourlogin/Desktop
  2. cd ~Yourlogin/Desktop
  3. cd ~/Desktop
  4. cd ~Yourlogin; cd Desktop
  5. cd; cd Desktop
The first uses the absolute path. The second uses a shorthand form to indicate the home directory of Yourlogin, but is still an absolute path. The third uses a shorthand form to indicate the home directory of the person executing the command (in this case, Yourlogin), but is also still an absolute path. The fourth one does the move in two steps (two commands separated by a semicolon). First it changes to your home directory absolutely, then it makes a change "relative" to the current directory, that is, it moves into the directory below the current directory (which would be ~Yourlogin) named "Desktop". The final version is also done in two steps. The first is real shorthand for changing to the current directory of the person executing the command, and the second is a relative move to ~/Desktop.

Okay, we've killed cd. What if you want to make your own directories? You can do this with the "mkdir" command. You can also remove these directories with the "rmdir" command. In either case, you can name the directory using an absolute or relative pathname. Let's create a subdirectory for your first project called "Project0" off your home directory:

cd
mkdir Project0
cd Project0
Do an "ls -la" and you should see something like:
total 8
drwxrwxr-x    2 rfisher  users      4096 Sep 13 17:17 ./
drwx------   10 rfisher  users      4096 Sep 13 17:17 ../
This is what an empty directory looks like. We'll start talking about filling it in a moment...

Part III: Basic File Manipulation

One of the basic commands in UNIX is "cat" (short for catenate). cat can be used to look at a file, copy a file, and catenate files together. Let's make a C source file using cat. You'll hate this.
cat > hello.c
cat takes anything it gets on stdin and sends it to stdout. In the above case, cat's stdin is the keyboard and its stdout has been "redirected" to the file "hello.c". This means that whatever you type will be sent straight into hello.c. Okay, let's start program. If you make a mistake, just keep going for now:
void main(void)
{
	printf("Hello, World!\n");
}

The <CTRL-D> tells cat that there is no more input, and that it should stop. You will get a command prompt back, so let's see what we created by running:
cat hello.c
This time we used cat to put the contents of hello.c on its stdout (i.e. the terminal screen):
void main(void)
{
	printf("Hello, World!\n");
}
What you get may not match the above. If it doesn't, try "cat > hello.c" a couple of more times until you get it.

Now, we should have #included stdio in the above file, so we need to get it at the top of the file somehow. Let's use cat to do this. First, we need to make a new file with the #include line in it. Let's call it "tmp:":

cat > tmp
#include <stdio.h>

Okay, we want to catenate (append) the stuff in hello.c onto the end of this and save it in a new file. Let's do this:
cat tmp hello.c > tmp2
This says put the contents of tmp, then the contents of hello.c into tmp2. That was too easy, so let's get rid of tmp2 and try it a different way:
rm tmp2
cat tmp > tmp2
cat hello.c >> tmp2
In the first command, we used the "rm" command to remove the file tmp2. Note that generally you use different commands to remove a file and to remove a directory. The second command basically just copied the contents of tmp into tmp2. The third command is a bit different. When using redirection, a > says to truncate (throw out the data in) the file before copying the new data to it. The >> says to just append the new data onto the end of the data currently in the file. If we had done "cat hello.c > tmp2" as the third command, the data copied by the second command (the contents of tmp) would have been thrown out.

We can look at what we have now by catting tmp2. We should see:

#include <stdio.h>
void main(void)
{
	printf("Hello, World!\n");
}
Okay, let's rename tmp2 to hello.c using the move command "mv", and get rid of tmp:
mv tmp2 hello.c
rm tmp
The move command may ask if you want to overwrite tmp2. If it does, say 'y' for yes. Now, let me point out something. In UNIX, there is no such thing as a "garbage can". If you delete a file, it is GONE!

If you are in cenbd431 (VLSI Design), then skip ahead to Part V.

Otherwise, let's compile this puppy and see what we get!

Part IV: Compiling Using GCC

GCC is the GNU (pronounced ga'nu) C Compiler. It has been ported to a large number of platforms, and is the C compiler usually found on a Linux system. In fact, much of the software found on a typical Linux system is from the GNU project.

Let's use gcc to compile our hello.c now:

gcc -Wall -o hello hello.c
This looks pretty complicated. The "-Wall" switch (no pun intended) tells gcc to Warn us about all common mistakes. The "-o" switch followed by "hello" tells gcc to name the output file "hello". Finally, the stuff after the switches are the names of the files we want to compile and/or link together to build "hello".

When we run gcc, it finds something to warn us about:

hello.c:3: warning: return type of `main' is not `int'
This tells us that on line 3, there was a "non-fatal" error. These are usually referred to as warnings. In this case, it was letting us know that while it is legal in C to have main() return void, it isn't a good idea. We'll change this so that we don't have to look at the warning message anymore, but notice that if you do an ls, you will see that the executable "hello" did get made and can be executed. Okay, let's fix our file. This time we will use a text editor called "vi"

Part V: The vi Text Editor

There are several text editors out there, each of which has its own positive characteristics and its own foibles. vi is found on almost, if not, all UNIX systems, so we'll look at it. You can teach yourself emacs or one of its clones some other time.

To open an existing file using vi, you just enter "vi" followed by the file name:

vi hello.c
Now, we should see something like this:
#include <stdio.h>
void main(void)
{
        printf("Hello, World!\n");
}
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
"hello.c" 5L, 67C
At the bottom, we see the name of the file followed by the line and character counts. At the top, we see the contents of the file followed by several lines with just a tilde (~) at the beginning of the line. These are empty lines in vi's buffer space. Note that the tildes may not show up on your web page, in which case they will appear as blank lines in this document.

Right now, we are in "command mode". We can move the cursor around or give vi commands. Let's move to the word "void" using the cursor movement keys. h, j, k, and l move the cursor left, down, up, and right, respectively. The arrow keys on your keyboard may or may not move the cursor. The cursor starts at the upper left corner, so we want to move down one line to get to the beginning of "void". Do that now.

Now we want to change that word from "void" to "int". Do this by using the "change word" command, cw:

cw int<ESC>
This says change the word under the cursor to "int" and go back to command mode. The cursor is now at the end of the word "int".

Now, let's change the word "World" to "Randy". We can go directly to the word "World" by using the search command (/):

/World<Enter>
This brings us to the beginning of the word "World". If we had a longer file with more "World"s in it, we could jump to the next one by using the "next" command (n). Let's do that now:
n
It tells us that "search hit BOTTOM, continuing at TOP". We could also search backwards using capital-N:
N
Now it tells us that "search hit TOP, continuing at BOTTOM".

Okay, enough playing around, let's change this word. This time we'll use the replace-characters command (R):

RRandy<Esc>
The R command tells vi to replace letters until we hit the &tl;Esc> key. Here, we replaced a word with exactly 5 letters with another of exactly 5 letters, so it worked out beautifully.

Let's save this version and compile it. We do that using the write command (:w) [colon-w]. This tells vi to save the file with its current name, hello.c.

We could also save it with a different name, say randy.c, by using the :w command with a filename:

:w randy.c

Now, let's see the files in the current directory without leaving vi. We do this by invoking a shell from vi using the shell command (:!) followed by the command we want to execute (in this case ls):

:!ls<Enter>
It will run the command, and wait for you to press the Enter key before going back into the editor. This lets you see the output. We don't really care about the output, I just wanted you to see how to invoke a shell from vi.

Let's hit <Enter> then quit vi. We quit using the quit command (:q):

<Enter>
:q<Enter>
If you make an error sometime and don't want to save the file, you can do a quit-without-save (:q!) command instead.

Okay, I said we would compile this, so let's do it:

gcc -Wall -o hello hello.c
Oh no, another error! By now you're probably wondering if I can write anything correctly. What we see is this:
gcc -Wall -o hello hello.c
hello.c: In function `main':
hello.c:5: warning: control reaches end of non-void function
This tells us that on line 5 we reached the end of main() without returning the int that we said main() returns, so let's fire up vi again and fix this. We could hit the up arrow, which scrolls back through the commands we have given until we get to "vi hello.c", or we could do "!vi" which tells the shell to execute the last command that started with the letters "vi" Either way, we end up doing:
vi hello.c
Okay, we want to add a line just before the closing brace of main() which is on the last line. We can go to the last line using the go-to-last-line command (G):
G
This puts us at the beginning of the last line in the file. We want to add a line over this one, so let's use the insert-line-above command (O):
O<Tab>return<Esc>
Oops! We forgot to end it with a semicolon. Let's use the append-to-line command (A) to fix this. From anywhere in the line, the A command will append whatever you type afterward onto the end of the current line:
A;
Doh! This time we forgot to return a value. Let's use the append-after-cursor command (a) to fix this. The a command will start inserting characters immediately after the cursor:
a 0
This should give us "return 0;". The insert command is just like append, but starts inserting characters at the cursor position.

Let's use the go command to go to line 2 just for kicks. Just type:

2G
This tells vi "line 2, go to the beginning of it". Note that the go-to-end-of-file command was just a special case of the G command.

Now, let's change the void argument to main() to be "int argc, char *argv[]);". We can do this without causing any new errors or warnings. To do this we'll use the delete-character command (x) a couple of times, then the change-to-end-of-line command (c$):

/main
lllllxxc$int argc, char *argv[])<Esc>
Remember, the 'l's move the cursor right. The dollar sign represents the end of the line, so c$ means change everything until the end of the line.

We can remove a line entirely by doing a delete-line command (dd). First, let's add a junk line to the file...

2j
A<Enter><Tab>junk this line<Esc>
(the 2j says "execute the next command twice: go up") and now delete it:
dd
The deleted line is now buffered, so we can "put" it somewhere else:
kkkp
We should now see:
#include <stdio.h>
int main(int argc, char *argv[]);
        junk this line
{
        printf("Hello, Randy!\n");
        return 0;
}
This can be used to move lines around. Let's move the body of main() above the junk line:
j4ddP
This time, we move down one line to be on the line with the opening brace, then deleted 4 lines (4dd), then put them above the current line (P) to get:
#include <stdio.h>
int main(int argc, char *argv[]);
{
        printf("Hello, Randy!\n");
        return 0;
}
        junk this line
We can also copy lines into the buffer. Go down to junk, do a yank (yy), then move above the body to drop it there:
/junk<Enter>yyk%P
This says go to &qout;junk" (/junk<Enter>), copy the line into the buffer (yy), go up one line (k) to the closing brace, go to the matching brace (%), and put the buffered text above the current line (P).

Now let's delete these lines and quit screwing around.

ddGdd
If we make a mistake, we can undo it with the undo command (u). Try it:
uuddGdd
and let's save and quit:
:wq<Enter>

If you are in cenbd431 (VLSI Design), you can skip ahead to Appendix B.

Let's compile it one more time to see if it's still correct:

gcc -Wall -o hello hello.c
You should get a command prompt back almost immediately (because there should be no errors, and it's a small program). Now, let's move on and look at a handy program called "make"...

Part VI: make and makefiles

"make" is a program which is used to automate program compilation. It can also be used for other things. For example, I use make to build the PostScript version of my Doctoral dissertation from the LaTeX source files.

make is actually a pretty stupid program, what it does is to look at the source files that a target file depends on to see if they are newer (have been changed) since the last time the target file was built. If so, it will follow a list of commands that you supply to update the target file.

I have a reasonably good tutorial on make already, so I'll just give a URL for it here, and let you read it later. For now though, we'll go ahead and build a makefile for our hello project...

First let's call our makefile "Makefile" and write it with cat:

cat > Makefile
Our first target will be the executable file "hello", which depends on the source file "hello.c" and nothing else. We describe this by putting a line like the following in Makefile:
hello: hello.c
The thing before the colon is the "target", everything behind the colon is a "dependency". Dependencies are separated by a space or tab.

After the target line, we can have a set of "command" lines. Each of these must begin with a tab. These lines are a list of commands to execute, just as you would from the command prompt, to build the target from the dependencies. The command we have been using will suffice, so we will add the following line under the target line:

	gcc -Wall -o hello hello.c
Note that we can have as many command lines as we like, as long as each begins with a tab. We could just as easily added the following set of lines instead:
	gcc -Wall -E -o hello.i hello.c
	gcc -Wall -S -o hello.s hello.i
	gcc -Wall -o hello hello.s
We could also have split this process up into several targets as such:
hello: hello.s
	gcc -Wall -o hello hello.s

hello.s: hello.i
	gcc -Wall -S -o hello.s hello.i

hello.i: hello.c
	gcc -Wall -E -o hello.i hello.c
Sometimes this is useful, but not especially for this project.

Okay, now we have a "rule" for building hello from hello.c. Let's add a couple more to make it easier to manage the project. First, let's add a "clean" target. For this course, clean should remove any files that are not needed to build or execute the program. That means it should remove any .i, .o, and .s files that it might create, but not the source file "hello.c" or the executable file "hello":


clean:
	rm -f *.i *.o *.s
Notice that there were no dependencies for this target. That's because we always want it to run the command to remove the intermediate files, but we don't want it to do anything else first. Also notice that we have used the "-f" switch for rm. This forces rm to remove the file. Actually it tells rm "If you are about to ask me if I really want to remove the files listed, the answer is `yes'."

Now let's add one more target to clean up our source tree to be distributed. We'll call the target "distclean":


distclean: clean
	rm -f hello
This rule does two things. First, the dependency on "clean" tells it to see if clean is up-to-date and update it if necessary. This has the effect of running the commands in the rule for "clean". The second thing it does is run the commands in the rule for "distclean".

If your lost here, it's because I didn't explain things too thoroughly above. Each target represents a file. If the file doesn't exist, or if the target file is older than any of its dependencies, the commands to build it will be run. Notice that none of the commands actually makes a file named "distclean", and also that none of them makes a file named "clean". Thus, whenever the rules for these targets are reached, the commands to build them will always be executed. This is really why we didn't need any dependencies in the rule for "clean".

Now make chains the dependencies to update each file that the target depends on before executing the commands to build that target. So, because distclean dependend on clean, the first thing that happens when make reaches the rule for distclean is that it updates the dependency clean by going to the rule to build it. There are no dependencies for clean, and never a file named "clean", so it tries to build it by executing the command "rm -f *.i *.o *.s". Then it returns to the rule for distclean.

If there were more dependencies for distclean, it would update each of them in turn, but because there aren't, it now checks to see if distclean is newer than its dependencies. Now, we know that distclean doesn't exist, so it will now try to build it by executing the command "rm -f hello".

Okay, we've played around enough here. You can read up on make in the tutuorial I mentioned above. Right now, your Makefile should look about like this:

hello: hello.c
	gcc -Wall -o hello hello.c

clean:
	rm -f *.i *.o *.s

distclean: clean
	rm -f hello
So <Ctrl-D> out of cat, and let's get this project ready to turn in.

Part VII: Documenting Your Project

I will give more specifics about what I want to see in the way of documentation later, but basically, you will write a file named Notes for each project.

In this file you must list your name and the class number. You must also describe the choices you made in implementing your solution to the project (for example "I chose to use a linked list instead of an array because..."), and the problems you encountered in doing so (for example "This was a bad idea because...").

Your documentation should reflect the way your program really works (or doesn't), not the way it was supposed to work according to the project assignment. I already know what it says, and can always print up a copy if I forget.

Part VIII: Submitting Your Project

Okay, you have a subdirectory named Project0 with your source file, Notes file, and Makefile. The Notes and Makefile must ALWAYS be in the top-level directory of the source tree that you submit. I must be able to run "make" and have it build the executable. The executable name must always be the name that I tell you to use. Your sources may be in subdirectories if you wish, as long as the makefile builds the executable and leaves it in the top-level directory, I don't care how you organize your sources.

To submit your project sources, you must first clean them up for distribution. If you implemented your makefile correctly, you should be able to just do:

make distclean
from the top-level directory.

Next, we want to convert the source tree into a single compressed file. To do this we will first move up one directory (to your home directory). You can do this by executing:

cd
. Building the compressed archive file is often done in two steps using "tar" and "gzip", or in a single step using GNU tar (which will automatically run gzip). Here's the one-step process:
tar cvzf Project0.tgz Project0
The "czvf" tells tar to create an archive (c), be verbose (tell us about each file it puts into the archive) (v), compress it using gzip (z), and call the archive file (f) "Project0.tgz". The rest of the line is the name of the files and directories that it should put into the archive. It will work recursively, so we need only give it the name of our top-level directory.

Now you have a single compressed archive of your project sources. However, this is a binary file, so we need to convert it to a text file so it can be mailed without getting trashed. We do this using "uuencode":

uuencode Project0.tgz Project0.tgz > Project0.uue
This tells uuencode to convert the file "Project0.tgz" to text, storing the name "Project0.tgz" (so that when I uudecode it, it will know what to name the file), and store the encoded version in "Project0.uue". This file will be mailable, and will be the file you actually turn in.

You will turn your project source archive in by mailing it to course account on ece.bd.psu.edu (for example, cenbd463@ece.bd.psu.edu for the Operating Systems course).

The subject line must include your login name and the project number. For example, if a student with the login "joe999" were in the Operating Systems course (cenbd463), he would use the command:

mail -s "joe999 project0" cenbd463@ece.bd.psu.edu < Project0.uue
Be sure that you use YOUR login name in the subject above, NOT "joe999"

Remember, you may submit new revisions of this project as often as you like up until the deadline. I will grade the last version submitted.

Appendix A: A Quick Word About Grading

Your project's correctness will be graded by script. That is, I'll collect all the final versions, then run a script that builds and runs each of them. The output will be saved, and then compared with the expected output. If they deviate, you will lose points.

It is important for you to realize that the scripts are not forgiving at all. If your programs do not output exactly the format I specify, the scripts will flag them as errors.

Appendix B: Other Resources

You should be aware of some basic resources pertaining to Linux. These include both on-line and hardcopy resources which you may wish to look at or obtain.

Here is a short list of Linux resources.

Appendix C: Xwindows

I occasionally get asked questions about Xwindows, so here is a brief introduction to Xwindows.