ネットワークプログラミング講座

スレッド型並行サーバ
スレッド型並行サーバ












並行サーバのセッションシーケンス
並行サーバ (TCPEchoServer-Thread.c)
スレッド生成によるメモリ領域の状況
TCPEchoServer-Thread.c main()
int CreateTCPServerSocket(unsigned short port)
int AcceptTCPConnection(int servSock)
void HandleTCPClient(int clntSocket)
forkのシステムコールとスレッドのシステムコール
pthread_create スレッドを生成する
pthread_join スレッドが終了するのを待つ
プログラム比較(例: forkの目覚まし時計)
プログラム比較(例:スレッドの目覚まし時計)
スレッド型並行サーバ
並行サーバのセッションシーケンス
クライアント
サーバ
クライアント
サーバ
(接続要求)
sock
servSock
connect(sock,…)
クライアント
accept(servSock,…)
サーバ
(接続)
sock
servSock
clntSock
pthread_create()
クライアント
サーバ
(データ通信)
sock
servSock
スレッド
clntSock
スレッド型並行サーバ
並行サーバ (TCPEchoServer-Thread.c)



スレッドの利点
 同一プロセスの中で複数のタスク(独立したスタック領
域)を実行できるため、(プロセスの)コピー時間が不要
 コンピュータリソース(コード領域、変数領域、ファイル
/ソケット識別子)を共有し、リソースの消費を少なく出
来る
スレッドの欠点
 子プロセスの制御(kill等)はコマンドラインから可能だ
が、個別スレッドの制御は出来ない
スレッド型並行サーバ(TCPEchoServer-Thread.c)
 プログラムの大部分は、 fork型並行サーバ
(TCPEchoServer-Fork.c)と同じ
スレッド型並行サーバ
スレッド生成によるメモリ領域の状況
スタック領域
変数領域
プログラム
コード領域
スレッドは、同一プロセス
のプログラムコード領域と
変数領域の双方を共有、
スタックのみが独立
スレッド型並行サーバ
TCPEchoServer-Thread.c main()
#include “TCPEchoServer.h”
#include <pthread.h>
/* TCP エコーサーバ用 */
/* POSIXスレッド用 */
void *ThreadMain(void *arg);
/* クライアントスレッドに渡す引数構造体 */
struct ThreadArgs
{
int clntSock;
/* クライアントソケット識別子 */
};
int main(int argc, char *argv[])
{
int servSock;
/* サーバ用ソケット識別子 */
int clntSock;
/* クライアント用ソケット識別子 */
unsigned short echoServPort;
/* サーバポート番号 */
pthread_t threadID;
/* pthread_create()からのスレッドID */
struct ThreadArgs *threadArgs; /* 引数構造体ポインタ */
if (argc != 2)
/* 引数の数が正しいかチェック */
{
fprintf(stderr,"Usage: %s <SERVER PORT>\n", argv[0]);
exit(1);
}
/* 第一引数はローカルポート番号 */
echoServPort = atoi(argv[1]);
servSock = CreateTCPServerSocket(echoServPort);
スレッド型並行サーバ
TCPEchoServer-Thread.c main()
for (;;) /* 永遠に繰返す */
{
clntSock = AcceptTCPConnection(servSock);
動的メモリ
割当
/* クライアント引数用にメモリを確保 */
if ((threadArgs = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs))) == NULL)
DieWithError("malloc() failed");
threadArgs -> clntSock = clntSock;
/* クライアントスレッド作成 */
if (pthread_create(&threadID, NULL, ThreadMain, (void *) threadArgs) != 0)
DieWithError("pthread_create() failed");
printf("with thread %ld\n", (long int) threadID);
}
/* この部分には到達しない */
}
自身のスレッド
IDを得る
void *ThreadMain(void *threadArgs)
{
int clntSock; /* クライアントのソケット識別子 */
/* 戻り時、スレッドリソースの開放 */
pthread_detach(pthread_self());
/* ソケットのファイル記述子を引数から取り出す */
clntSock = ((struct ThreadArgs *) threadArgs) -> clntSock;
free(threadArgs);
/* 引数に割当てられたメモリ開放 */
HandleTCPClient(clntSock);
return (NULL);
malloc() ⇔ free()
}
スレッド型並行サーバ
int CreateTCPServerSocket(unsigned short port)
スレッド型並行サーバ
int AcceptTCPConnection(int servSock)
スレッド型並行サーバ
void HandleTCPClient(int clntSocket)
スレッド型並行サーバ
forkのシステムコールとスレッドのシステムコール

forkシステムコール
 子プロセスが親プロセスの情報を殆ど複製するために、
処理が重い。
 親プロセスや複製された他のプロセスとの情報交換に、
パイプやプロセス間通信を使わなければいけない

スレッドのシステムコール
 同一プロセスの中で、複数のスレッドが生成できる。
 同一プロセスの中で、スレッド間の情報交換に変数を直
接利用できる。
 スレッドを使えば、並行して行うプログラムが容易に書く
ことが出来る。
 スレッドを使う場合、main関数が終了すると全てのスレッ
ドが終了する
スレッド型並行サーバ
pthread_create スレッドを生成する
プロセスのfork()に対応
pthread_create スレッドを生成する
インクルードファイル
#include <pthread.h>
書式 int pthread_create(pthread_t * thread, pthread_attr_t * attr,
void * (*start_routine)(void *), void * arg);
戻値
成功時 0 (スレッド識別子が*threadに設定)
失敗時 0以外の値
*thread
*attr
*start_routine
*arg
スレッド識別子を格納する変数
スレッドの属性、 NULL: デフォルトの属性
スレッドが実行する関数
スレッドが実行する関数の引数
スレッド型並行サーバ
pthread_join スレッドが終了するのを待つ
プロセスのwait()に対応
pthread_join スレッドが終了するのを待つ
インクルードファイル
#include <pthread.h>
書式 int pthread_join(pthread_t th, void **thread_return);
戻値
成功時 0 (thをthread_returnに設定)
失敗時 0以外の値
th
thread_return
終了待ちスレッド
スレッドの戻り値
スレッド型並行サーバ
プログラム比較(例: forkの目覚まし時計)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
Int main(int argc,char *argv[]) {
int dep_time;
dep_time=atoi(argv[1])*60-300;
if (fork()==0) { //子プロセスの生成
sleep(dep_time);
fprintf(stderr,”Leave in 5 min\n”);
}
return EXIT_SUCCESS;
}
$ gcc lv.c –o lv
$./lv 30
$
$ Leave in 5 min
スレッド型並行サーバ
プログラム比較(例: スレッドの目覚まし時計)
#include
#include
#include
#include
<pthread.h>
<stdio.h>
<stdlib.h>
<unistd.h>
int main(int argc, char *argv[]) {
pthread_t thread;
void *print_message(void *args);
$ gcc lv2.c -o lv2 -lpthread
$ ./lv2 30
You have to leave in 5
minutes
$
pthreadライブラリ
が必要
pthread_create(&thread, NULL, print_message, argv[1]);
pthread_join(thread,NULL);
return EXIT_SUCCESS;
}
void *print_message(void *args) {
int dep_time;
dep_time=atoi(args)*60-300;
sleep(dep_time);
fprintf(stderr, "You have to leave in 5 minutes\n");
return NULL;
}