Behold the definition of doserver. Assume that serverprog is an interactive program that takes input on standard input and output on standard output.
doserver(int connection) { dup2(connection, 1); dup2(connection, 0); close(connection); execlp("serverprog", "serverprog", 0); perror("execlp"); exit(1); }The following code implements a concurrent server for serverprog, running at machine plank and port 5005. It makes sure that no more than maxcli clients are attached to the server at any one time.
/* This is csm.c */ #include < stdio.h > #include "socketfun.h" main(int argc, char **argv) { int sock, connection, dummy, nclients, maxcli; maxcli = atoi(argv[1]); sock = serve_socket("plank", 5005); nclients = 0; while(1) { if (nclients < maxcli) { connection = accept_connection(sock); nclients++; if (fork() == 0) { close(sock); doserver(connection); } else { close(connection); } } else { wait(&dummy); nclients--; } } }This program has a slight problem with zombie processes. Specifically, at any one time, there may be as many as maxcli-1 zombie processes on the system.
Your job is to write a new version of csm.c that never has zombie processes. I'll sketch how.
Instead of forking off a child that calls doserver(), you will fork off a grandchild that calls doserver() as an orphan. Thus it can never become a zombie. The main csm process has to be able to know when a grandchild is done. It can do this by having a pipe open to each grandchild. Instead of calling wait() as above, it can call select() on all the pipes to find out which ones have been closed because the grandchildren have exited. For each such pipe, it can close the pipe, and decrement nclients.
Write this program.
Finally if you're having a hard time making the pieces fit together, make it easy for me to give you partial credit. Don't just write a bunch of junk with pipe()'s and select()'s and fork()'s. Comment what you do, and if you still think it's wrong, explain what you're trying to do.