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

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: LINUX

2010-05-16 04:40:48

这是我们的一次个并发服务程序,也是传统上应用的并发方式,服务器程序调用fork派生一个子进程来处理每个客户,这使得服务器能够同时为多个客户服务,每个进程负责一个客户。但是这里有一个限制,操作系统对一个进程能够同时拥有的子进程数是有限的,当客户数到达这个限制后,服务器程序就会fork出错,从而影响并发度。但这已经可以满足绝大多数需求了,所以绝大多数服务器程序也都是按照这个范式来编写的。

不过这种传统并发服务器也有一个问题,服务器程序为每个客户现场fork一个子进程比较耗费CPU时间。当一个服务器每天处理几百个或几千个客户时,这点CPU时间是可以接受的,然后现在繁重的web服务器每天接受的连接数以百万计。这还是就单个主机而言,更繁忙的站点往往运行多个主机来分摊(如利用DNS轮询来实施负载散布)。但是,这种传统意义上的并发服务器依然相当普遍。

每个客户一个子进程方式的并发服务程序也比较简单,其核心步骤如下:
  for ( ; ; ) {
    connfd = accept(listenfd, cliaddr, &clilen)

    if ( (childpid = Fork()) == 0) {    /* child process */
      Close(listenfd);    /* close listening socket */

      ......            /* deal with client's request */

      exit(0);
    }
    Close(connfd);    /* parent closes connected socket */
  }


这种传统方式的并发服务程序实例如下,不再做过多解释:
serv01.c
------------------------

#include "global.h"

  int
main(int argc, char **argv)
{
  int listenfd, connfd;
  pid_t childpid;
  void sig_chld(int), sig_int(int), web_child(int);
  socklen_t clilen, addrlen;
  struct sockaddr* cliaddr;

  if (argc == 2)
    listenfd = Tcp_listen(NULL, argv[1], &addrlen);
  else if (argc == 3)
    listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
  else
    err_quit("usage: serv01 [ ] ");
  cliaddr = Malloc(addrlen);

  Signal(SIGCHLD, sig_chld);
  Signal(SIGINT, sig_int);

  for ( ; ; ) {
    clilen = addrlen;
    if ( (connfd = accept(listenfd, cliaddr, &clilen)) < 0) {
      if (errno == EINTR)
        continue; /* back to for() */
      else
        err_sys("accept error");
    }

    if ( (childpid = Fork()) == 0) { /* child process */
      Close(listenfd); /* close listening socket */
      web_child(connfd); /* process request */
      exit(0);
    }
    Close(connfd); /* parent closes connected socket */
  }
}
/* end serv01 */

/* include sigint */
  void
sig_int(int signo)
{
  void pr_cpu_time(void);

  pr_cpu_time();
  exit(0);
}
/* end sigint */



sig_chld()函数调用waitpid()来处理僵死子进程
sig_chld_waitpid.c
--------------------------

#include "global.h"

void
sig_chld(int signo)
{
    pid_t pid;
    int stat;

    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) {
        /* printf("child %d terminated\n", pid); */
    }
    return;
}



每个客户一个子进程方式的执行结果如下:
$ ./serv01 173.26.100.162 12345

user time = 0.052002, sys time = 0.544033

程序源代码包见
<<客户/服务器程序设计范式---迭代服务器程序>>后的附件

阅读(731) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~