Copy my_ring.c and the makefile:
The Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int parse_args(int argc, char *argv[ ], int *np){
if ( (argc != 2) || ((*np = atoi (argv[1])) <= 0) ) {
fprintf (stderr, "Usage: %s nprocs\n", argv[0]);
return(-1); };
return(0);
}
int make_trivial_ring(){
int fd[2];
if (pipe (fd) == -1)
return(-1);
if ((dup2(fd[0], STDIN_FILENO) == -1) ||
(dup2(fd[1], STDOUT_FILENO) == -1))
return(-2);
if ((close(fd[0]) == -1) || (close(fd[1]) == -1))
return(-3);
return(0); }
int add_new_node(int *pid){
int fd[2];
if (pipe(fd) == -1)
return(-1);
if ((*pid = fork()) == -1)
return(-2);
if(*pid > 0 && dup2(fd[1], STDOUT_FILENO) < 0)
return(-3);
if (*pid == 0 && dup2(fd[0], STDIN_FILENO) < 0)
return(-4);
if ((close(fd[0]) == -1) || (close(fd[1]) == -1))
return(-5);
return(0);
}
int main(int argc, char *argv[ ]){
int i; /* number of this process (starting with 1) */
int childpid; /* indicates process should spawn another */
int nprocs; /* total number of processes in ring */
if(parse_args(argc,argv,&nprocs) < 0) exit(EXIT_FAILURE);
if(make_trivial_ring() < 0){
perror("Could not make trivial ring");
exit(EXIT_FAILURE); };
for (i = 1; i < nprocs; i++) {
if(add_new_node(&childpid) < 0){
perror("Could not add new node to ring");
exit(EXIT_FAILURE); };
if (childpid) break; };
/* ring process code */
fprintf(stderr, "node %d of %d\n", i, nprocs);
exit(EXIT_SUCCESS);
} /* end of main program here */
The makefile
:
COMPILER = gcc
CFLAGS = -Wall
EXES = example01 example02 example02a my_ring
all: ${EXES}
example01: example01.c
${COMPILER} ${CFLAGS} example01.c -o example01
example02: example02.c
${COMPILER} ${CFLAGS} example02.c -o example02
example02a: example02a.c
${COMPILER} ${CFLAGS} example02a.c -o example02a
my_ring: my_ring.c
${COMPILER} ${CFLAGS} my_ring.c -o my_ring
clean:
rm -f *.o *~ ${EXES}
Now edit the makefile so that it no longer contains any references to example01, or other stuff that is not needed (we will be playing just with my_ring.c today. Make the ring and execute it. Do you see why each process has a distinct value of i? We’ll make use of this later.
Now elaborate on this fprintf statment so that each process also prints out the process id of its parent, hint: getppid(). Make it, and test it.
Now create prtastr.h and prtastr.c (short for print a string) that contain the prototype and definition of the function, respectively:
void prtastr(const char* s, int fd, int n);
which prints the string s one character at a time to the file descriptor fd using write. If you don’t know how to use write to write a character at a time, then check out items 14 and 15 in:
After each character is output, the function should waste some CPU time by executing:
cpp
for(j = 0; j < delay; j++);
For some fixed value of delay. Modify the makefile so that produces prtastr.o, and my_ring.c so that it includes prtastr.h (the makefile should also be modified so that it links prtastr.o when producing the executable my_ring. E.g.
gcc -Wall my_ring.c prtastr.o -o my_ring
Now replace the call to fprintf by a call to prtastr, this of course will first require making the string using sprintf. Note that you must write out to STDERR_FILENO, since STDIN_FILENO, and STDOUT_FILENO are in being used for the pipes connecting the ring. Modify my_ring so that delay is the second command line argument. Start with delay being small, and increase it till you can see each character being printed. I found delay being a million did this quite well.
Do you understand why things look like they do??
Lets use a token ring to synchronize the access to the shared file STDERR_FILENO. In this version each process must possess the token before it can print. The token in this example will simply be an integer, say 0, that gets written around the ring. This is how it works: The first process (say with i being 1) should print its string to STDERR_FILENO, and then write the token to STDOUT_FILENO (it’s neighbour on the ring). Now all processes should do the following (in an infinite loop): 1. read the token from STDIN_FILENO, hint: 2. read(STDIN_FILENO, &token, sizeof(token)); 3. print their string, via prtastr 4. write the token to STDOUT_FILENO
They will pass the token around for a long time, control C can stop them). To see an example of passing things around the ring, look at:
Think of a way to exit gracefully (i.e. without generating a SIGPIPE a.k.a a broken pipe!).