Go最全简 易 版 的 进 程 池 模 型 学 习(2),膜拜

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

简 易  版 的 进 程 池 模  型 学 习


1、进程池流程

父进程流程

第一步:

make_child 初始化子进程

循环创建子进程,并初始化父进程的子进程管理结构体数组 child, 通过 socket_pair 将 socket
描述符一端放入数组

子进程流程

recv_fd 等待父进程发送任务

send_file 发送文件数据

Write 向父进程发送完成任务

第二步:

父进程 epoll 监控 fd_listen 描述符。

父进程 epoll 监控 parr 结构体数组的 socket 描述符

第三步:

While 1 启动 epoll_wait, 等待是否有客户端连接

有客户端连接后, accept 获得描述符, 循环找到非忙碌的子进程, 并发送给子进程, 标记对

应子进程忙碌。

当子进程完成任务后, 父进程一旦监控 socket 描述符可读, 代表子进程非忙碌, 然后标记子

进程非忙碌。

2、头文件及相关数据结构

#include<sys/stat.h>
#include <sys/stat.h>
#include<errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include<sys/wait.h>

#define FILENAME "file"

typedef struct{                //维护子进程的数据结构
    pid_t pid;
    int tfds;
    short busy;
}child,*pchild;     


typedef struct{               //网络数据发送的数据结构
    int len;
    char buf[1000];
}train,*ptrain;

void send_fd(int fdw,int fd);
void recv_fd(int fdr,int* fd);
void make_child(pchild p,int len);
void child_handle(int fdr);
void send_data(int );
int send_n(int ,char *,int);
int recv_n(int,char *,int);

3、父进程源代码

#include"func.h"

int exitfds[2];       //退出拉起管道
int exit_num=0;       //用于退出时判断机制

void set_status(int fd)
{
    int status=fcntl(fd,F_GETFL);
    status=status|O_NONBLOCK;
    fcntl(fd,F_SETFL,status);
}

void sighandle(int signum)
{
    write(exitfds[1],"over",4);
}

int main(int argc,char **argv)
{   
    pipe(exitfds);
    signal(SIGINT,sighandle);
    if(4!=argc)
    {
        perror("error argcs!\nplease enter IP  SOCKET  proccess_num\n");
        return -1;
    }
    int child_len=atoi(argv[3]);
    //首先为每个子进程动态申请空间,用指针实现数组效果
    pchild p=(pchild)calloc(child_len,sizeof(child)); 
    //创建子进程,并初始化数据结构,//封装成一个Init函数
    make_child(p,child_len);
     int sfd;
    sfd=socket(AF_INET,SOCK_STREAM,0);
    if(sfd==-1)
    {
        perror("socket");
        return -1;
    }
    struct sockaddr_in ser;
    ser.sin_addr.s_addr=inet_addr(argv[1]);
    ser.sin_port=htons(atoi(argv[2]));
    ser.sin_family=AF_INET;
    int ret;
    ret=bind(sfd,(struct sockaddr *)&ser,sizeof(ser));
    if(-1==ret)
    {
        perror("bind");
        return -1;
    }
    int epfd=epoll_create(1);     //创造一个epfd的句柄
    struct epoll_event event,*evs;
    evs=(struct epoll_event *)calloc(child_len+1,sizeof(struct epoll_event));
    memset(&event,0,sizeof(event));
    event.data.fd=sfd;
    event.events=EPOLLIN;
    epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
    int i;
    for(i=0;i<child_len;i++)     //监管注册每个子进程的描述符管道
    {
        event.data.fd=p[i].tfds;
        event.events=EPOLLIN;
        epoll_ctl(epfd,EPOLL_CTL_ADD,p[i].tfds,&event);
    }
    listen(sfd,child_len);
    int j,ret1,newfd;
    char flag[6];
    static int once=0;
    set_status(exitfds[0]);
    while(1)
    {
        ret=read(exitfds[0],flag,sizeof(flag));
        if(ret>0)
        {
            if(0==exit_num)
            {
                printf("exerting quit action--please wait····\n");
            event.data.fd=sfd;       //不在接受连接
            event.events=EPOLLIN;
            epoll_ctl(epfd,EPOLL_CTL_DEL,sfd,&event);
            for(i=0;i<child_len;i++)
            {
                printf("正在处理子进程p[i].pid=%d  ",p[i].pid);
                printf("\n");
                kill(p[i].pid,SIGKILL);
            }
                wait(NULL);
                printf("you have quited\n");
                return(0);
       
            }else{
                   printf("请等待,有客户正在交易···\n"); 
            }
        }
        ret1=epoll_wait(epfd,evs,child_len+1,-1);
        for(i=0;i<ret1;i++)
        {                   //妈的,又他妈犯这种错误了
            if(evs[i].data.fd==sfd)      //网络请求到达
            {
                newfd=accept(sfd,NULL,NULL);
                //找到一个空闲的子进程,并且将newfd发送给它
                for(j=0;j<child_len;j++)
                {
                    if(p[j].busy==0)
                    {
                        //通过tcp管道发送newfd给子进程
                        send_fd(p[j].tfds,newfd); 
                        exit_num++;
                        p[j].busy=1;
                        printf("find a not busy proccess,send newfd success!\n");
                        break;
                    }
                }
                close(newfd);       //关闭父进程对newfd的引用计数
            }
            //监听子进程的tcp管道的发送完成信息
            for(j=0;j<child_len;j++)              
            {
                if(evs[i].data.fd==p[j].tfds)
                {
                    //read(p[j].tfds,&flag,sizeof(flag));
                    char buf[15]={0};
                    read(p[j].tfds,buf,sizeof(buf));
                    printf("%s\n",buf);
                    //read(p[j].tfds,&flag,sizeof(flag));
                    p[j].busy=0;
                    exit_num--;
                    printf("child proccess %d is not busy!\n",j);
                    if(0==exit_num)
                    {
                        printf("Are you stall want to finish this deal,please push Ctrl+C\n");

                    }
                }
            }

        }
        
    } 
}

