【进程池封装】实现简单回射服务器


【进程池封装】实现简单回射服务器_第1张图片
图.1 进程池框架图
半同步/半异步模式

进程池类定义

class Process
{
public:
	Process() : m_pid(-1) {}
public:
	pid_t m_pid;
	int m_pipefd[2];
};

template< typename T >
class ProcessPool
{
private:
	ProcessPool(int listenfd, int process_number = 4);

public:
	/* 保证创建唯一一个进程池,singleton模式 */
	static ProcessPool<T>* Create(int listenfd, int process_number = 4)
	{
		if (!m_instance)
		{
			m_instance = new ProcessPool<T>(listenfd, process_number);
		}
		return m_instance;
	}
	void Run();

private:
	void setup_sig_to_pipe();
	void run_parent();
	void run_child();

private:
	static const int MAX_PROCESS_NUMBER = 16;
	static const int USER_PER_PROCESS = 65536;
	static const int MAX_EVENT_NUMBER = 1024;

	int m_process_number;
	int m_index;
	int m_epollfd;
	int m_listenfd;
	bool m_stop;
	/* save sub process info*/
	Process *m_sub_process;
       /* static变量,实现singleton模式 */
	static ProcessPool<T> *m_instance;
};

为了这个类只被实例化一次,使用了singleton模式(调用create实例化)。
构造函数创建进程,和unix套接字,用户父子进程的通信:
template<typename T>
ProcessPool<T>::ProcessPool(int listenfd, int process_number)
	: m_listenfd(listenfd), m_process_number(process_number), m_index(-1),
	  m_stop(false)
{
	assert((process_number > 0) && (process_number < MAX_PROCESS_NUMBER));
	m_sub_process = new Process[process_number];
	assert(m_sub_process);

	/* 创建子进程,并且父子进程间通过unix域套接字通信 */
	for (int i = 0; i < process_number; ++i)
	{
		int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, m_sub_process[i].m_pipefd);
		assert(ret == 0);
		m_sub_process[i].m_pid = fork();

		if (m_sub_process[i].m_pid == 0)
		{
			close(m_sub_process[i].m_pipefd[0]);
			m_index = i;
			break;
		}	
		else if (m_sub_process[i].m_pid > 0)
		{	
			close(m_sub_process[i].m_pipefd[1]);
		}	
	} 

}

添加信号的处理函数,使 当进程接受到信号时(kill/子进程死亡等),通过unix套接字传送给进程(进程在epoll注册了该unix域套接字):
template<typename T>
void ProcessPool<T>::setup_sig_to_pipe()
{
	m_epollfd = epoll_create(256);
	assert(m_epollfd != -1);
	int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sig_pipefd);
	assert(ret != -1);

	/* 对于父进程、子进程和所有其它进程,来自外界的信号都通过unix套接字传送到进程 */
	gSetNonblocking(sig_pipefd[1]);
	gAddfd(m_epollfd, sig_pipefd[0]);

	gAddSig(SIGCHLD, gSigHandler);
	gAddSig(SIGTERM, gSigHandler);
	gAddSig(SIGINT, gSigHandler);
	gAddSig(SIGPIPE, SIG_IGN);
}
父子进程代码区分界:
template<typename T>
void ProcessPool<T>::Run()
{
	if (m_index != -1)
	{
		printf("child run\n");
		this -> run_child();
		
		return ;
	}
	printf("parent run\n");
	this -> run_parent();
	
}

