Linux网络编程-自己动手写进程池
来源:互联网 发布:知入与知出阅读答案 编辑:程序博客网 时间:2024/04/30 10:03
并发服务器的实现中,可以通过动态的创建子进程(或子线程)来实现。这样有几个缺点:
1、动态创建进程(或线程)比较耗时间,将导致客户响应较慢
2、动态创建的子进程或子线程通常只用来为一个客户服务,这导致系统中产生了很多进程或线程,使进程或线程之间的切换消耗很多CPU时间
3、动态创建子进程是当前进程的完整映像,当前进程需要谨慎管理其分配的文件描述符,否则子进程可能复制这些资源,导致系统可用资源急剧下降,进而影响服务器性能
以上的几个缺点可以通过进程池来规避,进程池是由服务器预先创建一组子进程,一般数目在3-10之间,进程池中的所有子进程运行着相同的代码,应具有相同的属性,比如优先级、PGID等。当有新任务来时,主进程选择进程池中的一个子进程来为之服务,相比于动态创建子进程,选择一个已经存在子进程显然来得跟快。
以下是我自己参考网络资料写的一个简单的进程池示例,进程间通信使用的是管道。
该程序说明:主进程先创建两个管道,一个用于向子进程写数据,另一个用于子进程向主进程写数据。然后主进程再创建4个子进程,让4个子进程来进程处理客户的连接请求,然后给主进程发送信息,同时主进程也给子进程发送应答信息。
运行程序后的输出结果(比如端口设为60000):
在浏览器中输入http://192.168.1.2:60000(192.168.1.2运行该程序的主机)后就可以看到
源代码:
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/stat.h>#include <netinet/in.h>#include <string.h>#include <strings.h>#include <unistd.h>#include <signal.h>#include <fcntl.h>#define err_sys(msg) \ do { perror(msg); exit(-1); } while(0)#define err_exit(msg) \ do { fprintf(stderr, msg); exit(-1); } while(0)#define head200 "HTTP/1.1 200 OK \r\n"#define head404 "HTTP/1.1 404 Not Found\r\n"#define head503 "HTTP/1.1 503 Service unabailiable\r\n"#define MAXCHILD 4#define BUFFSIZE 1024typedef struct{ pid_t pid; char status;}sReport;int len200, len404, len503;int pipe_fd1[2], pipe_fd2[2];void process_child(int listenfd, char* filename){ int connfd; int cnt, len; struct sockaddr_in cliaddr; socklen_t clilen = sizeof(cliaddr); sReport req; char comm = '\0'; int running = 1; char head_buf[1024]; req.pid = getpid(); while(running) { connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen); if(connfd < 0) err_sys("accept"); req.status = 'n'; /* send message to parent process that got a new accept */ if(write(pipe_fd1[1], &req, sizeof(req)) < 0) err_sys("write"); int fd; struct stat file_stat; if((fd = open(filename, O_RDONLY)) < 0) err_sys("open"); if(stat(filename, &file_stat) < 0) err_sys("stat"); bzero(head_buf, sizeof(head_buf)); len = 0; cnt = snprintf(head_buf, BUFFSIZE - 1, head200); len += cnt; cnt = snprintf(head_buf + len, BUFFSIZE - 1 - len, "Coontent-Length: %lu\r\n", file_stat.st_size); len += cnt; cnt = snprintf(head_buf + len, BUFFSIZE - 1 - len, "\r\n"); char *file_buf; struct iovec iv[2]; if((file_buf = (char *)malloc(file_stat.st_size)) == NULL) err_sys("malloc"); bzero(file_buf, file_stat.st_size); if(read(fd, file_buf, file_stat.st_size) < 0) err_sys("read"); iv[0].iov_base = head_buf; iv[0].iov_len = strlen(head_buf); iv[1].iov_base = file_buf; iv[1].iov_len = strlen(file_buf); writev(connfd, iv, 2); /* send the file to client host */ free(file_buf); close(fd); close(connfd); req.status = 'f'; /* tell parent process that finish the request */ if(write(pipe_fd1[1], &req, sizeof(req)) < 0) err_sys("write"); /* wait for commond from parent process */ if(read(pipe_fd2[0], &comm, 1) < 1) err_sys("read"); if('e' == comm) { printf("[%d] exit\n", req.pid); running = 0; } else if('c' == comm) printf("[%d] continue\n", req.pid); else printf("[%d]: comm: %c illeagle\n", req.pid, comm); }}void handle_sigchld(int sig){ printf("the child process exit.\n");}int main(int argc, char *argv[]){ int listenfd; struct sockaddr_in servaddr; pid_t pid; if(argc != 2) err_exit("Usage: http-server port\n"); int port = atoi(argv[1]); len200 = strlen(head200); len404 = strlen(head404); len503 = strlen(head503); if(signal(SIGCHLD, handle_sigchld) < 0) err_sys("signal"); if(pipe(pipe_fd1) < 0) err_sys("pipe pipe_fd1"); if(pipe(pipe_fd2) < 0) err_sys("pipe piep_fd2"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(port); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_sys("socket"); if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) err_sys("bind"); if(listen(listenfd, 10) < 0) err_sys("listen"); int i; for(i = 0; i < MAXCHILD; i++) { if((pid = fork()) < 0) err_sys("fork"); else if(0 == pid) { process_child(listenfd, "test.txt"); //test.txt为测试文件,内容为 Hello World!!! } else { printf("have create child %d\n", pid); } } close(pipe_fd1[1]); close(pipe_fd2[0]); close(listenfd); char c = 'c'; int req_num = 0; sReport req; while(1) { if(read(pipe_fd1[0], &req, sizeof(req)) < 0) err_sys("read pipe_fd1"); /* a new request come */ if(req.status == 'n') //子进程收到一个连接请求 { req_num++; printf("parent: %d have receive new request\n", req.pid); } else if(req.status == 'f') /* just finish a accept */ { req_num--; if(write(pipe_fd2[1], &c, sizeof(c)) < sizeof(c)) err_sys("write"); } } printf("Done\n"); return 0;}
参考资料:
1、《Linux高性能服务器编程》 第15章 进程池和线程池
2、一个进程池的服务器程序
0 0
- Linux网络编程-自己动手写进程池
- 自己动手写进程管理器
- 自己动手写网络爬虫
- 自己动手写网络爬虫
- 自己动手写网络爬虫
- Linux网络编程:进程池 + CGI服务器
- 自己动手写网络爬虫1
- Linux网络编程之多进程
- 自己动手写数据库连接池
- 自己动手写数据库连接池
- 自己动手写对象池
- 【12】Linux-自己动手写驱动
- 《自己动手写操作系统》多进程代码分析
- 《自己动手写操作系统 》第六章 多进程
- 《自己动手写操作系统》第六章:进程调度
- Linux网络编程之多进程模型编程与一个使用进程池实现的CGI服务器
- 自己动手写网络抓包工具
- 自己动手写网络抓包工具
- Java解惑
- Java事件处理
- bzoj3176: [Coci 2012]Sort
- 在广播接收者里面启动Activity
- 永远需要迈出第一步——48 GameJam赛后有感
- Linux网络编程-自己动手写进程池
- 字符串(NSString)、字典(NSDictionary)、数组(NSArray)的总结
- 【PHP教程二】PHP基本知识,运行自己写的.php文件
- c# ListBox绑定对象时删除数据的问题
- WordPress 主题制作(一)主题的基本构成
- Spark中加载本地(或者hdfs)文件以及SparkContext实例的textFile使用
- 关于FastCGI的几个命令说明
- 设计模式(4) - Builder模式
- DEVENV CYGWIN MINGW特点总结