进程池

进程池应用至少包括两部分

   资源进程:预先创建好的空闲进程,管理进程会把工作分给空闲进程来处理

   管理进程:管理进程负责创建资源进程,把工作分给空闲资源进程,回收已经完成工作的资源进程。

进程池的实现

      让服务器在启动阶段调用fork创建一个子进程池,通过子进程来处理客户端请求。子进程与父进程之间使用socketpair进行通信(为了方便使用sendmsgrecvmsg,如果使用匿名管道,则无法使用以上两个函数)。以下针对TCP进行分析.

     server端使用select轮询用于监听客户端请求的被动套接字fd_listen以及用于父子之间通信的socketpair。每当客户端有请求时,server端会将由accept返回的用于与客户端通信的socket描述符通过socketpair发送给一个空闲的子进程,由子进程与客户端进行通信(处理请求)。因此服务器端需要维护一个子进程队列,队列中的每个元素存放着与子进程通信的socketpair以及标记子进程是否空闲的标志位,如下:

Tyepdef  struct  tag_chd

{

int s_sfd;      //与子进程通信的socketpair描述符

Int s_state;    //标记子进程是否空闲

}NODE, *pNODE;

每当子进程处理完客户端请求时,会通过socketpairserver端发送消息,serverselect到该socketpair后,会将对应子进程标志位设置为空闲。

注意

1. 由于父进程是先创建子进程,之后才accept用于与客户端通信的socket描述符fd_client,因此子进程的pcb中并没有fd_client的信息。server端需要将fd_client发送子进程。如果只是用send来发送fd_client信息的话,子进程只会将其当成一个整型数。我们需要用sendmsgfd_client连同其辅助(控制)信息一并发送,这样子进程才会将其当成一个socket描述符。

2. 父进程预先创建子进程池,该子进程如同server端一样是永远不会退出的。子进程中使用while死循环,如下:

while(1){

     readn = read(sfd, &flag, 4);       // 服务器分配的子进程在子进程队列中的下标

     printf("readn: %d \n", readn);             

     printf("read from father: %d \n", flag);

     recv_fd(sfd, &fd_client);  // recv_fd中封装了recvmsg,接收与客户端通信的socket

//描述符

     handle_request(fd_client);                  // 处理客户端请求

     write(sfd, &pid, sizeof(pid));  // 处理完请求后通过socketpair通知服务器,服务器

                              //将该子进程设置为空闲

    }

每当子进程处理完一个客户端请求后(也就是客户端退出了),子进程会阻塞在 read 处,等待接收下一个客户端请求。

由于是while死循环,且死循环中没有break语句,因此子进程不可能跳出这个while循环,也就不会执行while循环以下的内容了,这样可以保证子进程结尾没有exit也不会执行之后的内容。

 

 

 

代码实现:

Server.h

#ifndef __SERVER_H__

#define __SERVER_H__

#include "my_socket.h"

#include <sys/stat.h>

#include <sys/types.h>

#include <fcntl.h>

#include <sys/time.h>

#include <sys/select.h>

#include <sys/uio.h>

#include <sys/wait.h>

#include <errno.h>

#define SER_IP "127.0.0.1"

#define SER_PORT 8888

#define ST_BUSY 1

#define ST_IDLE 2

#define SIZE 8192

#define MSG_SIZE (SIZE - 4)

 

typedef struct tag_mag

{

    int msg_len ;

    char msg_buf[MSG_SIZE];//8188

}MSG, *pMSG;

 

typedef struct tag_chd

{

    int s_sfd ;

    int s_state ;

}NODE, *pNODE;

 

extern int errno ;

void make_child(pNODE arr, int cnt);

void child_main(int sfd) ;

void handle_request(int sfd);

void send_fd(int sfd, int fd_file) ;

void recv_fd(int sfd, int* fd_file) ;

void dispatch(pNODE arr, int cnt, int fd_client);

#endif

 

 

Server.c

#include "server.h"

void make_child(pNODE arr, int cnt)

