第五种 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