第五种 TCP预先派生子进程服务器程序:
对预先派生子进程服务器的最后一种改动就是由父进程调用accept,然后再将所接受的已连接描述字传递给子进程。父进程必须跟踪子进程的忙闲状态,以便给空闲子进程传递新的描述字。为每个子进程维护一个信息结构,用来管理各子进程。
在调用fork之前,先创建一个字节流管道(Unix域的字节流套接口),它是Unix域的字节流套接口。当子进程派生后,父进程关闭一个描述字(sockfd[1]),子进程关闭另一个描述字(sockfd[0]),此外,子进程将流管道的字节所在端(sockfd[1])复制到标准输出。
child.h:
typedef struct { pid_t child_pid; /* process ID */ int child_pipefd; /* parent's stream pipe to/from child */ int child_status; /* 0 = ready */ long child_count; /* # connections handled */ } Child; Child *cptr; /* array of Child structures; calloc'ed */
Child.c:
/* include child_make */ #include "unp.h" #include "child.h" Child *cptr; /* array of Child structures; calloc'ed */ pid_t child_make(int i, int listenfd, int addrlen) { int sockfd[2]; pid_t pid; void child_main(int, int, int); Socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd); if ( (pid = Fork()) > 0) { Close(sockfd[1]); cptr[i].child_pid = pid; cptr[i].child_pipefd = sockfd[0]; cptr[i].child_status = 0; return(pid); /* parent */ } Dup2(sockfd[1], STDERR_FILENO); /* child's stream pipe to parent */ Close(sockfd[0]); Close(sockfd[1]); Close(listenfd); /* child does not need this open */ child_main(i, listenfd, addrlen); /* never returns */ } /* end child_make */ /* include child_main */ void child_main(int i, int listenfd, int addrlen) { char c; int connfd; ssize_t n; void web_child(int); printf("child %ld starting\n", (long) getpid()); for ( ; ; ) { if ( (n = Read_fd(STDERR_FILENO, &c, 1, &connfd)) == 0) err_quit("read_fd returned 0"); if (connfd < 0) err_quit("no descriptor from read_fd"); web_child(connfd); /* process request */ Close(connfd); Write(STDERR_FILENO, "", 1); /* tell parent we're ready again */ } } /* end child_main */
pr_cpu_time.c:
#include "unp.h" #include <sys/resource.h> #ifndef HAVE_GETRUSAGE_PROTO int getrusage(int, struct rusage *); #endif void pr_cpu_time(void) { double user, sys; struct rusage myusage, childusage; if (getrusage(RUSAGE_SELF, &myusage) < 0) err_sys("getrusage error"); if (getrusage(RUSAGE_CHILDREN, &childusage) < 0) err_sys("getrusage error"); user = (double) myusage.ru_utime.tv_sec + myusage.ru_utime.tv_usec/1000000.0; user += (double) childusage.ru_utime.tv_sec + childusage.ru_utime.tv_usec/1000000.0; sys = (double) myusage.ru_stime.tv_sec + myusage.ru_stime.tv_usec/1000000.0; sys += (double) childusage.ru_stime.tv_sec + childusage.ru_stime.tv_usec/1000000.0; printf("\nuser time = %g, sys time = %g\n", user, sys); }
web_child.c:
#include "unp.h" #define MAXN 16384 /* max # bytes client can request */ void web_child(int sockfd) { int ntowrite; ssize_t nread; char line[MAXLINE], result[MAXN]; for ( ; ; ) { if ( (nread = Readline(sockfd, line, MAXLINE)) == 0) return; /* connection closed by other end */ /* 4line from client specifies #bytes to write back */ ntowrite = atol(line); if ((ntowrite <= 0) || (ntowrite > MAXN)) err_quit("client request for %d bytes", ntowrite); Writen(sockfd, result, ntowrite); } }
client.c:
#include "unp.h" #define MAXN 16384 /* max # bytes to request from server */ int main(int argc, char **argv) { int i, j, fd, nchildren, nloops, nbytes; pid_t pid; ssize_t n; char request[MAXLINE], reply[MAXN]; if (argc != 6) err_quit("usage: client <hostname or IPaddr> <port> <#children> " "<#loops/child> <#bytes/request>"); nchildren = atoi(argv[3]); nloops = atoi(argv[4]); nbytes = atoi(argv[5]); snprintf(request, sizeof(request), "%d\n", nbytes); /* newline at end */ for (i = 0; i < nchildren; i++) { if ( (pid = Fork()) == 0) { /* child */ for (j = 0; j < nloops; j++) { fd = Tcp_connect(argv[1], argv[2]); Write(fd, request, strlen(request)); if ( (n = Readn(fd, reply, nbytes)) != nbytes) err_quit("server returned %d bytes", n); Close(fd); /* TIME_WAIT on client, not server */ } printf("child %d done\n", i); exit(0); } /* parent loops around to fork() again */ } while (wait(NULL) > 0) /* now parent waits for all children */ ; if (errno != ECHILD) err_sys("wait error"); exit(0); }
server.c:
/* include serv05a */ #include "unp.h" #include "child.h" static int nchildren; int main(int argc, char **argv) { int listenfd, i, navail, maxfd, nsel, connfd, rc; void sig_int(int); pid_t child_make(int, int, int); ssize_t n; fd_set rset, masterset; socklen_t addrlen, clilen; struct sockaddr *cliaddr; if (argc == 3) listenfd = Tcp_listen(NULL, argv[1], &addrlen); else if (argc == 4) listenfd = Tcp_listen(argv[1], argv[2], &addrlen); else err_quit("usage: serv05 [ <host> ] <port#> <#children>"); FD_ZERO(&masterset); FD_SET(listenfd, &masterset); maxfd = listenfd; cliaddr = Malloc(addrlen); nchildren = atoi(argv[argc-1]); navail = nchildren; cptr = Calloc(nchildren, sizeof(Child)); /* 4prefork all the children */ for (i = 0; i < nchildren; i++) { child_make(i, listenfd, addrlen); /* parent returns */ FD_SET(cptr[i].child_pipefd, &masterset); maxfd = max(maxfd, cptr[i].child_pipefd); } Signal(SIGINT, sig_int); for ( ; ; ) { rset = masterset; if (navail <= 0) FD_CLR(listenfd, &rset); /* turn off if no available children */ nsel = Select(maxfd + 1, &rset, NULL, NULL, NULL); /* 4check for new connections */ if (FD_ISSET(listenfd, &rset)) { clilen = addrlen; connfd = Accept(listenfd, cliaddr, &clilen); for (i = 0; i < nchildren; i++) if (cptr[i].child_status == 0) break; /* available */ if (i == nchildren) err_quit("no available children"); cptr[i].child_status = 1; /* mark child as busy */ cptr[i].child_count++; navail--; n = Write_fd(cptr[i].child_pipefd, "", 1, connfd); Close(connfd); if (--nsel == 0) continue; /* all done with select() results */ } /* 4find any newly-available children */ for (i = 0; i < nchildren; i++) { if (FD_ISSET(cptr[i].child_pipefd, &rset)) { if ( (n = Read(cptr[i].child_pipefd, &rc, 1)) == 0) err_quit("child %d terminated unexpectedly", i); cptr[i].child_status = 0; navail++; if (--nsel == 0) break; /* all done with select() results */ } } } } /* end serv05a */ void sig_int(int signo) { int i; void pr_cpu_time(void); /* 4terminate all children */ for (i = 0; i < nchildren; i++) kill(cptr[i].child_pid, SIGTERM); while (wait(NULL) > 0) /* wait for all children */ ; if (errno != ECHILD) err_sys("wait error"); pr_cpu_time(); for (i = 0; i < nchildren; i++) printf("child %d, %ld connections\n", i, cptr[i].child_count); exit(0); }
编译命令:
gcc server.c child.c pr_cpu_time.c web_child.c -o server -lunp