{

    int index ;

    for(index = 0; index < cnt; index ++)

    {

        pid_t pid ;

        int fds[2] ;//fds[0] - c  fds[1] - p

        socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);

        pid = fork() ;

        if(pid == 0)// child

        {

            close(fds[1]);         /* 子进程用fds[0],关闭fds[1] */

            child_main(fds[0]) ;   /* 每创建一个子进程,子进程就进入该函数中(死循环),接收请求,处理请求,如此循环。*/

 

        }else

        {

            /* 初始化进程池队列中的每一个子进程 */

            arr[index].s_sfd = fds[1] ;

            arr[index].s_state = ST_IDLE ;

            close(fds[0]);         /* 父进程用fds[1], 关闭fds[0] */

        }

 

    }

 

}

void child_main(int sfd)

{

    int fd_client ;

    int flag ;

    int readn ;

    pid_t pid = getpid();

    while(1)

    {

        readn = read(sfd, &flag, 4);

        printf("readn: %d \n", readn);

        printf("read from father: %d \n", flag);

        recv_fd(sfd, &fd_client);//从父进程中读出客户端的描述符

        handle_request(fd_client);

        write(sfd, &pid, sizeof(pid));

    }

}

void handle_request(int sfd)

{    

 

    MSG my_msg ;

    int recvn ;

    while(1)

    {

        memset(&my_msg, 0, sizeof(MSG));

        my_recv(&recvn, sfd, &my_msg, 4);

        if(my_msg.msg_len  == 0)//如果客户端退出

        {

            break ;

        }

        my_recv(NULL, sfd, my_msg.msg_buf, my_msg.msg_len);

        my_send(NULL, sfd, &my_msg, my_msg.msg_len + 4);

 

    }

 

}
//父进程传文件描述符给子进程

void send_fd(int sfd, int fd_file)

{

    struct msghdr my_msg ;

    memset(&my_msg, 0, sizeof(my_msg));

    

    struct iovec bufs[1] ;

    char buf[32] = "hello world ! \n";

    bufs[0].iov_base = buf ;

    bufs[0].iov_len = strlen(buf) ;

    

    my_msg.msg_name = NULL ;

    my_msg.msg_namelen = 0 ;

    my_msg.msg_iov = bufs ;

    my_msg.msg_iovlen = 1 ;

    my_msg.msg_flags = 0 ;

 

    struct cmsghdr *p  ;

    int cmsg_len = CMSG_LEN(sizeof(int)) ;     /* 所传为文件描述符,因此sizeof(int) */

    p = (struct cmsghdr*)calloc(1, cmsg_len) ;

    p -> cmsg_len = cmsg_len ;

    p -> cmsg_level = SOL_SOCKET ;

    p -> cmsg_type = SCM_RIGHTS ;

    *(int*)CMSG_DATA(p) = fd_file ;

    

    my_msg.msg_control = p ;

    my_msg.msg_controllen = cmsg_len ;

    

    int sendn ;

    sendn = sendmsg(sfd, &my_msg, 0);

    printf("send masg len : %d \n", sendn);

}

void recv_fd(int sfd, int* fd_file)

{

    struct msghdr my_msg ;

    

    struct iovec bufs[1] ;

    char buf1[32]="" ;

    bufs[0].iov_base = buf1 ;

    bufs[0].iov_len = 31 ;

 

    my_msg.msg_name = NULL ;

    my_msg.msg_namelen = 0 ;

    my_msg.msg_iov = bufs ;

    my_msg.msg_iovlen = 2 ;

    my_msg.msg_flags = 0 ;

    

    struct cmsghdr *p  ;

    int cmsg_len = CMSG_LEN(sizeof(int)) ;

    p = (struct cmsghdr*)calloc(1, cmsg_len) ;

    my_msg.msg_control = p ;

    my_msg.msg_controllen = cmsg_len ;

    

    int recvn ;

    recvn = recvmsg(sfd, &my_msg, 0);

    

    *fd_file = *(int*)CMSG_DATA((struct cmsghdr*)my_msg.msg_control); //写成

                              //*(int*)CMSG_DATA(P)也可

    printf("buf1: %s, recv msg len : %d   \n", buf1, recvn);

 

}

void dispatch(pNODE arr, int cnt, int fd_client)

