简介
基于Linux的进程池的实现,并发进程池有多种实现模式,在这里统一进行分析。首先给出客户端的测试代码:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <wait.h>
const int clientNum = 10; // 创建用户个数
const int nloop = 100; // 每个用户动态创建连接个数
static int cnt = 0;
void sig_handler (int sig) {
if (sig != SIGCHLD) {
return;
}
pid_t pid;
while ( (pid = waitpid (-1, NULL, WNOHANG) ) > 0) {
printf ("child process: %d left\n", pid);
++cnt;
}
if (cnt >= clientNum) {
puts ("all clients end...");
exit (0);
}
}
int main (int argc, char* argv[]) {
if (argc != 3) {
printf ("Usage: %s <ip of server> <port of server>\n", argv[0]);
exit (0);
}
int port = atoi (argv[2]);
if (port < 1024 || port > 65535) {
perror ("port error\n");
exit (0);
}
struct sockaddr_in serv;
bzero (&serv, sizeof (serv) );
if (inet_pton (AF_INET, argv[1], &serv.sin_addr) == -1) {
perror ("inet_pton() error\n");
exit (1);
}
serv.sin_family = AF_INET;
serv.sin_port = htons (port);
struct sigaction sa;
bzero (&sa, sizeof (sa) );
sa.sa_handler = sig_handler;
sa.sa_flags = SA_RESTART;
if (sigaction (SIGCHLD, &sa, NULL) < 0) {
perror ("sigaction() error");
}
for (int i = 0; i < clientNum; ++i) {
pid_t pid;
if ( (pid = fork() ) == 0) { // 孩子进程
int fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror ("socket() error\n");
exit (1);
}
if (connect (fd, (struct sockaddr*) &serv, sizeof (serv) ) < 0) {
perror ("connect() error\n");
exit (1);
}
const char* buf = "Hello world !\n";
for (int j = 0; j < nloop; ++j) {
send (fd, buf, strlen (buf), 0);
}
close (fd);
printf ("client %d end...\n", i);
exit (0);
}
}
while (true) {
sleep (1);
}
exit (0);
}
这里介绍的是使用不带锁的accept
结构的进程池。原理是:如果有多个accept
函数等待监听内核中同一个fd,那么没有数据时都阻塞。如果有数据,那么所有的都会被唤醒,但是只能有一个进程获取fd,其余的唤醒后接着休眠。这种结构简单,但是出现线程池“惊群”现象,这里的唤醒和休眠会浪费大量的时间,并发度高的时候,更是如此。但是对于少量并发的情况,还是比较合适的,给出代码实现:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <signal.h>
#include <wait.h>
const int MAX_PROCESS_NUMBER = 15;
const int MAX_BUFFER_SIZE = 1024;
bool stop_server = false;
pid_t* pids = nullptr;
int processNum = 0;
void sig_int (int signo); // 终止整个线程池
void process (int fd); // 子进程处理连接
pid_t child_make (int fd); // 产生新的子进程
void child_main (int fd); // 子进程的主要操作
int main (int argc, char* argv[]) {
if (argc != 3) {
printf ("Usage: %s <port of server> <number of process>\n", argv[0]);
exit (0);
}
int port = atoi (argv[1]);
if (port < 1024 || port > 65535) {
perror ("port error\n");
exit (0);
}
processNum = atoi (argv[2]);
if (processNum < 0 || processNum > MAX_PROCESS_NUMBER) {
perror ("process number error\n");
exit (0);
}
struct sockaddr_in serv;
bzero (&serv, sizeof (serv) );
serv.sin_family = AF_INET;
serv.sin_port = htons (port);
serv.sin_addr.s_addr = htonl (INADDR_ANY);
int listenfd = socket (AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
perror ("socket() error\n");
exit (1);
}
if (bind (listenfd, (struct sockaddr*) &serv, sizeof (serv) ) < 0) {
perror ("bind() error\n");
exit (1);
}
if (listen (listenfd, 32) < 0) {
perror ("listen() error\n");
exit (1);
}
struct sigaction sa; // 注册终止信号
bzero (&sa, sizeof (sa) );
sa.sa_handler = sig_int;
sa.sa_flags = SA_RESTART;
if (sigaction (SIGTERM, &sa, NULL) < 0) {
perror ("sigaction() error\n");
exit (1);
}
pids = new pid_t[processNum];
for (int i = 0; i < processNum; ++i) {
pids[i] = child_make (listenfd);
}
while (!stop_server) {
sleep (1);
}
exit (0);
}
void sig_int (int signo) {
if (signo != SIGTERM) {
return;
}
for (int i = 0; i < processNum; ++i) {
kill (pids[i], SIGTERM); // 终止子进程
}
if (pids != nullptr) {
delete[] pids;
}
stop_server = true;
}
pid_t child_make (int fd) {
pid_t pid;
if ( (pid = fork() ) > 0) {
return pid;
}
child_main (fd);
}
void child_main (int fd) {
struct sockaddr_in address;
bzero (&address, sizeof (address) );
socklen_t addrlen = sizeof (address);
for (;;) {
int connfd = accept (fd, (struct sockaddr*) &address, &addrlen);
if (connfd < 0) {
return;
}
puts ("accept data");
process (connfd);
close (fd);
}
}
void process (int fd) {
char buf[MAX_BUFFER_SIZE];
for (;;) {
if (recv (fd, buf, MAX_BUFFER_SIZE, 0) <= 0) {
return;
}
printf ("%s", buf);
}
}