TCP并发服务器(二)——预创建子进程,accept不上锁
2014-07-24 09:52
316 查看
TCP并发服务器(二)——预创建子进程,accept不上锁
1.说明
这是预创建进程中速度最快的。
适用于源自BSD的内核,但是accept是一个库函数的System V可能不允许这么做(因此需要accept上锁保护)。
2.优缺点
无需引入父进程执行fork的开销就能处理新的客户。缺点时必须在启动阶段猜测需要预创建多少子进程。
如果某个时刻客户数恰好等于子进程总数,新的客户将被忽略直到至少一个子进程重新可用。
在实际上客户没有被忽略,内核为每个新客户完成三路握手,直到listen调用的backlog数为止。然后accept在已连接队中取出描述符。
尽管connect可能立即返回,但是请求可能在一段时间以后才被服务器处理。
3.解决办法
可以监控闲置子进程数,动态增减子进程数。
4.代码
5.惊群
由于子进程阻塞在accept上,但是当一个连接到达时只会有一个子进程获得连接。但是所有的子进程都被唤醒了,这种现象就是惊群,会影响程序性能。
当子进程过多时惊群现象会更加严重。
我们可以不阻塞于accept,而是select上。
但是多个进程在引用同一个套接字描述符上调用select会发生select冲突。此时会增加CPU时间,因为增加了系统调用。
结论:如果多个进程阻塞在引用同一个实体(如:套接字或普通文件)的描述符上,最好直接阻塞于诸如accept之类的函数,而不是select。
1.说明
这是预创建进程中速度最快的。
适用于源自BSD的内核,但是accept是一个库函数的System V可能不允许这么做(因此需要accept上锁保护)。
2.优缺点
无需引入父进程执行fork的开销就能处理新的客户。缺点时必须在启动阶段猜测需要预创建多少子进程。
如果某个时刻客户数恰好等于子进程总数,新的客户将被忽略直到至少一个子进程重新可用。
在实际上客户没有被忽略,内核为每个新客户完成三路握手,直到listen调用的backlog数为止。然后accept在已连接队中取出描述符。
尽管connect可能立即返回,但是请求可能在一段时间以后才被服务器处理。
3.解决办法
可以监控闲置子进程数,动态增减子进程数。
4.代码
#include "unp.h" #include <vector> #include <sys/mman.h> //for mmap() using std::vector; //共享内存首地址 static long *cptr;; static int nchildren; static vector<int> pids; void sig_int(int signo) { for (int i = 0; i < nchildren; ++i) { kill(pids[i], SIGTERM); } while (wait(NULL) > 0) { ; } if (errno != ECHILD) { err_sys("wait error"); } DPRINTF("The number of child process accept."); for (int i = 0; i < nchildren; ++i) { DPRINTF("%ld", cptr[i]); } void pr_cpu_time(void); pr_cpu_time(); exit(0); } int main(int argc, char *argv[]) { int listenfd; socklen_t addrlen; 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: a.out [ <host> ] <port#> <#children>"); } nchildren = atoi(argv[argc - 1]); pids.resize(nchildren); //查看连接在子进程的分布 //使用共享内存未每个子进程的连接计数 //fork之后父进程和子进程共享cptr long *meter(int); cptr = meter(atoi(argv[argc - 1])); //cptr = (long*)Calloc(nchildren, sizeof(long)); //创建子进程 pid_t child_make(int i, int listenfd, int addrlen); for (int i = 0; i < atoi(argv[argc - 1]); ++i) { pids[i] = child_make(i, listenfd, addrlen); } //SIGCHLD处理函数 void sig_chld(int); Signal(SIGCHLD, sig_chld); Signal(SIGINT, sig_int); for ( ; ;) { ; //child does everything } //end for(;;) return 0; } void sig_chld(int) { static int cnt = 0; pid_t pid; int stat; //param1: 想要等待的PID;-1: 等待第一个终止的子进程 //param2: 子进程的终止状态(整数) //param3: 附加选项;WNOHANG:没有子进程终止时,不阻塞 while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) { //成功:返回进程ID > 0, 出错:0或-1 DPRINTF("Waitpid for %d child process\n", ++cnt); ; } return; } pid_t child_make(int i, int listenfd, int addrlen) { void child_main(int, int, int); pid_t pid = Fork(); if (pid > 0) { //parent return pid; } child_main(i, listenfd, addrlen); } void child_main(int i, int listenfd, int addrlen) { DPRINTF("child %ld starting\n", (long)getpid()); void web_child(int); for ( ; ; ) { DPRINTF("Wait for a connection"); int connfd = Accept(listenfd, NULL, NULL); //共享内存计数增加 ++cptr[i]; DPRINTF("Accept a connection, total accept %ld connection", cptr[i]); web_child(connfd); Close(connfd); } } long *meter(int nchildren) { #ifdef MAP_ANON //匿名映射 long *ptr = (long*)Mmap(0, nchildren * sizeof(long), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); #else int fd = Open("/dev/zero", O_RDWR, 0); long *ptr = (long*)Mmap(0, nchildren * sizeof(long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //已完成文件到进程地址空间的映射,可以关闭原文件 Close(fd); #endif return ptr; }
5.惊群
由于子进程阻塞在accept上,但是当一个连接到达时只会有一个子进程获得连接。但是所有的子进程都被唤醒了,这种现象就是惊群,会影响程序性能。
当子进程过多时惊群现象会更加严重。
我们可以不阻塞于accept,而是select上。
但是多个进程在引用同一个套接字描述符上调用select会发生select冲突。此时会增加CPU时间,因为增加了系统调用。
结论:如果多个进程阻塞在引用同一个实体(如:套接字或普通文件)的描述符上,最好直接阻塞于诸如accept之类的函数,而不是select。
相关文章推荐
- TCP并发服务器(三)——预创建子进程,accept文件锁
- TCP并发服务器(四)——预创建子进程,accept互斥锁
- TCP并发服务器(六)——创建线程池,每个线程accept,accept使用互斥锁保护——基于UNP代码
- TCP并发服务器(七)——可动态增减的线程池,主线程accept——基于UNP代码修改
- 多进程并发编程----基于高级的预先创建进程池(accept使用文件上锁)的模型
- 多进程并发编程----基于高级的预先创建进程池(accept不上锁)的模型
- 多进程并发编程----基于高级的预先创建进程池(accept使用线程上锁)的模型
- TCP并发服务器设计
- DEARTCP1.2-通用TCP服务器组件,用于创建物联网开发
- TCP_DEFER_ACCEPT与高性能web服务器
- 实现TCP并发服务器之五(epoll函数)
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- Java 并发TCP 服务器[CODE]
- 快速构建MMO服务器框架(七)高并发TCP网络框架
- 实现TCP并发服务器之二(多线程)
- 实现TCP并发服务器之三(select函数)
- 笔试题14:用TCP通信模型创建一个Web服务器(源码)
- 多进程服务器中,epoll的创建应该在创建子进程之后
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器 <转>
- 多进程服务器中,epoll的创建应该在创建子进程之后