首页 > 代码库 > UNIX网络编程卷1 服务器程序设计范式5 预先派生子进程,由父进程向子进程传递套接字描述符
UNIX网络编程卷1 服务器程序设计范式5 预先派生子进程,由父进程向子进程传递套接字描述符
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie
1.只让你进程调用 accept,然后把所接受的已连接套接字“传递”给某个子进程。
这样做就不用因为所有子进程都调用 accept 而需提供上锁保护
2.父进程必须跟踪子进程的忙闲状态,以便给空闲子进程传递新的套接字
typedef struct { pid_t child_pid; /* 子进程的进程 ID */ int child_pipefd; /* 父进程中连接到该子进程的字节流管道描述符 */ int child_status; /* 子进程状态 */ long child_count; /* 该子进程已处理的客户计数 */ } Child; Child *cptr; /* array of Child structures; calloc'ed */ /* include serv05a */ static int nchildren; int main(int argc, char **argv) { int listenfd, i, navail, maxfd, nsel, connfd, rc;//navail 表示可用子进程数目 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; //0.创建监听套接字 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); //分配 Child 结构数组的内存空间 //1.增设一个命令行参数供用户指定预先派生的子进程个数。 nchildren = atoi(argv[argc-1]); navail = nchildren; cptr = Calloc(nchildren, sizeof(Child)); /* 4prefork all the children */ //2.调用 child_make 创建各个子进程 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); } //3.设置中断信号 SIGINT 的处理函数 Signal(SIGINT, sig_int); for ( ; ; ) { rset = masterset; //如果无可用子进程则关掉监听套接字 if (navail <= 0) FD_CLR(listenfd, &rset); /* turn off if no available children */ //阻塞于 select 调用等待连接或与子进程的字节流管道可读 nsel = Select(maxfd + 1, &rset, NULL, NULL, NULL); /* 4check for new connections */ //4.accept 新连接 if (FD_ISSET(listenfd, &rset)) { clilen = addrlen; //accept 新连接,得到已连接套接字描述符 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; /* 把该子进程的状态标记为 1 (busy) */ cptr[i].child_count++; // 该子进程已处理的客户计数加 1 navail--; //可用子进程数减 1 //向该子进程的字节流管道传递一个单字节的数据 n = Write_fd(cptr[i].child_pipefd, "", 1, connfd); Close(connfd); //关闭已连接套接字 if (--nsel == 0) // 如果 select 返回的可读描述符已处理完,直接进入下一轮循环 continue; /* all done with select() results */ } /* 4find any newly-available children */ //5.处理新近可用的子进程 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++; //增加navail 计数器 if (--nsel == 0) //如果 select 返回的可读描述符已处理完,break 出这个循环,进入外部循环的下一轮 break; /* all done with select() results */ } } } } /* end serv05a */ //中断信号 SIGINT 处理函数 void sig_int(int signo) { int i; void pr_cpu_time(void); /* 4terminate all children */ //给每个子进程发送 SIGTERM 信号终止它们 for (i = 0; i < nchildren; i++) kill(cptr[i].child_pid, SIGTERM); //用 wait 回收所有子进程的资源 while (wait(NULL) > 0) /* wait for all children */ ; if (errno != ECHILD) err_sys("wait error"); //调用 pr_cpu_time 统计已终止子进程的资源利用统计 pr_cpu_time(); //todo for (i = 0; i < nchildren; i++) printf("child %d, %ld connections\n", i, cptr[i].child_count); exit(0); } /* include child_make */ #include "unp.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); //1.创建一个字节流管道 Socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd); //2.创建子进程 if ( (pid = Fork()) > 0) { Close(sockfd[1]); //父进程关闭其中一个描述符 sockfd[1] cptr[i].child_pid = pid; //保存子进程 pid cptr[i].child_pipefd = sockfd[0]; // 父进程中连接到该子进程的字节流管道描述符 cptr[i].child_status = 0; // 设置子进程的状态为 0 (ready) return(pid); /* 父进程返回 */ } Dup2(sockfd[1], STDERR_FILENO); /* 子进程把流管道的自身拥有端复制到标准错误输出,这样每个子进程就通过读写标准错误输出和父进程通信 */ Close(sockfd[0]); //关闭流管道 sockfd[0] Close(sockfd[1]); //关闭流管道 sockfd[1] (sockfd[1]已经复制到标准错误输出了) Close(listenfd); //子进程不用监听客户连接 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 ( ; ; ) { //1.等待来自父进程的已连接套接字描述符 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"); //2.处理客户请求 web_child(connfd); /* process request */ //3.关闭已连接套接字 Close(connfd); //4.告诉父进程自己已准备好 Write(STDERR_FILENO, "", 1); /* tell parent we're ready again */ } } /* end child_main */
UNIX网络编程卷1 服务器程序设计范式5 预先派生子进程,由父进程向子进程传递套接字描述符
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。