进程池调研

来源:互联网 发布:电子商务属于软件信息 编辑:程序博客网 时间: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;       }}

利用线程池与进程池可以使管理进程与线程的工作交给系统管理,不需要程序员对里面的线程、进程进行管理。

原创粉丝点击