Chinaunix首页 | 论坛 | 博客
  • 博客访问: 259430
  • 博文数量: 74
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 793
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 21:01
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: LINUX

2010-05-16 04:42:41

对预先派生子进程服务器程序的最后一个修改版本是只让父进程调用accept,然后把所接受的已连接套接字传递给某个子进程。这么做是为了绕过为所有子进程的accept调用提供锁保护的可能需求。这种技术多少有点儿复杂,因为父进程必须跟踪子进程的忙闲状态,以便给空闲子进程传递新的套接字。
所以,我们必须为每个子进程维护一个信息结构以便管理.在child.h中我们定义了这个结构

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 */


此结构包含了进程ID,父进程中连接到子进程的字节流管道描述符,进程状态,该子进程已处理客户的计数。

父进程和子进程是通过管道来传递连接套接字的。派生出子进程后,父进程关闭其中一个描述符(sockfd[1]),子进程关闭另一个描述符(sockfd[0])。子进程还把流管道的自身拥有端(sockfd[1])复制到标准错误输出,这样每个子进程就通过读写标准错误输出和父进程通信。
而在child_main函数里,子进程阻塞在read_fd调用中,等待父进程传递过来一个已连接套接口描述字。

child05.c
------------------------------

/* include child_make */
#include "global.h"
#include "child.h"

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 ( (= 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 */





我们关闭每个子进程中的监听套接口,因为只有父进程才调用accept。父进程必须处理监听套接口以及所以字节流套接口,所以父进程使用select多路选择它的所以描述符。
下面的main函数相比以前的版本的变动在于:分配描述符集,打开与监听套接口以及到各个子进程的字节流管道对应的位;计算最大描述符的值;分配child结构数组的内存空间;主循环由一个select调用驱动。

serv05.c
---------------------------

/* include serv05a */
#include "global.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 [ ] <#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 (= 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 (= 0; i < nchildren; i++)
                if (cptr[i].child_status == 0)
                    break; /* available */

            if (== 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 (= 0; i < nchildren; i++) {
            if (FD_ISSET(cptr[i].child_pipefd, &rset)) {
                if ( (= 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 (= 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 (= 0; i < nchildren; i++)
        printf("child %d, %ld connections\n", i, cptr[i].child_count);

    exit(0);
}



运行结果:
$ ./serv05 12345 10
child 17637 starting
child 17638 starting
child 17639 starting
child 17640 starting
child 17641 starting
child 17642 starting
child 17643 starting
child 17644 starting
child 17645 starting
child 17646 starting

user time = 0.008, sys time = 0.17201
child 0, 937 connections
child 1, 741 connections
child 2, 502 connections
child 3, 320 connections
child 4, 0 connections
child 5, 0 connections
child 6, 0 connections
child 7, 0 connections
child 8, 0 connections
child 9, 0 connections


程序源代码包见<<客户/服务器程序设计范式---迭代服务器程序>>后的附件
阅读(587) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~