SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ファイルディスクリプタについて

ファイルディスクリプタについて(7)
~シグナル駆動I/Oの紹介

第7回

  • X ポスト
  • このエントリーをはてなブックマークに追加

 ファイルディスクリプタは、プログラムの外部との入出力を行う為の抽象的なインタフェースです。Unix/Linux におけるファイルディスクリプタは、一般的なファイルだけではなく、デバイスやソケットやパイプも対象としております。当レポートは、ファイルディスクリ プタにまつわる色んな機能と管理方法等を提示したいと思っております。

  • X ポスト
  • このエントリーをはてなブックマークに追加

はじめに

 前回は、各種多重I/Oの紹介や性能値を測定し、傾向を紹介しました。今回は、ディスクリプタの入出力イベントで発生するシグナルイベントを補足することでデータを取得する入出力モデルについて説明していきたいと思います。

連載概要

 サンプルプログラムは100行前後程度までは画面に記載します。全プログラムは圧縮してページ上部よりダウンロード可能にしています。makeコマンドでコンパイルできます。i386/x86_64環境で動作確認済みです。

 プログラムのボリューム上、エラー処理や引数チェックなどを省いているので、あらかじめご了解ください。また使用法を誤るとシステムに重大な影響を与える可能性があります。利用する場合は責任のとれる環境において実行するよう、お願いします。

 当トピックでは、実際にプログラムを通して動作確認や性能測定を行うことで、個人的な見解を述べさせていただきます。あくまで個人的な感想に基づいているので、反論や指摘などあるかと思います。指摘や質問などは大歓迎なので、その際はぜひご連絡ください。可能な限りの対応に努めます。

シグナル駆動I/Oについて

 シグナル駆動I/Oとは、ディスクリプタ上で発生する入出力イベントとして発生するSIGIOを捕捉するためのシグナルハンドラを用意し、ハンドラ内でファイルディスクリプタからデータを取得するI/Oです。

 シグナルハンドラでは複雑で時間のかかる処理はなるべく回避しなくてはいけません。複数のファイルディスクリプタが存在する時、シグナルハンドラ内では、どのファイルディスクリプタからデータを吸い上げるのか調べる手間がかかってしまいます。さらに、TCPソケットの場合はコネクションを行った時や切断時にもSIGIOが発生し、かつハンドラ内ではどのような理由で呼ばれたのかまでは分かりません。

 そこで利用する局面としては、プロセス中に使用するファイルディスクリプタはなるべく少なく(できれば1個だけ)、かつコネクションレスなディスクリプタである必要があります。

 もちろんシグナルハンドラを使用することによるデメリットも、そのまま適用されてしまいます。

 また、シグナルはキューイングされないので、ハンドラ内でデータ処理中に同じシグナルが2回連続で発生すると、処理されるデータは1回だけになってしまい、2回目のデータが読みだされません。

 それを回避するため、ディスクリプタは非ブロッキング設定を行う必要があります。

 以上のような縛りもあるので、使用できる条件がかなり限られますが、メインプロセスとは別の場所でデータの送受信を行いたい場合には利用できるでしょう。

サンプルプログラム

 サンプルプログラムは次のとおりです。

sigio_sample.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
in_port_t make_udp_sock( );
void regist_signal( int signo, void ( *func )( int sig ));
void sig_io( int signo );
void * client_threaed( void * arg );
int  sock = 0;

int main( ) {

    in_port_t port = make_udp_sock( );
    regist_signal( SIGIO, sig_io );

    pthread_t pid;
    pthread_create( &pid, 0, &client_threaed, &port );
    pthread_join( pid, 0 );

    close( sock );
    return 0;
}

in_port_t make_udp_sock( ) {

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons( 0 );
    addr.sin_addr.s_addr = INADDR_ANY;
    sock = socket( AF_INET, SOCK_DGRAM, 0 );
    bind( sock, ( struct sockaddr *)&addr, sizeof( addr ));

    fcntl( sock, F_SETOWN, getpid( ));
    fcntl( sock, F_SETFL,  O_ASYNC | O_NONBLOCK );

    socklen_t len = sizeof( addr );
    getsockname( sock, ( struct sockaddr *)&addr, &len );
    return addr.sin_port;
}

void regist_signal( int signo, void ( *func )( int sig )) {

    struct sigaction sa = {
        .sa_handler = func,
        .sa_flags   = SA_RESTART,
    };
    sigemptyset( &sa.sa_mask );
    sigaddset( &sa.sa_mask, signo );
    sigaction( signo, &sa, 0 );
}

void sig_io( int signo ) {
    ( void )signo;

    char getline[1024];
    struct sockaddr_in udpaddr;
    socklen_t len = sizeof( udpaddr );
    while( 1 ) {
        ssize_t rtn = recvfrom( sock, getline, sizeof( getline ), 0,
            ( struct sockaddr * )&udpaddr, &len );
        if( rtn < 0 && errno == EAGAIN ) {
            break;
        }
        printf( "sig_io:[%zd] [%.*s]\n", rtn, ( int )rtn, getline );
    }
}

void * client_threaed( void * arg ) {
    int port = *( unsigned short int * )arg;

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons( ntohs( port ));
    addr.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
    int sock = socket( AF_INET, SOCK_DGRAM, 0 );

    int i;
    for( i = 0; i < 3; i ++ ) {
        sleep( 1 );
        sendto( sock, "hello", 5, 0, ( struct sockaddr *)&addr, sizeof( addr ));
    }
    close( sock );
    return 0;
}

 実装する際のポイントとしては、下記の通りです。

  • SIGIOシグナルイベントに対するハンドラを用意すること
  • プロセス内で管理するディスクリプタは1個、かつグローバルであること
  • ディスクリプタのオーナを設定すること
    (fcntlのF_SETOWN)
  • ディスクリプタでシグナル駆動I/Oを許可すること
    (fcntl F_SETFLでO_ASYNC)
  • ディスクリプタの非ブロッキング設定をすること
    (fcntl F_SETFLでO_NONBLOCK)

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
性能

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
ファイルディスクリプタについて連載記事一覧

もっと読む

この記事の著者

赤松 エイト(エイト)

(株)DTSに勤てます。WebアプリやJavaやLL等の上位アプリ環境を密かに憧れつつも、ず~っとLinuxとかHP-UXばかり、ここ数年はカーネル以上アプリ未満のあたりを行ったり来たりしています。mixiもやってまして、こちらは子育てとか日々の日記メインです。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/4846 2010/03/12 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング