Exercises:C processes: Difference between revisions

From COMP15212 Wiki
pc>Yuron
No edit summary
gravatar W81054ch [userbureaucratinterface-adminsysopPHRhYmxlIGNsYXNzPSJ0d3BvcHVwIj48dHI+PHRkIGNsYXNzPSJ0d3BvcHVwLWVudHJ5dGl0bGUiPkdyb3Vwczo8L3RkPjx0ZD51c2VyPGJyIC8+YnVyZWF1Y3JhdDxiciAvPmludGVyZmFjZS1hZG1pbjxiciAvPnN5c29wPGJyIC8+PC90ZD48L3RyPjwvdGFibGU+] (talk | contribs)
m (1 revision imported)
 
(No difference)

Latest revision as of 10:22, 9 August 2019

On path: Exercises 0: Exercises • 1: Pointer Exercise • 2: Arguments Exercise • 3: Malloc Exercise • 4: Structs Exercise • 5: Processes Exercise • 6: Shared memory Exercise • 7: Pipes Exercise • 8: Exceptions Exercise • 9: Synchronisation Exercise • 10: Files Exercise • 11: Threads Exercise • 12: Unix proc Exercise
Depends on Processes

Download exercise files

The purposes of this exercise are:

  • to understand the spawning of processes in Unix
  • to catch process exceptions and take remedial action

Before starting, if you haven’t tried this before, run the utility top from a Unix shell. This shows the (currently) busiest processes on your system, together with some information about them. Of most immediate interest now is “PID” - the Process IDentifier which is a unique number allocated to a process when it is started.

The command/name is also shown; you can probably spot top itself running there.

Pressing q will terminate that utility.

You can try that again; top will get a different PID this time.

Processes can create other processes. In Unix/C this is done using the library function fork(). processes.c has an example piece of code which you should compile and execute.

Note: the call getpid() returns your own PID; this will be different each time you execute the utility.

Notice that everything after the fork() call sort-of happens twice; this is because fork() has cloned the process, and you now have two processes executing processes.c. The processes will have have different PIDs, and fork()’s return value distinguishes which copy you are. If you are the same process which made the fork() call, the return value is that of the new (“daughter”) process; if you are the newly created process the value is 0.

Fork flowchart

Two copies of the same thing may not be very useful, so it is common to identify one of the copies and change that to run something new. First compile the program baby.c and check it executes okay.

Now, to processes.c, add the line:

if (new_PID == 0) execlp("./baby", "baby", (char*)NULL);

(It should already be present, but commented out.)

between the fork() and the exit(0) call. This causes the daughter (only) to execute the new program (using “./baby” in case the working directory is not in your shell’s $PATH).

Notes: the second (and subsequent) argument(s) is(are) the input to the new process; really it is better if the second argument repeats the first so the new process can know precisely how it was called… but today you can play.   The final argument is a “null string” - a pointer to a \0 - which can be treated as a ‘magic word’ if you’re not that interested in C right now.

Other experiments

Also build, execute and understand the behaviour of the two small demonstrations of ‘orphans’ and ‘zombies’; a brief introduction to these terms are given in the processes article.

To see the zombie, run top in another window: it should be observable whilst the parent process stays asleep.

To ‘ensure’ operations happen in a particular order, both of these use a sleep() call, which suspends that process for that number of seconds. (More about sleep elsewhere.) This is not a 100% reliable way of sequencing processes: there are better ways where they deliberately communicate.

Curiosity

Here’s an observation made by one of our students, experimenting with this exercise. In involves a few concepts not necessarily covered in detail, yet: pointers are given below.

The initial program, as described, prints out the start-up message once, then forks so it prints the termination message twice.

However if the output is redirected to a file instead, when that file is examined it contains two copies of the start-up message as well as two of the termination message. Can you think of an explanation?

Here are some possible hints:

It’s a fairly tough question but try and puzzle it out before seeking an explanation.

Explanation

printf() is a C library which, for efficiency purposes, will not output directly to an I/O device but will buffer some characters: this is illustrated in the file access article.

The default behaviour is that the stdout stream is directed to the terminal. As this is intended for humans to read the buffer is flushed at each LF character ('\n'). However if the output is to a file it will buffer a larger (‘file-sized’) block of bytes before flushing.

In the first instance, therefore, stdout is flushed and empty (having printed one message) before the fork. However in the second case the message is still buffered in the process’ data space and is duplicated by the fork() call. When it is flushed out (i.e. when the streams terminate) this will appear in the output file. In this case, both outputs from the fork are being redirected into the same file (this is a bit naughty!) so all the output appears there.

This example (true story - 2018) shows how some O.S. mechanisms can sometimes be ‘exposed’ to a user.