4、子进程流程代码

#include"func.h"


/*首先,要创建len个子进程,就会用到fork( )函数,于是,必须把父进程和子进程分别开来,一个父进程,len个子进程。
其次,初始化是针对定义的数据结构初始化,有:
1、每个子进程在fork创建之后需要根据返回值保存自己的pid.
2、对于全双工的TCP管道,需要用socketpair( )函数初始化
3、刚开始时,将其每个子进程的busy位置为0,表示空闲
对于子进程,必须让它在循环里面持续发送,从父进程接收任务,父进程将到发送数据
,再到发送完,通知父进程,可以通过写一个flag,然后父进程将其busy改为0,
表示空闲。
*/
void child_handle(int fdr)
{
    int newfd;
    char flag[6]={0};
    strcpy(flag,"over");
    while(1)
    {
        recv_fd(fdr,&newfd);
        send_data(newfd);
        printf("I am child,send success!\n");
        write(fdr,&flag,sizeof(flag));
        //write(fdr,"hello",5);
    }
}

void make_child(pchild p,int len)
{
    int fds[2],i,ret;
    pid_t pid;
    for(i=0;i<len;i++)
    {
        ret=socketpair(AF_LOCAL,SOCK_STREAM,0,fds);
        pid=fork();
        if(0==pid)
        {
            close(fds[1]);
            child_handle(fds[0]);
            //p[i].tfds=fds[0]; 将fds[0]端留给子进程
        }   //让其子进程在里面循环,不出来
        close(fds[0]);
        p[i].pid=pid;       //获取子进程的pid
        p[i].busy=0;        //置为空闲
        p[i].tfds=fds[1];   //将管道的一端传递给主进程
    }
}

5、父进程向子进程发送套接字文件描述符(内核信息)

#include "func.h"

void send_fd(int fdw,int fd)
{
	struct msghdr msg;
	memset(&msg,0,sizeof(msg));
	struct cmsghdr *cmsg;
	int len=CMSG_LEN(sizeof(int));
	cmsg=(struct cmsghdr *)calloc(1,len);
	cmsg->cmsg_len=len;
	cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
	*(int*)CMSG_DATA(cmsg)=fd;
	msg.msg_control=cmsg;
	msg.msg_controllen=len;
	char buf1[10]="hello";
	char buf2[10]="world";
	struct iovec iov[2];
	iov[0].iov_base=buf1;
	iov[0].iov_len=5;
	iov[1].iov_base=buf2;
	iov[1].iov_len=5;
	msg.msg_iov=iov;
	msg.msg_iovlen=2;
	int ret=sendmsg(fdw,&msg,0);
	if(-1==ret)
	{
		perror("sendmsg");
		return;
	}
}
void recv_fd(int fdr,int* fd)


![img](https://img-blog.csdnimg.cn/img_convert/b3aa88fa47c7d3f5dc4e79957bd87462.png)
![img](https://img-blog.csdnimg.cn/img_convert/75e0fa6d4686583d00d18ee8c1f31b1f.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618658159)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

=2;
	int ret=sendmsg(fdw,&msg,0);
	if(-1==ret)
	{
		perror("sendmsg");
		return;
	}
}
void recv_fd(int fdr,int* fd)


[外链图片转存中...(img-6QOG8DmD-1715830001019)]
[外链图片转存中...(img-rrWAb9kk-1715830001019)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618658159)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值