进程池和线程池
池的概念
由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是池的概念。池是一组资源的集合,这组资源在服务器启动之初就完全被创建并初始化,这称为静态资源分配。当服务器进入正是运行阶段,即开始处理客户请求的时候,如果它需要相关的资源,就可以直接从池中获取,无需动态分配。很显然,直接从池中取得所需资源比动态分配资源的速度要快得多,因为分配系统资源的系统调用都是很耗时的。当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用来释放资源。从最终效果来看,池相当于服务器管理系统资源的应用设施,它避免了服务器对内核的频繁访问。
池可以分为多种,常见的有内存池、进程池、线程池和连接池。
进程池是由服务器预先创建的一组子进程,这些子进程的数目在 3~10 个之间(当然这只是典型情况)。线程池中的线程数量应该和 CPU 数量差不多。
进程池中的所有子进程都运行着相同的代码,并具有相同的属性,比如优先级、 PGID 等。
当有新的任务来到时,主进程将通过某种方式选择进程池中的某一个子进程来为之服务。相比于动态创建子进程,选择一个已经存在的子进程的代价显得小得多。至于主进程选择哪个子进程来为新任务服务,则有两种方法:
1.主进程使用某种算法来主动选择子进程。最简单、最常用的算法是随机算法和 Round Robin (轮流算法)。
2.主进程和所有子进程通过一个共享的工作队列来同步,子进程都睡眠在该工作队列上。当有新的任务到来时,主进程将任务添加到工作队列中。这将唤醒正在等待任务的子进程,不过只有一个子进程将获得新任务的“接管权”,它可以从工作队列中取出任务并执行之,而其他子进程将继续睡眠在工作队列上。
当选择好子进程后,主进程还需要使用某种通知机制来告诉目标子进程有新任务需要处理,并传递必要的数据。最简单的方式是,在父进程和子进程之间预先建立好一条管道,然后通过管道来实现所有的进程间通信。在父线程和子线程之间传递数据就要简单得多,因为我们可以把这些数据定义为全局,那么它们本身就是被所有线程共享的。
代码:
1,父进程listen,创建pipe(下面所有父子进程之间的通信都用该pipe)
2,父进程预fork n个子进程
3,各个子进程accept(listenfd),即所有子进程竞争accept请求。由于listenfd是在fork之前就有的,所以所有子进程都可以访问到,不需用到“进程间文件描述符传递”问题;
4,子进程每accept到一个请求都告诉父进程,父进程把请求数加1;子进程没完成一个请求,父进程把请求数减1;当父进程发现请求数 >= 子进程数时,父进程创建新的子进程,并把子进程数加1(当然子进程数有个预先上限);当父进程发现子进程数大于请求数加1时,父进程杀死多余的子进程。
总的来说,思想是让子进程accept并处理请求,父进程通过子进程发来的信息控制请求数与子进程数之间的关系。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PRECHILD 5
#define MAXCHILD 50
#define BUFSIZE 4096
#define PIDPATH "pid"
#define head503 "HTTP/1.1 503 Service unavailable\r\n"
#define head404 "HTTP/1.1 404 Not Found\r\n"
#define head200 "HTTP/1.1 200 0K\n\rContent—Type: text/html\n\rContent—Length: "
int len503, len404, len200;
int fd1[2], fd2[2];
typedef struct {
pid_t pid;
char status; // 'n' means new request; 'f' means finish the request
} REPORT;
void answer(int listenfd)
{
int connfd;
char buf[BUFSIZE];
int count;
int pid = getpid();
struct sockaddr_in cliaddr;
int size = sizeof(cliaddr);
char comm;
REPORT rep;
rep.pid = pid;
while (1) {
connfd = accept(listenfd, (struct sockaddr *)&cliaddr,(socklen_t *)&size ); //子进程accept请求
rep.status = 'n';
if (write(fd1[1], &rep, sizeof(rep)) < 0) { //通知父进程已经accept了请求
perror("write pipe new failed");
exit(-1);
}
count = read(connfd, buf, BUFSIZE);
char req[10];
char filepath[256];
sscanf(buf, "%s%s", req, filepath + 1);
filepath[0] = '.';
if (strcmp("GET", req) != 0) {//503
write(connfd, head503, len503);
//goto err_out;
close(connfd);
exit(-1);
}
char content[BUFSIZE];
struct stat stbuf;
if (lstat(filepath, &stbuf) != 0) {
int err = errno;
if (err == ENOENT) {//404
write(connfd, head404, len404);
}
close(connfd);
exit(-1);
}
count = write(connfd, head200, len200);
u_int filesize = stbuf.st_size;
sprintf(content, "%u\n\r\n\r", filesize);
count = write(connfd, content, strlen(content));
FILE *fp = fopen(filepath, "r");
if (fp == NULL) {
printf("open file %s failed\n", filepath);
close(connfd);
exit(-1);
}
while((count = fread(content, 1, sizeof(content), fp)) > 0) {
//printf("%s", content);
if (write(connfd, content, count) != count) {
printf("write failed\n");
}
}
fclose(fp);
close(connfd);
rep.status = 'f';
if (write(fd1[1], &rep, sizeof(rep)) < 0) {//告诉父进程自己处理完了请求
perror("write pipe finish failed");
exit(-1);
}
if (read(fd2[0], &comm, 1) < 1) {//等待来自父进程的命令
perror("read pipe failed");
exit(-1);
}
//printf("[%d] reve %c from pa\n", pid, comm);
if (comm == 'e') { //收到exit命令
printf("[%d] exit\n", pid);
exit(-1);
}
else if (comm == 'c') { //收到继续accept的命令
printf("[%d] continue\n", pid);
}
else {
printf("[%d] comm : %c illeagle\n", pid, comm);
}
}
}
void usage()
{
printf("Usage: http-serv port\n");
}
int write_pid()
{
int fd;
if ((fd = open(PIDPATH, O_WRONLY | O_TRUNC | O_CREAT, S_IWUSR)) < 0){
perror("open pidfile faild");
return -1;
}
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) == -1) {
int err = errno;
perror("fcntl faild");
if (err == EAGAIN) {
printf("Another http-serv process is running now!\n");
}
return -1;
}
return 0;
}
void daemon_init()
{
//clear file creation mask;
umask(0);
//become a session leader
if (fork() != 0)
exit(-1);
if (setsid() < 0)
exit(-1);
//make sure can be never get the TTY control
if (fork() != 0)
exit(-1);
//may chdir here
int i;
for (i = 0; i < 1024; i++)
close(i);
/* * Attach file descriptors 0, 1, and 2 to /dev/null. */
int fd0, fd1, fd2;
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
printf("init failed\n");
exit(-1);
}
}
int
main(int argc, char **argv)
{
int listenfd;
struct sockaddr_in servaddr;
pid_t pid;
if (argc != 2) {
usage();
return -1;
}
signal(SIGCHLD, SIG_IGN);
len200 = strlen(head200);
len404 = strlen(head404);
len503 = strlen(head503);
daemon_init(); //转为后台程序,如需打印调试,把这行注释掉
if (write_pid() < 0) //避免同时有多个该程序在运行
return -1;
if (pipe(fd1) < 0) {
perror("pipe failed");
exit(-1);
}
if (s_pipe(fd2) < 0) {
perror("pipe failed");
exit(-1);
}
int port = atoi(argv[1]);
//initialize servaddr and listenfd...
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listenfd, 1000);
int i;
for (i = 0; i < PRECHILD ; i++) { //父进程预fork 子进程
if ((pid = fork()) < 0) {
perror("fork faild");
exit(3);
}
else if (pid == 0) {
answer(listenfd);
}
else {
printf("have create child %d\n", pid);
}
}
char e = 'e';
char c = 'c';
int req_num = 0;
int child_num = PRECHILD;
REPORT rep;
while (1) {
//printf("req_num = %d, child_num = %d\n", req_num, child_num);
if (read(fd1[0], &rep, sizeof(rep)) < sizeof(rep)) {//等待子进程发来消息
perror("parent read pipe failed");
exit(-1);
}
//printf("parent: receive from %d\n", pid);
if (rep.status == 'n') {//子进程刚accept了新的请求
req_num ++;
printf("parent: %d have receive new request\n", rep.pid);
if (req_num >= child_num && child_num <= MAXCHILD) { //请求数过多,创建更多子进程
if ((pid = fork()) < 0) {
perror("fork faild");
exit(3);
}
else if (pid == 0) {
answer(listenfd);
}
else {
printf("have create child %d\n", pid);
child_num ++;
}
}
}
else if (rep.status == 'f') {//子进程刚处理完了一个请求
req_num --;
//printf("parent: %d have finish a request\n", rep.pid);
if (child_num > (req_num + 1) && child_num > PRECHILD) {//子进程数过多,删除多余的子进程
if (write(fd2[1], &e, sizeof(e)) < sizeof(e)) {
perror("pa write pipe failed");
exit(-2);
}
//printf("tell child exit\n");
child_num --;
}
else {
if (write(fd2[1], &c, sizeof(c)) < sizeof(c)) {//让子进程继续等待accept
perror("pa write pipe failed");
exit(-2);
}
//printf("tell child continue\n");
}
}
}
return 0;
}