子进程执行区代码,当有新的连接请求时,父进程会通过unix域套接字告诉子进程:
template<typename T>
void ProcessPool<T>::run_child()
{
	this->setup_sig_to_pipe();

	/* 每个子进程都往epoll内核注册此套接字,当有新的连接时,父进程将以此套接字和子进程通信 */
	int pipefd = m_sub_process[m_index].m_pipefd[1];
	gAddfd(m_epollfd, pipefd);

	epoll_event event[MAX_EVENT_NUMBER];

	/* 服务类型的类,根据下文,T类必须实现init、process函数 */
	T *users = new T[USER_PER_PROCESS];
	assert(users);
	int number = 0;
	int ret = -1;
	int wu = 1;

	while (!m_stop)
	{

		number = epoll_wait(m_epollfd, event, MAX_EVENT_NUMBER, -1);
		if (number < 0 && errno != EINTR)
		{
			printf("epoll failure\n");
			break;
		}	

		for (int i = 0; i < number; ++i)
		{
			int sockfd = event[i].data.fd;
			/* EPOLLIN事件来自父子进程通信的unix套接字,说明有新的用户连接服务器 */
			if (sockfd == pipefd && (event[i].events & EPOLLIN))
			{
				printf("child event come.\n");
				int client;
				ret = recv(sockfd, (char *)&client, sizeof(int), 0);
				if (ret < 0 && errno != EAGAIN || ret == 0)
					continue;
				struct sockaddr_in cliaddr;
				socklen_t clilen = sizeof(sockaddr_in);
				int connfd = accept(m_listenfd, (struct sockaddr *)&cliaddr, &clilen);
				printf("connect sock fd:%d\n", connfd);

				if (connfd < 0)
				{
					printf("errno is -> %d:%s\n", errno, strerror(errno));
					continue;
				}
				gAddfd(m_epollfd, connfd);
				users[connfd].Init(m_epollfd, connfd, cliaddr);
				

			} /* 来子外界的信号,如在终端输入kill -signal PID给此进程时 */
			else if (sockfd == sig_pipefd[0] && (event[i].events & EPOLLIN))
			{
				int sig;
				int signals[256];
				ret = recv(sig_pipefd[0], (char *)signals, sizeof(signals), 0);
			
				if (ret <= 0)
					continue;
				
				for (int i = 0; i < ret; ++i)
				{
					switch (signals[i])
					{
						case SIGCHLD:
						{
							pid_t pid;
							int state;
							while ((pid = waitpid(-1, &state, WNOHANG)) > 0)
							{
								continue;
							}
							break;
						}
						case SIGTERM:
						case SIGINT:
						{
							m_stop = true;
							break;
						}
						default:
							break;
					}
				} 
			}/* 已经连接的用户发送请求的数据到达 */
			else if (event[i].events & EPOLLIN)
			{
				
				//printf("[Test LT]Does't process, epoll_event size:%d\n", number);
				//continue;
				
				users[sockfd].Process();
			}
			else
				continue;
		}	
	}
	delete [] users;
	users = NULL;
	close(pipefd);
	close(m_listenfd);
	close(m_epollfd);
	close(sig_pipefd[0]);   //close by create process
	close(sig_pipefd[1]);
	
}

父进程负责,listen套接字、死亡时要先将子进程杀死等:
template<typename T>
void ProcessPool<T>::run_parent()
{
	this->setup_sig_to_pipe();

	gAddfd(m_epollfd, m_listenfd);

	epoll_event event[MAX_PROCESS_NUMBER];
	int sub_process_counter = 0;
	int new_conn = 1;
	int number = 0;
	int ret = -1;


	while (!m_stop)
	{
		number = epoll_wait(m_epollfd, event, MAX_EVENT_NUMBER, 0);
		if (number < 0 && errno == EAGAIN)
		{
			printf("epoll_wait error -> %d:%s", errno, strerror(errno));
			break;
		}	

		for (int i = 0; i < number; ++i)
		{
			int sockfd = event[i].data.fd;
			/* 有新的连接,用轮询算法将accept等剩余工作递交给子进程 */
			if (sockfd == m_listenfd)
			{
				int idx = sub_process_counter;
				do
				{
					if (m_sub_process[idx].m_pid != -1)
						break;
					idx = (idx + 1)%m_process_number;
				}while (idx != sub_process_counter);

				if (m_sub_process[idx].m_pid == -1)
				{
					m_stop = true;
					break;
				}

				sub_process_counter = (sub_process_counter + 1)%m_process_number;
				printf("Send request to child [%d]\n", m_sub_process[idx].m_pid);
				send(m_sub_process[idx].m_pipefd[0], (char *)&new_conn, sizeof(new_conn), 0);
				

			} /* 有来自外界的信号 */
			else if (sockfd == sig_pipefd[0] && (event[i].events & EPOLLIN))
			{
				int sig;
				int signals[256];
				ret = recv(sockfd, (char *)&signals, sizeof(signals), 0);
				if (ret <= 0)
					continue;
				for (int i = 0; i < ret; ++i)
				{
					switch(signals[i])
					{
						case SIGCHLD:
						{
							pid_t pid;
							int state;
							while ((pid = waitpid(-1, &state, WNOHANG)) > 0)
							{
								for (int i = 0; i < m_process_number; ++i)
								{
									if (m_sub_process[i].m_pid == pid)
									{
										printf("child [%d] go die\n", pid);
										close(m_sub_process[i].m_pipefd[0]);
										m_sub_process[i].m_pid = -1;
									}
								}
							}
							m_stop = true;
							for (int i = 0; i < m_process_number; ++i)
							{
								if (m_sub_process[i].m_pid != -1)
									m_stop = false;
							}
							break;
						}
						case SIGINT:
						case SIGTERM:
						{
							printf("kill all child now\n");
							for (int i = 0; i < m_process_number; ++i)
							{
								if (m_sub_process[i].m_pid != -1)
								{
									kill(m_sub_process[i].m_pid, SIGTERM);
									close(m_sub_process[i].m_pipefd[0]);
								}
							}
							break;
						}
						default:
							break;
					}
				}
			}else 
				continue;

		}	
	}
	close(m_listenfd);
	close(m_epollfd);
	close(sig_pipefd[0]);
	close(sig_pipefd[1]);
	
}


注意,要实现什么服务,自定义一个类,实现Init, Process函数即可,Init通信需要的地址信息,process编写服务代码

源码见 Github: https://github.com/jammgit/ProcessPool






你可能感兴趣的:(linux,并发,服务器,进程池,半同步半异步)