Concurrent Server
First of all, let's assume that there are two types of servers: first, the first connection request processing time is 1s, the 50th connection request processing time is 50s, the 100th connection request processing time is 100s. That is, at the same time a lot of customer service connection, need to queue to accept, but as long as the acceptance, their service time on average only 1s. Second, the processing time for all connection requests does not exceed 1s, but the average service time is 2~3s.
Even if it is possible to extend the service time, we generally choose the second way in the actual network programming, so that it can provide service to all the customer service that initiates the request at the same time, in order to improve the average satisfaction. Moreover, the data communication time in the network program is greater than the CPU time, so we should choose the second way of the server-the concurrent server, make full use of the cup.
Let's take a look at the typical implementation model and method of the concurrent server side:
- Multi-Process Server: Provides services at the same time by creating multiple processes
- Multiplexing servers: Serving by bundling and managing I/O objects uniformly
- Multi-Threaded server: Provides services by generating the same amount of threads as the customer service
This chapter first of all: multi-process server. This approach is not supported by Windows, so focus on the Linux platform ...
Theory basis of multi-process
-
What is a process :
We can understand the process in this way: It is a running program that occupies memory space. such as: We downloaded a game on the Internet and installed to the hard disk, at this time the game is not a process, but the program. Because the game does not go into a running state. The next step is to run the program, when the game is loaded into memory and in a running state, which is called a process. If you open the game at the same time, it will generate the corresponding number of processes, will also occupy the corresponding number of processes of memory space, they are independent of the memory structure, non-impact.
There is also a program run process can also produce multiple processes, this chapter to talk about the multi-process server is the representative of this. The
Supplement: A CPU with 2 computing devices is called a dual-core CPU, and a CPU with 4 operators is called a 4-core. The number of cores is the same as that of the processes that can run concurrently. Conversely, if the number of processes exceeds the number of cores, the process uses CPU resources for ticks. Just because the CPU is running very fast, we feel that all the processes are running at the same time. Of course, the more the number of nuclear, the more obvious this feeling.
-
process ID
When all processes are created, the operating system assigns them an ID number that is an integer greater than 2, and the id=1 process is the first process after the operating system is started (to assist the operating system). This process is not available to users. Here's a look at the commands for viewing the current running process on Linux: PS (view details: PS au)
-
Create a process by calling the fork function
Pi d_t fork (void); The
successfully returns the process ID, which returns 1 on failure. The
Fork function is a process that replicates a running call to the fork function. The both processes execute the statement after the fork function is called. So after the statement execution process to distinguish between the parent process and the child process, we can be separated by the return value of the fork function, that is, the parent process: The fork function returns the child process ID, the child process: The fork function returns 0.
pid_t pid;pid = fork();if0){ //子进程}else{ //父进程}
Zombie Process
A zombie process is a process called a zombie process that is supposed to destroy the freed up memory space, but it is not destroyed and resides in memory.
So how did the zombie process occur? First of all, the process of recycling is operating system control, the system recovery time is generally two cases: 1, passing parameters and calling the Exit function. The 2,main function executes the Retrun statement and returns a value. However, for child processes, the operating system does not directly destroy the child processes, and the destruction of the child processes is only passed indirectly through its parent process to the operating system for recycling. Since then, the subprocess has become a zombie process. Ultimately, it is only destroyed with the parent process.
Destroying child processes
It said that the child process directly call exit or Retrun end will become a zombie process, then how to destroy the child process? As stated earlier, the destruction of a child process requires that its parent process indirectly pass the destruction message to the operating system, in two ways:
Destroy child process mode 1: Using the Wait function
int status;pid_t pid = fork (); if (PID = = 0 ) {exit (1 ); // or return 0 ;} else {wait (&status); // returns the terminated child process ID on success, return-1 if (status)///macro, wifexited normal termination return true printf ( "child return: %d \ n ", Wexitstatus (status)); // macro, returns the return value of the child process}
This is to destroy the child process with the wait function, but note that the wait function is a blocking function, and if there are no terminated child processes, the program will block until a child process terminates, so use this function with caution.
Destroy child process mode 2: Using the Waitpid function
The wait function causes the program to block, and we can also use the Waitpid function, which does not block.
intstatus;pid_t PID =Fork();if(PID = =0){Sleep( the);return -;}Else{//No terminating child process returned0, there is a return to the terminating child process ID, which returns on failure-1 while(!Waitpid(-1, &status, Wnohang))//does not block {Sleep(3); Puts"Sleep 3s"); }if(wifexited (status))printf("Child return %d \ n", Wexitstatus (status));}
-
Signal Processing (event-driven)
before we know how to destroy the sub-process, to avoid the zombie process generation. But the question is, when exactly are we going to terminate the sub-process? We must not be able to cycle through the parent process as we wrote above. So, in this section we'll discuss another efficient way: signal processing, or event-driven (that is, registering an event, and automatically callback the function that notifies the registry whenever the event is generated). This way we don't have to worry about when to terminate the child process.
1, first look at the previous version of Linux registration function
Void (*signal (int signo, void (*func) (int))) (int);
Return value: void (*signal) (int)
Function name: signal
Parameter: int signo, void (*func) (int)
Description: int Signo parameter is registered Types, typically: SIGALRM (time to alarm registration), SIGINT (input CTRL + C), SIGCHLD (child process termination). The Void (*func) (int) parameter is only a function pointer, which is the callback function to execute after the parameter 1 event response. The
instance is as follows:
//时间响应回调函数timeout(int sig){ if(sig == SIGALRM) puts("Time out!")main(){ signal(SIGALRM, timeout);//注册事件 alarm(2);//2s后产生SIGALRM类型事件 sleep(100); return 0;}
2, the actual use of more registration functions (signal function in the Unix family of different operating systems may be the difference, but the sigaction function is exactly the same)
int sigaction (int signo, const struct sigaction Act, struct sigaction oldact);
Returns 0 on success, 1 for failure
struct sigaction
{
void (*sa_handler) (int); callback function
sigset_t Sa_mask; General fully initialized to 0
int sa_flags; Initialized to 0
}
Examples are as follows:
void timeout(int sig){ if(sig == SIGALRM) puts("Time out!");}int main(){ //初始化结构体 struct sigaction act; act.sa_handler = timeout; //设置sa_mask成员的所有位为0 0; 0);//注册事件 alarm(2); sleep(100); return0;}
Well, the above is all of our theoretical knowledge, and below we can actually use this knowledge to write process-based concurrent servers.
Concurrent servers based on multiple processes
////Main.cpp//Hello_server////Created by app05 on 15-8-18.//Copyright (c) 2015 APP05. All rights reserved.////Each company has a customer service side to open a process, that is, the number of service terminal process and customer service connection number corresponding#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/wait.h>#include <arpa/inet.h>#include <sys/socket.h>#include <signal.h>#define BUF_SIZE 1024x768voidError_handling (Char*message);voidRead_childproc (intSIG);//Child process destroy callbackintMainintargcConst Char* argv[]) {intServ_sock, Clnt_sock;structSockaddr_in Serv_adr, Clnt_adr; pid_t pid;structSigaction Act; Socklen_t ADR_SZ;intStr_len, State;CharBuf[buf_size];if(ARGC! =2) {printf("Usage:%s <port> \ n", argv[0]);Exit(1); } Act.sa_handler = Read_childproc; Sigemptyset (&act.sa_mask); Act.sa_flags =0; State = Sigaction (SIGCHLD, &act,0);//Register child process termination eventsServ_sock = socket (pf_inet, Sock_stream,0);memset(&serv_adr,0,sizeof(SERV_ADR)); serv_adr.sin_family = af_inet; SERV_ADR.SIN_ADDR.S_ADDR = htonl (Inaddr_any); Serv_adr.sin_port = htons (Atoi (argv[1]));if(Bind (Serv_sock, (structSOCKADDR *) &serv_adr,sizeof(SERV_ADR)) == -1) Error_handling ("bind () Error");if(Listen (Serv_sock,5) == -1) Error_handling ("Listen () Error"); while(1) {ADR_SZ =sizeof(CLNT_ADR); Clnt_sock = Accept (Serv_sock, (structSOCKADDR *) &clnt_adr, &ADR_SZ);if(Clnt_sock = =-1)Continue;Else puts("New client connected ..."); PID = fork ();//Create child process if(PID = =-1) {Close (Clnt_sock);Continue; }if(PID = =0)//Sub-process Run module{/ * The child process replicates the serv_sock handle in the parent process, all pointing to the same set of sockets, and only two of them are close before the socket is destroyed. So be aware that one by one corresponds to close closed. */Close (Serv_sock); while((Str_len = Read (Clnt_sock, buf, buf_size))! =0) Write (Clnt_sock, buf, Str_len); Close (Clnt_sock);puts("Client disconnected ...");return 0; }ElseClose (Clnt_sock); } close (Serv_sock);return 0;}voidError_handling (Char*message) {fputs(message, stderr); FPUTC (' \ n ', stderr);Exit(1);}voidRead_childproc (intSIG) {pid_t pid;intStatus PID = Waitpid (-1, &status, Wnohang);//Destroy child processes printf("removed proc ID:%d \ n", PID);}
////Main.cpp//Hello_client////Created by app05 on 15-8-18.//Copyright (c) 2015 APP05. All rights reserved.////customer-side I/O segmentation, the parent process is responsible for receiving data, the child process is responsible for sending data, separate processing#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <arpa/inet.h>#include <sys/socket.h>#define BUF_SIZE 1024x768voidError_handling (Char*message);voidRead_routime (intSockChar*BUF);//Receive DatavoidWrite_routine (intSockChar*BUF);//Send dataintMainintargcConst Char* argv[]) {intSock pid_t pid;CharBuf[buf_size];structSockaddr_in Serv_adr;if(ARGC! =3) {printf("Usage:%s <IP> <port> \ n", argv[0]);Exit(1); } sock = socket (pf_inet, Sock_stream,0);if(Sock = =-1) Error_handling ("socket () error");memset(&serv_adr,0,sizeof(SERV_ADR)); serv_adr.sin_family = af_inet; SERV_ADR.SIN_ADDR.S_ADDR = inet_addr (argv[1]); Serv_adr.sin_port = htons (Atoi (argv[2]));if(Connect (sock,structSOCKADDR *) &serv_adr,sizeof(SERV_ADR)) == -1) Error_handling ("Connect () Error");/* Customer side I/O segmentation, the parent process is responsible for receiving data, the child process is responsible for sending data. This separation can improve the performance of the program that frequently exchanges data, not the same as before, only to receive the last piece of data before the next one can be sent. */PID = fork ();if(PID = =0) Write_routine (sock, buf);ElseRead_routime (sock, buf); Close (sock);return 0;}voidError_handling (Char*message) {fputs(message, stderr); FPUTC (' \ n ', stderr);Exit(1);}voidRead_routime (intSockChar*BUF) { while(1) {intStr_len = Read (sock, buf, buf_size);if(Str_len = =0)return; Buf[str_len] =0;printf("Message from server:%s", buf); }}voidWrite_routine (intSockChar*BUF) { while(1) {fgets (buf, Buf_size, stdin);if(!strcmp(BUF,"q\n") || !strcmp(BUF,"q\n") {Shutdown (sock, SHUT_WR); } write (sock, buf,strlen(BUF)); }}
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
TCP/IP network Programming Learning note _11--multi-process server side