Linux Socketマルチプロセスサーバ
6269 ワード
server.c
1.登録信号SIGCHLDのための処理関数
SIGCHLD:サブプロセスが終了すると、親プロセスはこの信号を受信します.親プロセスがこの信号を処理していない場合、サブプロセスは終了しますが、カーネルプロセステーブルにテーブルアイテムが占有されます.この場合、サブプロセスはゾンビプロセスと呼ばれます.この場合、(親プロセスがSIGCHALD信号を無視したり、キャプチャしたり、waitが派生したサブプロセスが発生したり、親プロセスが先に終了したりした場合、サブプロセスの終了は自動的にinitプロセスによって引き継がれます).
2. 3 4
関数プロトタイプ:pid_t waitpid(pid_t pid,int * status,int options);
関数の説明:
waitpid()が呼び出されたときにサブプロセスが終了した場合、waitpid()はすぐにサブプロセス終了ステータス値を返します.サブプロセスの終了ステータス値は、パラメータstatusによって返されます.
サブプロセスのプロセス識別コードも一緒に返されます.終了状態値を気にしない場合は、パラメータstatusをNULLに設定できます.
パラメータpid:
待ちたいサブプロセス識別コードの数値的意味は次のとおりです.
pid<-1プロセスグループ識別コードがpid絶対値である任意のサブプロセスを待つ
pid=-1はwait()に相当する任意のサブプロセスを待つ
pid=0待機プロセスグループ識別コード現在のプロセスと同じサブプロセス
pid>0サブプロセス識別コードがpidのサブプロセスを待つ
パラメータ:
戻るステータス
WIFEXITED(status)正常終了サブプロセスが戻った状態であれば真である.この場合、WEXITSTATUS(status)を実行し、サブプロセスをexitまたは_に渡すことができます.eixtの下位8ビット.
WEXITSTATUS(status)は、サブプロセスexit()が返す終了コードを取得し、通常はWIFEXITEDで正常に終了するかどうかを判断してこのマクロを使用する.
パラメータoptions:
waitpidを制御する追加のオプションが用意されています.パラメータoptionは0または「|」演算子で接続して使用できます.たとえば、次のようにします.
WNOHANG pidが指定したサブプロセスが終了しない場合、waitpid()関数は0を返し、待たない.終了すると、そのサブプロセスのIDが返される
5 signal(SIGCHLD,sig_child); サブプロセス終了信号の処理関数の登録
親プロセスがこの信号を登録処理する場合、サブプロセスはexitを実行した後にゾンビプロセスを生成します.以下のようにします.
zxnms 18717 16191 0 15:53 pts/0 00:00:00 ./server
zxnms 18719 18717 0 15:53 pts/0 00:00:00 [server]
zxnms 18721 18717 0 15:53 pts/0 00:00:00 [server]
zxnms 18723 18717 0 15:53 pts/0 00:00:00 [server]
親プロセスはwaitまたはwaitpid処理ゾンビプロセスを実行します.
あるいは、親プロセスが終了すると、サブプロセスのゾンビプロセスが孤児プロセスになり、これらのゾンビプロセスがinitによって回収されます.
信号処理関数の処理では、親プロセスの現在のタスクをブロックせず、サブプロセスが信号を送信した場合にのみ処理できます.
6サブプロセスで無効な親プロセスのリスニングsocket fdを閉じる
7親プロセスでサブプロセスが使用されていないsocket fdは、マルチプロセス環境ではcloseが参照カウントを1だけ減らし、すべてのプロセスがcloseになり、参照カウントが0になるまでカーネルがFINを送信しないため閉じます.
8ここでは直接終了します.そうしないと、サブプロセスの後ろの文もwhileループになり、forkにも実行されます.それは2層だけでなく、ネストされ続け、サブプロセスのサブプロセスのサブプロセスになります.
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <signal.h>
#define MAXLINE 1024
// 1:
static void sig_child(int signo)
{
pid_t pid;
int status;
while( (pid=waitpid(-1, &status,WNOHANG)) > 0) //2: W NO HANG
{
printf("child terminated,pid:%d
", pid);
if (WIFEXITED(status)) // W IF EXIT ED // 3:
{
printf("exit code:%d
",WEXITSTATUS(status)); //4: W EXIT STATUS
}
}
}
int main()
{
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1)
{
printf("socket error[%d]:%s
", errno, strerror(errno));
exit(errno);
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(55555);
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
printf("bind error[%d]:%s
", errno, strerror(errno));
exit(errno);
}
if (listen(listen_fd, 3) == -1)
{
printf("listen error[%d]:%s
", errno, strerror(errno));
exit(errno);
}
//5: catch child terminate signal
signal(SIGCHLD,sig_child);
while (1)
{
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
int len = sizeof(client_addr);
int client_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &len);
if (client_fd == -1)
{
printf("accept error[%d]:%s
", errno, strerror(errno));
sleep(1);
continue;
}
pid_t pid = fork();
if (pid < 0)
{
printf("fork error[%d]:%s
", errno, strerror(errno));
continue;
}else if (pid == 0) // child process
{
close(listen_fd); //6: close no use parent fd in child
printf("child process[%d],ppid:%d
", getpid(), getppid());
char rb[100] = {0};
int recv_len = recv(client_fd, rb, MAXLINE, 0);
if (recv_len == -1)
{
printf("recv error[%d]:%s
", errno, strerror(errno));
exit(errno);
}
printf("recv[%d]:%s
", recv_len, rb);
strcat(rb, ", has been received by server, the msg from server");
if (send(client_fd, rb, strlen(rb), 0) == -1)
{
printf("send error[%d]:%s
", errno, strerror(errno));
exit(errno);
}
printf("send success
");
close(client_fd);
exit(0); // 8
}
close(client_fd); //7: close no use child fd in parent
}
close(listen_fd);
}
1.登録信号SIGCHLDのための処理関数
SIGCHLD:サブプロセスが終了すると、親プロセスはこの信号を受信します.親プロセスがこの信号を処理していない場合、サブプロセスは終了しますが、カーネルプロセステーブルにテーブルアイテムが占有されます.この場合、サブプロセスはゾンビプロセスと呼ばれます.この場合、(親プロセスがSIGCHALD信号を無視したり、キャプチャしたり、waitが派生したサブプロセスが発生したり、親プロセスが先に終了したりした場合、サブプロセスの終了は自動的にinitプロセスによって引き継がれます).
2. 3 4
関数プロトタイプ:pid_t waitpid(pid_t pid,int * status,int options);
関数の説明:
waitpid()が呼び出されたときにサブプロセスが終了した場合、waitpid()はすぐにサブプロセス終了ステータス値を返します.サブプロセスの終了ステータス値は、パラメータstatusによって返されます.
サブプロセスのプロセス識別コードも一緒に返されます.終了状態値を気にしない場合は、パラメータstatusをNULLに設定できます.
パラメータpid:
待ちたいサブプロセス識別コードの数値的意味は次のとおりです.
pid<-1プロセスグループ識別コードがpid絶対値である任意のサブプロセスを待つ
pid=-1はwait()に相当する任意のサブプロセスを待つ
pid=0待機プロセスグループ識別コード現在のプロセスと同じサブプロセス
pid>0サブプロセス識別コードがpidのサブプロセスを待つ
パラメータ:
戻るステータス
WIFEXITED(status)正常終了サブプロセスが戻った状態であれば真である.この場合、WEXITSTATUS(status)を実行し、サブプロセスをexitまたは_に渡すことができます.eixtの下位8ビット.
WEXITSTATUS(status)は、サブプロセスexit()が返す終了コードを取得し、通常はWIFEXITEDで正常に終了するかどうかを判断してこのマクロを使用する.
パラメータoptions:
waitpidを制御する追加のオプションが用意されています.パラメータoptionは0または「|」演算子で接続して使用できます.たとえば、次のようにします.
WNOHANG pidが指定したサブプロセスが終了しない場合、waitpid()関数は0を返し、待たない.終了すると、そのサブプロセスのIDが返される
5 signal(SIGCHLD,sig_child); サブプロセス終了信号の処理関数の登録
親プロセスがこの信号を登録処理する場合、サブプロセスはexitを実行した後にゾンビプロセスを生成します.以下のようにします.
zxnms 18717 16191 0 15:53 pts/0 00:00:00 ./server
zxnms 18719 18717 0 15:53 pts/0 00:00:00 [server]
zxnms 18721 18717 0 15:53 pts/0 00:00:00 [server]
zxnms 18723 18717 0 15:53 pts/0 00:00:00 [server]
親プロセスはwaitまたはwaitpid処理ゾンビプロセスを実行します.
あるいは、親プロセスが終了すると、サブプロセスのゾンビプロセスが孤児プロセスになり、これらのゾンビプロセスがinitによって回収されます.
信号処理関数の処理では、親プロセスの現在のタスクをブロックせず、サブプロセスが信号を送信した場合にのみ処理できます.
6サブプロセスで無効な親プロセスのリスニングsocket fdを閉じる
7親プロセスでサブプロセスが使用されていないsocket fdは、マルチプロセス環境ではcloseが参照カウントを1だけ減らし、すべてのプロセスがcloseになり、参照カウントが0になるまでカーネルがFINを送信しないため閉じます.
8ここでは直接終了します.そうしないと、サブプロセスの後ろの文もwhileループになり、forkにも実行されます.それは2層だけでなく、ネストされ続け、サブプロセスのサブプロセスのサブプロセスになります.