Process Management Operating Systems Spring 2005

Lab Assistant Magnus Johansson [email protected] room: 1442 postbox: 54 (4th floor, building 1) phone: 018 - 471 6225

Course hompage http://www.it.uu.se/edu/course/homepage/os/distvt05



• The lab package can be downloaded from the course homepage. You unpack the package in an appropriate directory by tar zxf ProcessManagement.SunOS.tar.gz Begin with this. All files necessary for this lab is located in the OSLab/process mgmt/ directory. • Questions are marked Qx and programs Px, x ∈ IN. Answers must be well justified, answers of the “yes”, “3”, or “no” will be rejected. • How to hand in: Send an email to [email protected] with answers to the questions and the source code to the programs. • There is a FAQ (frequently asked question) the end of this assignment (se section 4), have a look at it before you start. 

This document was last updated January 26, 2005 1 Getting Started

1.1 (1)

In this part of the assignment you will learn how to use the ps command to see what processes are running. Start by reading the manual page for ps(1), (do: man -s 1 ps in your shell)1. Use the ps command to see what processes are running on your machine. To use the ps command just type ps in a shell. You should not type ps(1). The (1) is just away of telling in what section of the manual pages the ps command belongs. Try using different options to see what they do. In particular, run ps with:

• no options • -l

• -e • -u



Q1 What does ps do with no option and with these options?

Q2 Explain what happens when you use -el? 

Try the (1) command and get familiar with the pstree2 script.



Q3 Explain what the -s flag, that you gave to man command above, actually does. Hint: use man man to read the manual page for the man command. 

The manual pages are divided into different sections. An introduction to each section can be found by man -sn intro, where n is the section number.



Q4 Which section of the manual holds documentation for the UNIX system calls? Q5 And shortly, what is the difference between (1) and wait(2)? 

A useful command when looking for manual pages is apropos. It looks at the indexes of the manual pages and sorts out the occurences of what you are looking for. Try apropos wait. There is also a graphical tool for displaying manual pages called xman. xman & in your shell and familiarise yourself with it. It may not work depending on your system setup, in this case check your PATH that should include /usr/sup/X11/bin. In the worst case use /usr/sup/X11/bin/xman with full path. If you use the Konqueror web brower you can also type man:wait in the location field to get a good looking .

1.2 fork(2) and wait(2)

In this part you will learn how processes are created, and how to avoid defunct (zombie) processes. Begin with reading the manual pages for fork(2) and wait(2). This is the standard way to use fork(2) to create a single :

1man 1 ps on 2If pstree is not available on your platform, a script is provided in the lab package, run it with ./pstree 2 /* parent creates a process */ pid=fork();

/* the return value is a process ID, and * has the following meaning (always these): */ switch (pid) { case -1: /* error: fork was unsuccessful */ case 0: /* this is the child process */ /* no process ID */ /* ... do something ... */ default: /* this is the parent process */ /* pid=process ID of the child */ /* ... */ }

/* both processes continue here */

Note: both processes will run the code that follows the switch statement. Normally this is unde- sirable, and by forcing the child to terminate after it has done its “useful work”, we can prevent it. Pay attention to the switch command, you may want to use or break. The code shown above can be modified as follows:

. . . case 0: /* this is the child process */ /* ... */ exit(0); . . .



P1 Write a simple program that uses the fork(2) system call. Start with simplefork.c and complete it. Do not forget to wait for the spawned child: use wait(2), in order to avoid defunct processes. 

Note: “to use fork(2)...” or “ use wait(2)...” means that the system call fork or wait shall be used, you are not supposed to use fork or wait with 2 as an argument. A good way to find out what arguments a system call needs is to look at the man pages.

Compile: type and all your programs will be compiled. You will get an error message No rule to make target ‘server1.c’, needed by ‘server1’. That’s normal, you will create these files, it is the next step, do not worry about that, but care about errors from the compiler! To answer the following questions, you may experiment with the simple fork.c program.



Q6 Processes marked are known as zombies. When/why do they appear? When do they disappear? (Additional (3C) statements may help you follow the behaviour closely).