{

    int index ;

    for(index = 0 ; index < cnt; index ++)

    {

        if(arr[index].s_state == ST_IDLE)

        {

            write(arr[index].s_sfd, &index, 4);

            send_fd(arr[index].s_sfd, fd_client); /* 向空闲的子进程分配任务,将服务器

accept返回的socket描述符发送给子进程*/

            arr[index].s_state = ST_BUSY ;

            break ;

        }

    }

}

 

client.c

#include "my_socket.h"

#define MY_IP "127.0.0.1"

#define MY_PORT 6666

#define SER_IP "127.0.0.1"

#define SER_PORT 8888

#define SIZE 8192

#define MSG_SIZE (SIZE - 4)

typedef struct tag_mag//

{

    int msg_len ;

    char msg_buf[MSG_SIZE];//8188

}MSG, *pMSG;

int main(int argc, char* argv[])

{

    int sfd ;

    my_socket(&sfd, MY_TCP, MY_IP, atoi(argv[1]));

    my_connect(sfd, SER_IP, SER_PORT);

    MSG my_msg ;

    while(memset(&my_msg, 0, sizeof(MSG)), fgets(my_msg.msg_buf, MSG_SIZE, stdin)!= NULL)

    {

        my_msg.msg_len = strlen(my_msg.msg_buf);

        my_send(NULL, sfd, &my_msg, 4 + my_msg.msg_len );

        memset(&my_msg, 0, sizeof(MSG));

        my_recv(NULL, sfd, &my_msg, 4);

        my_recv(NULL, sfd, &my_msg.msg_buf, my_msg.msg_len);

        printf("recv from server : %s \n", my_msg.msg_buf);

    

    }

    /* 客户端退出时,向服务器发送一个长度为0的消息 ,用于通知服务器退出 */

    memset(&my_msg, 0, sizeof(MSG));

    my_send(NULL, sfd, &my_msg, 4 + my_msg.msg_len);

    close(sfd);

 

}

main.c

 

#include "server.h"

int main(int argc, char* argv[])//exe chld_cnt

{

    if(argc != 2)

    {

        printf("Usage: exe , child_cnt! \n");

        exit(1);

    }

    int child_cnt = atoi(argv[1]);

    pNODE arr_child = (pNODE)calloc(child_cnt, sizeof(NODE)) ; /* 动态数组维护子进程池 */

    make_child(arr_child, child_cnt);

    

    int fd_listen, fd_client ;

    my_socket(&fd_listen, MY_TCP, SER_IP, SER_PORT);

    my_listen(fd_listen, 10);

    

    fd_set readset, readyset ;

    FD_ZERO(&readset);

    FD_ZERO(&readyset);

    FD_SET(fd_listen, &readset);//先将监听客户端的描述符设置到监听集合

    int index ;

    for(index = 0; index < child_cnt; index ++)

    {

        FD_SET(arr_child[index].s_sfd, &readset);//再将和子进程的通信的socket设置到监听集合

    }

    

    int select_ret ;

    struct timeval tm ;

    while(1)

    {

        tm.tv_sec = 0 ;

        tm.tv_usec = 1000 ;

        readyset = readset ;

        select_ret = select(1024, &readyset, NULL, NULL, &tm);

        if(select_ret == 0)        /* 轮询时间内,所有描述符均没有活动,返回0,继续轮询 */

        {

            continue ;

        }else if(select_ret == -1) /* 信号 */

        {

            if(errno == EINTR)

            {

                continue ;

            }else

            {

                exit(1);

            }

        }else

        {

            if(FD_ISSET(fd_listen, &readyset))//如果是客户端发来的信号

            {

            fd_client = accept(fd_listen, NULL, NULL) ;    

            dispatch(arr_child, child_cnt ,fd_client);

            close(fd_client);

            }

            for(index = 0; index < child_cnt; index ++)

            {

                if(FD_ISSET(arr_child[index].s_sfd, &readyset))//是子进程发来的信号

                {

                    int val ;

                    read(arr_child[index].s_sfd, &val, 4);

                    arr_child[index].s_state = ST_IDLE ;

                }

            }

            

        }

        

    }   

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值