进程池模型

简介:

进程池模型需要通过system V IPC机制或管道、信号、文件锁等进行同步。以下是进程池的一般模型。

               wKioL1XhTHnwwGnnAAEcmdVtU6Y290.jpg


Linux惊群现象:

惊群:惊群是指多个进程/线程在等待同一资源时,每当资源可用,所有的进程/线程都来竞争资源的现象。

accept、select、epoll实现进程池模型时的惊群现象:

    1).Linux多进程accept系统调用的惊群问题(注意,这里没有使用select、epoll等事件机制),在linux 2.6版本之前的版本存在,在之后的版本中解决掉了。

    2).使用select epoll等事件机制,在linux早期的版本中,惊群问题依然存在(epoll_create在fork之前)。 原因与之前单纯使用accept导致惊群,原因类似。Epoll的惊群问题,同样在之后的某个版本部分解决了。

    3).Epoll_create在fork之后调用,不能避免惊群问题,Nginx使用互斥锁,解决epoll惊群问题。

    进程池中的进程调用accept如果阻塞在同一个listen队列中,有可能产生惊群现象(取决于Linux版本):当一connect到达时,所有accept都会唤醒,但只有一个accept会返回正确结果,其他的都会返回错误码。

    accept多进程实现:让一个进程bind一个网络地址(可能是AF_INET,AF_UNIX或者其他任何你想要的),然后fork这个进程自己:


int s = socket(...)  

bind(s, ...)  

listen(s, ...)   

fork()  

Fork自己几次之后,每个进程阻塞在accept()函数这里

for(;;) {  

    int client = accept(...);  //子进程阻塞在这了  

    if (client < 0) continue;  

    ...    

}  

             wKioL1XhTaLg-yBwAAE6dY5WFD0255.jpg

在较老的unix系统中,当有连接到来时,accept()在每个阻塞在这的进程里被唤醒。但是,只有这些进程中的一个能够真正的accept这个连接,其他的进程accept将返回EAGAIN惊群造成结果是系统对用户进程/线程频繁的做无效的调度、上下文切换,系统系能大打折扣。


实现代码:

服务器端代码:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <sys/types.h>

#include <unistd.h>

#include <time.h>


#define BUFLEN 1024

#define PIDNUM 3


/*******************并发服务器模型之一:预先分配好了进程的个数**********************/


static void handle_fork(int sockfd){

    int newfd;

    struct sockaddr_in c_addr;

    char buf[BUFLEN];

    socklen_t len;

    time_t now;

    while(1){

        len = sizeof(struct sockaddr);

        if((newfd = accept(sockfd,(struct sockaddr*) &c_addr, &len)) == -1){

            perror("accept");        

            exit(errno);

        }else

        printf("\n*****************通信开始***************\n");

        printf("正在与您通信的客户端是:%s: %d\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));


        /******处理客户端请求*******/

        bzero(buf,BUFLEN);

        len = recv(newfd,buf,BUFLEN,0);

        if(len >0 && !strncmp(buf,"TIME",4)){

            bzero(buf,BUFLEN);

            /*获取系统当前时间*/

            now = time(NULL);

            /*ctime将系统时间转换为字符串,sprintf使转化后的字符串保存在buf*/

            sprintf(buf,"%24s\r\n",ctime(&now));

            //******发送系统时间*******/

            send(newfd,buf,strlen(buf),0);

        }

        /*关闭通讯的套接字*/

        close(newfd);

    }

}


int main(int argc, char **argv)

{

    int sockfd;

    struct sockaddr_in s_addr;

    unsigned int port, listnum;

    pid_t pid[PIDNUM];

    

    /*建立socket*/

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){

        perror("socket");

        exit(errno);

    }else

        printf("socket create success!\n");

    /*设置服务器端口*/    

    if(argv[2])

        port = atoi(argv[2]);

    else

        port = 4567;

    /*设置侦听队列长度*/

    if(argv[3])

        listnum = atoi(argv[3]);

    else

        listnum = 3;

    /*设置服务器ip*/

    bzero(&s_addr, sizeof(s_addr));

    s_addr.sin_family = AF_INET;

    s_addr.sin_port = htons(port);

    if(argv[1])

        s_addr.sin_addr.s_addr = inet_addr(argv[1]);

    else

        s_addr.sin_addr.s_addr = INADDR_ANY;

    /*把地址和端口帮定到套接字上*/

    if((bind(sockfd, (struct sockaddr*) &s_addr,sizeof(struct sockaddr))) == -1){

        perror("bind");

        exit(errno);

    }else

        printf("bind success!\n");

    /*侦听本地端口*/

    if(listen(sockfd,listnum) == -1){

        perror("listen");

        exit(errno);    

    }else

        printf("the server is listening!\n");

    /*处理客户端的连接*/

    int i = 0;

    for(i = 0; i < PIDNUM; i++){

        pid[i] = fork();

        if(pid[i] == 0)

            handle_fork(sockfd);    

    }

    /*关闭服务器的套接字*/

    close(sockfd);

    return 0;

}