Q7 What happens when you try to use or kill -9 to remove a zombie? What will happen if they are left behind after your program exits?  3 If you want to see a defunct process, put in a sleep(20) before the wait to delay the parent for 20 seconds. Start another xterm and use the ps -u [user name] command repeatedly. After 20 second, the defunct process should disapear from the listing.



Q8 You see some printouts in the program (simplefork.c), what do pid,ppid and pgrp stand for? You may look at the manual pages of getpid(2),getppid(2) and getpgrp(2). The function sleep(3C) is used to delay the processes. 

2 Process Management

This assignment will demonstrate to you a more advanced use of fork(2) and wait(2) through a client-server program. The files you have to look at are server.c and netstuff.h. When you start the server on a machine it needs a “port” to listen to. It takes by default 5000. If another server is already running (if you are sitting on hamberg this is probably the case), it cannot take it, and you may have an error. It is possible to bypass this by giving another port. Of course, if you do this, the client has to know it. The client may take as an argument, the name of a machine (useful if you run the server on a different machine) or a port number. The client may take both argument in the order server name port number.

• Begin with compiling the server (normally already done, with make that you used for the previous exercise). Try xterm -e ./server & and xterm -e ./client &3 to get a feeling of what the programs do. • Start several clients and observe the problem: the server can handle only one client at a . The other ones have to wait.

The client is used as follows: it waits for a string (you enter one at the prompt), sends the string to the server which does some useful job, in this case it returns the reversed string4 . The client has an auto mode, to activate it, enter auto at the prompt. To stop the client press Ctrl-D or Ctrl-C. Now you have a feeling of your task : make the server handle several clients at the same time. To achieve this, you will have to use fork(2). You will proceed in two steps :

1. add a fork() 2. handle the generated zombies

2.1 Accepting Several Clients

The server works as follows :

1. initServer() which initializes network stuff, you are not supposed to look at this, and you do not have the sources either. 2. for(;;) infinite loop where the server (a) listens to the network and accept a new connection with acceptConnection() which returns 0 in case of success. (b) handle the connection with connectionJob(pid)

You see in the code closeAccept(pid) which is to close the accepted connection, got from acceptConnection(). The problem is : when the server is in connectionJob(pid) it does not listen to the network. The function connectionJob returns only when the connection is closed. And that’s the reason why clients have to wait.

3the & is used to start the programs in the background. 4try to enter “dromedarenalpotoplanerademord” 4 client requests client requests

listens listens acceptConnection acceptConnection

handle the connection fork handle the connection connectionJob fork connectionJob

close the connection close the connection terminate closeAccept closeAccept exit

server.c server1.c

Figure 1: Structure of server.c (given) and server1.c (your task)



P2 Copy server.c to server1.c and modify server1.c so that it can accept several connections at the same time. The main process will do acceptConnection, fork, closeAccept while the spawned children will do connectionJob, exit. Do not care (yet) to the function childHandler. Figure 1 illustrates the programs. 

Note: you must get rid of error messages if you get any during execution.



Q9 How are multiple connections handled now? 

Open several clients and make sure that they all get responses from the server. Close one con- nection(client)5 and use ps(1) with the right options to visualize the zombies, they are marked .



Q10 Why isn’t it a good idea to use wait in this program to get rid of the zombie servers. 

2.2 Getting Rid Of The Zombies - 1

Normally one would use signals to take care of the zombies: when a child exits, the main process is interrupted, calls the wait(2) function and returns to its job. To try this (not compulsory, this is for your culture), activate the code in server.c by replacing #define PART 1 by #define PART 2. The zombies will disappear. Notice the call to the function sigaction(2) that installs our signal handler. However in the lab we are supposed to study the fork function, so we will skip this solution and use another, heavier, not so clean, but very for lab purposes.

2.3 Getting Rid Of The Zombies - 2

When you use pstree, you notice that the first process at the root is and has process ID 1. This is the first process created when your machine is booted. As you see this process has many children. Do you think that this process has created all these children? Think about it . . . . Here is the secret : the init process adopts orphan processes. When the parent of a process dies, it can’t wait for its children, can it? And you have child processes that may terminate . . . which is not good. So init adopts these children, i.e., it takes them in the process hierarchy (that you get with pstree) as its descendant. This is why init has so many process children. So you are going to use this! 5Click on one client window and press ctrl-c 5 client requests

