NO.1
一:什么是多进程模型
多进程模型是服务器在接收到大量高并发客户端访问时,通过创建多个子进程来与客户端进行通信。单进程阻塞在read()系统调用的时候,会导致服务器无法响应到其他的连接请求。这时可以通过fork()函数创建出多个子进程来处理业务,而主进程继续循环accept()其他客户连接,子进程实施具体的通信细节。
二:fork函数详解
NAME
fork - create a child process
SYNOPSIS
#include <unistd.h>
pid_t fork(void);
DESCRIPTION
fork() creates a new process by
duplicating(复制) the calling process. The new process, referred to as
the child, is an exact(准确的)
duplicate of the calling process, referred to as the parent, except for the following points:
* The child has its own unique process ID, and this PID does
not match the ID of any existing process group (setpgid(2)).
* The child's parent process ID is the same as the parent's process ID.
* The child does not inherit(继承) its parent's memory locks (mlock(2), mlockall(2)).
* Process resource utilizations(利用) (getrusage(2)) and CPU time counters (times(2)) are reset to zero in the child.
* The child's set of pending(挂起) signals is initially empty (sigpending(2)).
* The child does not inherit semaphore(信号量) adjustments from its parent (semop(2)).
* The child does not inherit record(记录) locks from its parent (fcntl(2)).
* The child does not inherit timers from its parent (setitimer(2), alarm(2), timer_create(2)).
......
RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is
returned in the parent, no child process is created, and errno is set appropriately(适当的).
ERRORS
EAGAIN fork()
cannot allocate sufficient(足够的) memory to copy the parent's page
tables and allocate a task structure for the child.
EAGAIN It was not possible to create a new process because the
caller's RLIMIT_NPROC(用户可拥有的最大进程数) resource limit was
encountered(冲突).
To exceed this limit, the process must have either the CAP_SYS_ADMIN or the CAP_SYS_RESOURCE capability.
ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight(紧的).
ENOSYS fork() is not supported on this platform(平台) (for example, hardware without a Memory-Management Unit).
所以由上可以总结出我们使用fork的“套路”:
- pid_t pid;
-
- if
- elseif
-
-
-
- else
-
三:服务器代码示例
下面是一个简单的回射服务器代码,服务器将客户端发送的代码回给客户端。
- #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #define ERR_EXIT(sz) \ do while void connfd)
- recvbuf[1024];
- for sizeof sizeof if break sizeof intvoid struct listenfd;
- if
- sizeof
-
-
- on = 1;
- ifsizeof
- ifstructsizeof
- if
- struct connfd;
- sizeof
- for ifstruct
-
-
- if );
- if
-
- else return }
四:客户端代码示例
- #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #define ERR_EXIT(sz) \ do while intvoid struct sockfd;
- if
- sizeof );
- ifstructsizeof
- sendbuf[1024] = {0};
- recvbuf[1024] = {0};
- whilesizeof
-
- sizeof
-
- sizeof sizeof return }
以上程序均经过测试。
NO.2
下面是一个进程池的实现:
- #ifndef _PROCESS_POOL_H #define _PROCESS_POOL_H #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <fcntl.h> #include <stdlib.h> #include <sys/epoll.h> #include <signal.h> #include <sys/wait.h> #include <sys/stat.h> //描述一个子进程的类,pid_是目标子进程的pid,pipefd是父进程和子进程通信用的管道 class public pipefd_[2];
- //进程池类,将它定义为模板类是为了代码复用。其模板参数是处理逻辑任务的类 templatetypename class private listenfd, process_number = 8);
- static public
- static listenfd, process_number = 8) {
- static listenfd, process_number = 8) {
- if new return void private void void void private
- staticconst MAX_PROCESS_NUMBER = 16;
-
- staticconst USER_PER_PROCESS = 65536;
-
- staticconst MAX_EVENT_NUMBER = 10000;
-
- process_number_;
-
- index_;
-
- epollfd_;
-
- listenfd_;
-
- stop_;
-
- templatetypename //用于处理信号的管道,以统一事件源,后面称之为信号管道 static sig_pipefd[2];
- static setnonblocking( fd)
- old_option = fcntl(fd, F_GETFL);
- new_option = old_option | O_NONBLOCK;
- return staticvoid epollfd, fd)
- //从epollfd标识的epoll内核事件表中删除fd上的所有注册事件 staticvoid epollfd, fd)
- staticvoid sig)
- save_errno = errno;
- msg = sig;
- *)&msg, 1, 0);
- staticvoid sig, voidint restart = true struct , sizeof if //进程池构造函数,参数listenfd是监听socket,它必须在创建进程池之前被创建, //否则子进程无法直接引用它,参数process_number指定进程池中子进程的数量 templatetypename listenfd, process_number)
- false new
- forint ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sub_process_[i].pipefd_);
- if continue else
-
- break
-
- //统一事件源,这个pipe不是用来和父进程通信的pipe templatetypename void ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sig_pipefd);
-
-
- //父进程中index_为-1,子进程中index_值大于等于0,我们据此判断接下来要运行的是父进程代码还是子进程代码 templatetypename void templatetypename void
- pipefd = sub_process_[index_].pipefd_[1];
-
-
- new number = 0;
- ret = -1;
- while if break forint sockfd = events[i].data.fd;
- if client = 0;
-
- *)&client, sizeof if continue else struct sizeof connfd = accept(listenfd_, (struct if continue
-
- elseif sig;
- signals[1024];
- sizeof if continue else forint switch case stat;
- while continue break case case true break default break
- elseif else continue delete templatetypename void
- sub_process_counter = 0;
- number = 0;
- ret = -1;
- while if break forint sockfd = events[i].data.fd;
- if
- i= sub_process_counter;
- do if
- break while if
- true break new_conn = 1;
- *)&new_conn, sizeof
- elseif sig;
- signals[1024];
- sizeof if continue else forint switch case stat;
- while forint
- if
-
- true forint if false break case case
-
- forint pid = sub_process_[i].pid_;
- if break default break else continue #endif
服务器主程序:
- #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <fcntl.h> #include <stdlib.h> #include <sys/epoll.h> #include <signal.h> #include <sys/wait.h> #include <sys/stat.h> #include "process_pool.h" class public sizeof public void epollfd, sockfd, const void idx = 0;
- ret = 1;
-
- whiletrue
- if if break
- elseif break else
- for if) && (buf_[idx] == ))
- break
- if continue ;
- * file_name = buf_;
-
- if
- break
- if break elseif
- break
- else
- private
- staticconst BUFFER_SIZE = 1024;
- static epollfd_;
- sockfd_;
- buf_[BUFFER_SIZE];
-
- read_idx_;
- int int argc, ** argv)
- if return const* ip = argv[1];
- port = atoi(argv[2]);
- listenfd = socket(AF_INET, SOCK_STREAM, 0);
- ret = 0;
- struct sizeof on = 1;
- sizeof structsizeof if return if delete return }
CGI程序:
- #include <stdio.h> int );
- return }
将CGI程序生成可执行文件,等候客户端请求。
客户端程序:
然后客户端就会输出,“hello,client”