客户端代码:



#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <sys/types.h>

#include <unistd.h>

#include <time.h>


#define BUFLEN 1024


int main(int argc, char **argv)

{

    int sockfd;

    struct sockaddr_in s_addr;

    socklen_t len;

    unsigned int port;

    char buf[BUFLEN];    

    

    /*建立socket*/

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){

        perror("socket");

        exit(errno);

    }else

        printf("socket create success!\n");


    /*设置服务器端口*/    

    if(argv[2])

        port = atoi(argv[2]);

    else

        port = 4567;

    /*设置服务器ip*/

    bzero(&s_addr, sizeof(s_addr));

    s_addr.sin_family = AF_INET;

    s_addr.sin_port = htons(port);

    if (inet_aton(argv[1], (struct in_addr *)&s_addr.sin_addr.s_addr) == 0) {

        perror(argv[1]);

        exit(errno);

    }

    /*开始连接服务器*/    

    if(connect(sockfd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr)) == -1){

        perror("connect");

        exit(errno);

    }else

        printf("conncet success!\n"); 

    /******缓冲区清零*******/    

    bzero(buf,BUFLEN);

    strcpy(buf,"TIME");

    /******发送消息*******/

    send(sockfd,buf,strlen(buf),0);

    /******缓冲区清零*******/

    bzero(buf,BUFLEN);

    /******接收消息*******/

    len = recv(sockfd,buf,BUFLEN,0);

    if(len > 0)

        printf("服务器的系统时间是:%s\n",buf);

    close(sockfd); /*关闭连接*/

    return 0;

}




本文转自 a_liujin 51CTO博客,原文链接:http://blog.51cto.com/a1liujin/1689523,如需转载请自行联系原作者
相关文章
|
3月前
|
Java C++
线程池-手写线程池C++11版本(生产者-消费者模型)
线程池-手写线程池C++11版本(生产者-消费者模型)
62 0
|
8月前
|
安全 Linux 数据安全/隐私保护
【Linux线程同步】生产者消费者模型
【Linux线程同步】生产者消费者模型
76 0
|
8月前
|
缓存 Linux 数据安全/隐私保护
Linux线程的生产者消费者模型 --- 阻塞队列(blockqueue)(一)
Linux线程的生产者消费者模型 --- 阻塞队列(blockqueue)(一)
71 0
|
8月前
|
Linux
Linux线程的生产者消费者模型 --- 阻塞队列(blockqueue)(二)
Linux线程的生产者消费者模型 --- 阻塞队列(blockqueue)(二)
62 0
|
9月前
|
缓存 前端开发 Java
线程池中线程重用导致的问题
之前在公司做的一个项目中,有一个 core 的公共依赖包,那个依赖里面简单封装了用户的信息。
|
11月前
|
负载均衡
进程池设计
本文介绍了当要求父进程作为写端,需要多个子进程作为读端去读取父进程往匿名管道写入的数据,并拿着数据去完成一些任务,这样一项微进程池设计。
78 0
|
12月前
|
Java 程序员 数据库连接
吓我一跳?看了线程和线程池的对比,才知道池化技术到底有多牛
情商高的人是能洞察并照顾到身边所有人的情绪,而好的文章应该是让所有人都能看懂。 尼采曾经说过:人们无法理解他没有经历过的事情。因此我会试着把技术文章写的尽量具象化一些,力求让所有人都能看懂,所以在正式开始之前,我们先从两个生活事例说起。
|
12月前
|
存储 缓存 前端开发
Web性能优化之Worker线程(下)
{服务工作线程|Service Worker} 基础概念 ⭐️⭐️⭐️ 线程缓存 ⭐️⭐️⭐️⭐️ 线程客户端 生命周期 ⭐️⭐️⭐️ 控制反转与线程持久化 updateViaCache 管理服务文件缓存 ⭐️⭐️⭐️ 线程消息 ⭐️⭐️⭐️ 拦截 fetch 事件 ⭐️⭐️⭐️⭐️⭐️
160 0
|
12月前
|
Web App开发 消息中间件 JavaScript
Web性能优化之Worker线程(上)
Worker 线程简介 {专用工作线程|Dedicated Worker} 专用工作线程 + Webpack {共享工作线程| Shared Workers }
419 0
|
Web App开发 JavaScript API
进程模型
进程模型
69 0
进程模型