listens acceptConnection

fork fork handle the connection fork fork connectionJob

close the connection terminate terminate closeAccept exit exit

Big mother Suicidal child Orphan worker

Figure 2: Structure of server2.c.

By now, server1 consists of several processes. To make the process handling the connection(the one doing all the connectionJob be adopted by init, you have to let its parent process die, but of course it can’t be the main server process! The solution is to use two consecutive forks, the second one handles the connection, the first one exits to let the second child be adopted by init. Figure 2 illustrates the structure of the server2.c program.



P3 Copy server1.c to server2.c and implement the solution. Be careful not to have zombies left. Check this with ps or pstree. Q11 Describe two methods that let you see the merciful adpoption of the orphans by Init. 

Now you have a question which is: why all this pain about zombies if init solves the problem eventually? The answer is : your server process is going to run a long time, and if you have zombies, it will be unable to fork after a while, and your server is “dead”6.

3 Process Communication

In this part of the assignment you will learn how processes can communicate with each other. You will write a program that consists of two processes that communicate through a pipe to solve a problem.

3.1 Pipes

A pipe is a mechanism provided by the operating system that lets one process send a stream of bytes to another one. Since a child process inherits all open descriptors from the parent when it is created, we can create a pipe (which is a pair of connected descriptors) before creating the child process, thus allowing both processes access to the same pipe. In figure 3, we see how an empty array of 2 descriptors is passed to pipe(2), and a pipe object is created to connect them. After the call to fork(2), both parent and child processes share the same pipe object through their own copies of the descriptors. Both processes can now write to and read from the pipe. After one of the processes closes the write descriptor and the other closes the read descriptor, the two processes share a one-way communication channel.

3.2 Problem description

The problem you will solve is this: while working for Intrusions AB (security consultants) you have come across a file containing userids and passwords for the employees at Victims-R-Us. You need to

6non fertile might be more appropriate. . . 6 0 1 0 1 0 1 0 1

Pipe Pipe Pipe

0 1 0 1

(i) (ii) (iii) (iv)

Figure 3: (i) Before the call to pipe(). (ii) After the call to pipe(). (iii) After the call to fork(). (iv) After each of the processes closes one of the descriptors. show the management of Victims-R-Us that their system is insecure, by cracking all the passwords in the file. Write a program to do so. Since the users at Victims-R-Us are very naive, they have all chosen ordinary English words for pass- words. In the OSLab directory you find a pair of of files containing English words, process mgmt/words1 and process mgmt/words2.

3.3 Assignment

Begin by reading the manual pages for pipe(2), read(2), write(2) and close(2). You have the most part of this program in crack.c. You just have to complete the main function. The model your program should use is as follows:

• A main program that sets up the communication, then starts two other processes to do the actual work. The first argument to the program should be the name of a file containing words to try. The main program should use the pipe(2) system call to create a pipe. Then it should start two child processes (a consumer pid1 and a producer pid2) and wait for both of them to finish.

• A producer that “generates” passwords to try, one at a time. An argument to the producer indicates the name of a word file. The producer should open this file, send the words one at a time through the pipe to the consumer until the end of the file is reached or the consumer is finished, then close the file and exit. The producer shall use non-buffered I/O, (ie. write(2), to send words to the consumer. The producer shall count the words it reads from the file, and print the total number of read words just before exiting. If you have doubts on how the code works, have a look at the manual pages for fopen(3S), fgets(3S) and scanf(3S).

• A consumer gets words from the producer and tests each of them on all the uncracked accounts. The consumer should read one word at a time from the pipe, (using read(2)), and test it, until there are no more words to read, or until all the accounts have been successfully cracked. The consumer shall, just before it exits, print the number of words it read from the pipe and the number of remaining (uncracked) accounts.



IMPORTANT: To handle the cracking in the consumer process pid1, you only need to call the consumer() function. To read words from the file in the producer process pid2 , you only need to call the producer() function. You must provide these functions with the appropiate arguments, remember that the filename is in argv[1]. 

Either the producer or the consumer will finish first, depending on what words are in the file 7 and what passwords the employees have chosen. The code already deals with this situation in a reasonable manner, and without the use of special messages though the pipe.



P4 Complete the main function of crack.c to make the program work as described. Note: the skeleton handles errors and looks at the exit status of the processes, so be strict with error handling and do not return EXIT SUCCESS in case of error, i.e.m do no use exit(EXIT SUCCESS) but exit(EXIT FAILURE) in case of an error. Furthermore, you will have to use kill(2) to kill the first process if you cannot create the second one. Be careful with the pipe: do not forget to close it where appropriate. You may want to test your program on words3, which is faster than words1 words2 for debugging. 

Important

• Read the manual pages for pipe(2), read(2) and write(2).

• The function strlen(3C) returns the number of bytes in a string, (without including the terminating null character, ’\0’). It is used in the cracker program.

• Always check return values. Most system calls return a value to indicate if they were successful, and if not, the reason for failure. They do fail sometimes! In these cases you should use perror(3) to print a message describing the failure.

• perror(3C) is a special message function that understands the error values returned by most system calls. Use it! But note also that perror(3C) is not a general purpose function, it will only indicate the status of system calls, not C library functions and other functions. Use fprintf(stderr,...) when these fail.

• Leave no zombies. If you create a process, you need to wait for it too (with wait(2)). All processes should exit properly, and the main process must clean up after them. This means that the main process must keep track of how many processes have been successfully started, and never exit without waiting for each of them.

• Compile with: make. The given Makefile contains appropriate rules and options for compi- lation. Warnings are signs of deficiencies in the code and are treated as errors!



Q12 (a) Explain the basic characteristics of a pipe. (b) Explain how the pipe behaves when • you try to read an empty pipe and there is – no open write descriptor – at least one open write descriptor • you try to write to a full pipe and there is – no open read descriptor – at least one open read descriptor 

3.4 Test runs

The report you hand in for the assignment must include test runs for both words1 and words2 in the course directory. One of the files contains all the passwords, and the other only contains some of them. Make sure that both the consumer and the producer exists correctly, (prints number of words read respectively written), in both test runs. 8 The command script [filename] can be used record a terminal session to file filename. To end the recording, type exit.

9 4 FAQ

How to print? Use a2ps -Ppr1515 yourfile.c (change pr1515 if needed).

I got segmentation fault, what is that? Your program tried to read/write in forbidden memory zones. It is typical from a pointer not initialized.

Bus error Fatal error, typically you tried wait(2) in your program and you did not notice that wait(2) meant look at wait in the section 2 of the man pages.

What does kill do? It sends a signal to a process, to terminate it generally (depends on the options), it is similar to clicking to the close button of a window. kill -9 is a nasty signal which cannot be ignored and that always kills a running or sleeping process, i.e. not a zombie. See the manual pages on kill for complete information.

Why compilation uses -Wall -Werror? To warn on all possible errors and treat them as errors. . . to force you to have some discipline with C because it is a rather permissive language.

What is make? It is an utility widely used in programming: it takes a file (Makefile most often) in a special format, which contains dependency rules, then it calls the proper programs to build the targets. The result is that you type make and depending on your last modifications, the right programs are rebuilt. It is very convenient to use it, that’s why a Makefile is pro- vided in the lab. You may have a look at it if you are curious. Have a look at the manual and www.csd.uu.se/documentation/programming/make for more information.

Binding error? Address in use error? Are there client/server options? When you start the server on a machine it needs a “port” to listen to. It takes by default 5000. If another server is already running, it cannot take it, and you may have an error. It is possible to bypass this by giving another port. Of course, if you do this, the client has to know it. The client may take as an argument, the name of a machine (useful if you run the server on a different machine) or a port number. The client may take both argument in the order server name port number.

How do these programs communicate? The programs use sockets to communicate over the network by using the TCP/IP stack. Go to the computer network course to know more, it is very interesting.

Command not found? You are trying pstree (for example) and you expect it work, but you get an error. Check that you are in the right directory () and try ./pstree. The error comes from the fact that “.” (your current directory is not in your path: $PATH to check this).

Can’t execvp You probably tried xterm -e /client& or xterm -e .client& instead of xterm -e ./client&.

10