进程池调研
来源:互联网 发布:电子商务属于软件信息 编辑:程序博客网 时间:2024/05/22 02:09
再说进程池之前先看看都有什么池?、
一、池的概念
因为服务器的硬件资源很丰富,所有想要提高服务器性能就有一个很直接的方法:以空间换时间(浪费”服务器的硬件资源,以换取其运行效),这就是池的概念。
池是一组资源的集合,这组资源在服务器启动之初就完全被创建并初始化,这称为静态资源分配。当服务器进入正式运行阶段,即开始处理客户请求的时候,如果它需要相关的资源,就可以直接从池中获取,无需动态分配。
很显然,直接从池中取得所需资源比动态分配资源的速度要快得多,因为分配系统资源的系统调用都是很耗时的。当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用来释放资源。从最终效果来看,池相当于服务器管理系统资源的应用设施,它避免了服务器对内核的频繁访问。
二、常见的池
1、内存池:
内存池:内存池是指程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回给操作系统,而是返回内存池。当程序退出(或者特定时间)时,内存池才将之前申请的真正内存释放。
也可以理解为,当使用new、malloc在堆区申请一块内存,会因为每次申请的内存大小不一样就会产生很多内存碎片,造成不好管理与浪费的情况。内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升。
2、线程池:
3、进程池:
三、线程池:
线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:
先启动若干数量的线程,并让这些线程都处于睡眠状态,当需要一个开辟一个线程去做具体的工作时,就会唤醒线程池中的某一个睡眠线程,让它去做具体工作,当工作完成后,线程又处于睡眠状态,而不是将线程销毁。
1、线程池主要用于
a.需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。
因为Telnet会话时间比线程的创建时间大多了。
b.对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
c.接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,并出现”OutOfMemory”的错误。
2、线程池优点
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
应用程序创建一个对象,然后销毁对象是很耗费资源的。创建线程,销毁线程,也是如此。因此,我们就预先生成一些线程,等到我们使用的时候在进行调度,于是,一些”池化资源”技术就这样的产生了。
本文所提到服务器程序是指能够接受客户请求并能处理请求的程序,而不只是指那些接受网络客户请求的网络服务器程序。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。但如果对多线程应用不当,会增加对单个任务的处理时间。
3.代码实现:
#include <pthread.h>#include <unistd.h>#include <stdlib.h>#include <stdio.h>#define P_NUMBER 255//并发线程数量#define COUNT 5 //每线程打印字符串数#define TEST_LOG "logFile.log"FILE *logFile=NULL;char *s="hello bit\0";print_hello_linux()//线程执行的函数{ int i=0; for(i=0;i<COUNT;i++) { printf("[%d]%s\n",i,s);//想控制台输出 //当你频繁读写文件的时候,Linux内核为了提高读写性能与速度,会将文件在内存中进行缓存,这部分内存就是Cache Memory(缓存内存)。可能导致测试结果不准,所以在此注释 } pthread_exit(0);//线程结束}int main(){ int i=0; pthread_t pid[P_NUMBER];//线程数组 logFile=fopen(TEST_LOG,"a+");//打开日志文件 for(i=0;i<P_NUMBER;i++) pthread_create(&pid[i],NULL,(void *)print_hello_linux,NULL);//创建线程 for(i=0;i<P_NUMBER;i++) pthread_join(pid[i],NULL);//回收线程 printf("Okay\n"); return 0;}
四、进程池调研:
1..进程池的概念:
进程池是资源进程、管理进程组成的技术的应用。
进程池技术的应用至少由以下两部分组成:
1)资源进程:预先创建好的空闲进程,管理进程会把工作分发到空闲进程来处理。
2)管理进程:管理进程负责创建资源进程,把工作交给空闲资源进程处理,回收已经处理完工作的资源进程。
管理进程如何有效的管理资源进程,分配任务给资源进程,回收空闲资源进程,管理进程要有效的管理资源进程,那么管理进程跟资源进程间必然需要交互,通过IPC,信号,信号量,消息队列,管道等进行交互。
2.动态创建进程的缺点:
一般我们是通过动态创建子进程(或者子线程)来实现并发服务器的,这样的缺点
(1)动态创建进程(或线程)比较耗费时间,这将导致较慢的客户响应
(2)动态创建的子进程通常只用来为一个客户服务,这样导致了系统上产生大量的细微进程(或线程)。进程和线程间的切换将消耗大量CPU时间
(3)动态创建的子进程是当前进程的完整映像,当前进程必须谨慎的管理其分配的文件描述符和堆内存等系统资源,否则子进程可能复制这些资源,从而使系统的可用资源急剧下降,进而影响服务器的性能。
3.进程池的优点:
进程池:有服务器预先创建的一组子进程,这些子进程的数目在3~10个之间,
4.进程池中的子进程:
a.他们都运行着相同的代码,具有相同的属性,比如优先级,PGID(组识别码)等。
b.进程池在服务器启动之初就创建好了,所以每个子进程都相对”干净”,即它们没有打开不必要的文件描述符(从父进程继承而来)
c.也不会错误地使用大块的堆内存(从父进程复制得到)
5.选择子进程为新任务服务的方式:
(1)主进程使用某种算法来主动选择子进程
(2)主进程和所有子进程通过一个共享的工作队列来实现同步
:子进程都睡眠在该工作队列上,当有新的任务到来时,主进程将任务添加到工作队列中。
这将唤醒正在等待任务的子进程,不过只有一个子进程将获得新任务的“接管权”,它可以从工作队列中取出任务并执行之,而其他子进程将继续睡眠在工作队列上。
6.需求:
主进程除了选择好子进程以外,还需要使用某种通知机制来告诉目标子进程有新任务需要处理,并传递必要的数据。
最简单的办法:在父子进程之间预先建立好一条管道,然后通过该管道来实现所有的进程间通信(预先定义好协议来规范管道的使用)
(父子线程间就可以直接用全局变量)
7.进程池代码实现:
每次请求都生成新进程其实必要性并不大,大部分并发服务器处理的每秒并发量一般最多就在几百左右,因此一般几个或者十几个进程循环提供服务就可以hold住,为了减少每次请求建立新进程的成本,我们的前辈又发明了多进程池(prefork)的模式,预先生成若干进程来处理请求。
1)tcp.server.c
#include<stdio.h>#include<stdlib.h>#include<sys/socket.h>#include<sys/types.h>#include<netinet/in.h>#include<sys/wait.h>#include<string.h>#include<time.h>#define MAX_CONNECTION 2int main(int argc, char** argv){ int fd = -1; time_t ticks; pid_t pid; pid_t pids[10];//后面将会一次产生十个子进程,存放每个子进程的id int port = 99999;//指定端口号为99999 //父进程创建监听套接字 fd = socket(AF_INET,SOCK_STREAM,0); //绑定套接字与ip地址和端口号 struct sockaddr_in addr; memset(&addr,0,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); int size = sizeof(struct sockaddr); bind(fd,(struct sockaddr*)&addr,size); //父进程监听客户端的请求 listen(fd,MAX_CONNECTION); //创建子进程 int i; for(i = 0; i< 10;i++) { pid = fork(); if(pid>0) { continue;//父进程继续循环创建子进程 } //子进程处理客户端的请求 pids[i] = pid; struct sockaddr_in client_addr; while (1) { memset(&client_addr, 0, sizeof (client_addr)); char buf[1024]; memset(&buf,0,sizeof(buf)); //创建new_socket来处理客户端的请求 int client_fd = accept(fd, (struct sockaddr *) &client_addr, &size); close(fd);//关闭监听文件(不必要的文件) ticks = time (0); //打印时间 snprintf(buf, sizeof(buf), "%s", ctime(&ticks));//将buf里的内容写到网络中 write(client_fd, buf, sizeof(buf)); sleep(3600); //客户端请求结束,关闭文件 close(client_fd); } } close(fd); //等待回收子进程 for(i = 0; i< 10;i++) { int status; if(pids[i] < 0) { continue; } waitpid(pids[i],&status,0); } return 0;}
2)tcp_client.c:
#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>#include<string.h>void static Usage(const char* proc){ printf("Usage: %s [local_ip] [local_port]/n",proc);}int main(int argc,char* argv[]){ if(argc!=3) { Usage(argv[1]); exit(1); } int sock = socket(AF_INET,SOCK_STREAM,0); if(sock < 0 ) { perror("socket"); exit(2); } struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(atoi(argv[2])); server.sin_addr.s_addr = inet_addr(argv[1]); if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0) { perror("connect"); exit(3); } char buf[1024]; while(1) { printf("Please Enter$"); fflush(stdout); ssize_t s = read(0,buf,sizeof(buf)-1); if(s>0) { buf[s-1] = 0; write(sock,buf,strlen(buf)); s = read(sock,buf,sizeof(buf)-1); if(s>0) { buf[s] = 0; printf("servetr echo#:%s/n",buf); } } return 0; }}
利用线程池与进程池可以使管理进程与线程的工作交给系统管理,不需要程序员对里面的线程、进程进行管理。
- 进程池调研
- Linux进程池、线程池调研
- 调研
- 调研
- 调研
- Android 5.0/6.0进程自动重启调研(5.0+)
- AIDL绑定到对方进程后,进程被杀后的现象调研
- 调研注意事项
- cfs调研
- 如何调研
- 中间件调研.
- JDK调研
- ERP调研
- collectd调研
- GIGA+调研
- Py4j调研
- CommonJS调研
- Entropy调研
- NAT的四种类型及类型检测
- pandas 的groupby函数
- charles抓取https请求-移动端
- AJAX和SpringMVC交互
- codeM初赛B轮A题
- 进程池调研
- Spring获取bean几种方式
- Java之集合框架图及 Collection和Collections的区别及用法总结
- excel 解析
- shiro+redis配置
- [转]Qt中使用GridLayout如何设置一个按钮占两个位置
- linux 下ssh 图形管理工具 remmina
- 先天性心脏病的宝宝治疗过程之路---值得一看
- Cordavo