进程池模型

简介:

进程池模型需要通过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,如需转载请自行联系原作者
相关文章
|
Kubernetes Java Nacos
nacos常见问题之通过helm方式部署设置开启授权认证功能如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
996 0
|
3月前
|
Prometheus Kubernetes 监控
Kubernetes技巧:使用Prometheus监控Pod性能指标。
记住,监控和可视化是维持健康集群的必备工具,而Prometheus加上Grafana就是这个任务上的黄金搭档。安装这两位侦探后,你的Kubernetes集群将会像一个经过精心维护的庞大机器一样,高效、有序地运转。
468 21
|
10月前
|
应用服务中间件 网络安全 数据安全/隐私保护
【赵渝强老师】使用Docker Machine远程管理Docker
Docker Machine 是 Docker 官方提供的远程管理工具,可帮助开发人员在远程主机或虚拟机上安装和管理 Docker 环境。本文介绍了如何在远程主机上安装 Docker 并配置免密码登录,以及使用 Docker Machine 管理远程 Docker 主机的常用命令。
254 0
|
11月前
|
Kubernetes API 调度
k8s学习--pod的所有状态详解(图例展示)
k8s学习--pod的所有状态详解(图例展示)
1174 1
|
10月前
|
存储 网络安全
Curl error (60): SSL peer certificate or SSH remote key was not OK for https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/x86_64/repodata/repomd.xml [SSL: no alternative certificate subject name matches target host name 'update.cs2c.com.cn']
【10月更文挑战第30天】在尝试从麒麟软件仓库(ks10-adv-os)下载元数据时,遇到 SSL 证书验证问题。错误提示为:`Curl error (60): SSL peer certificate or SSH remote key was not OK`。可能原因包括证书不被信任、证书与域名不匹配或网络问题。解决方法包括检查网络连接、导入 SSL 证书、禁用 SSL 证书验证(不推荐)、联系仓库管理员、检查系统时间和尝试其他镜像。
2955 1
|
调度
【Cron表达式】cron表达式详细介绍以及常用的例子
【Cron表达式】cron表达式详细介绍以及常用的例子
4630 2
|
关系型数据库 MySQL Linux
如何在Linux上搭建本地Docker Registry镜像仓库并实现公网访问
如何在Linux上搭建本地Docker Registry镜像仓库并实现公网访问
621 0
|
存储 Docker 容器
记录Docker搭建私有仓库的步骤教程
记录Docker搭建私有仓库的步骤教程
798 0
|
传感器 算法 Linux
查看 PCD 点云 windows
在Linux系统查看PCD 点云有许多方法,但发现在windows下的工具比较少,这里分享两个思路,一个是使用MATLAB工具编程,另一个是下载CloudCompare软件进行查看点云。
11831 0
查看 PCD 点云 windows
|
Web App开发 移动开发 前端开发
Selenium+Python3之:多线程进行跨浏览器测试
Selenium+Python3之:多线程进行跨浏览器测试
412 1
Selenium+Python3之:多线程进行跨浏览器测试