質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.81%

マルチスレッドプログラム(Linux)によるシリアル通信 [ C言語 ]

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 4,417
退会済みユーザー

退会済みユーザー

現在linuxマルチスレッドのプログラムでシリアル通信の受信をやっております.

プログラム起動後数回は受信に成功するのですが, その後全く受信することができません.原因がわかる方がいらっしゃいましたらご教授お願い致します.

シリアルを開く関数
int open_serial_port( char* modem_dev ){
  struct termios newtio;

    fd[0] = open( modem_dev, O_RDWR | O_NOCTTY | O_NONBLOCK );
  if( fd[0] < 0 ){
    fprintf( stderr, "open error\n" );
    return -1;
  }

  fcntl( fd[p], F_SETOWN, getpid() );
  fcntl( fd[p], F_SETFL, FASYNC );
  tcgetattr( fd[p], &oldtio ); //save

  newtio.c_iflag = 0;
  newtio.c_oflag = 0;
  newtio.c_cflag = 0;
  newtio.c_lflag = 0;
  newtio.c_line = 0;
  bzero( newtio.c_cc, sizeof( newtio.c_cc ) );

  cfsetispeed( &newtio, BAUDRATE );
  cfsetospeed( &newtio, BAUDRATE );

  newtio.c_cflag |= ( CLOCAL | CREAD );

  newtio.c_cflag &= ~PARENB;
  newtio.c_cflag &= ~CSTOPB;
  newtio.c_cflag &= ~CSIZE; 
  newtio.c_cflag |= CS8;
  newtio.c_cflag &= ~CRTSCTS;

  newtio.c_lflag &= ~( ICANON | ECHO | ECHOE | ISIG );
  newtio.c_iflag = ( IGNPAR | IGNCR ); 
  newtio.c_iflag &= ( IXON | IXOFF | IXANY );

  newtio.c_oflag &= ~OPOST;

  newtio.c_cc[VTIME] = 0;
  newtio.c_cc[VMIN] = 1;

  /* clear the modem line */
  tcflush( fd[p], TCIFLUSH );
  tcsetattr( fd[p], TCSANOW, &newtio );

  return 0;
}
シリアル受信のスレッドです

void* thread0(){
  struct sigaction action;
  struct sigevent evp;

  k1 = 0;
  k2 = 0;

  pthread_mutex_init( &mutex0, NULL );
  pthread_cond_init( &cond0, NULL );

  memset( &action, 0, sizeof(action) );
  memset( &evp, 0, sizeof(evp) );

  action.sa_sigaction = SignalHandler;
  action.sa_flags = SA_SIGINFO | SA_RESTART;
  sigemptyset( &action.sa_mask );

  evp.sigev_notify = SIGEV_SIGNAL;
  evp.sigev_signo = SIGIO;

  if( sigaction( SIGIO, &action, NULL ) < 0 ){
    perror( "sigaction error" );
    return -1;
  }

  while(1){
    pthread_mutex_lock( &mutex0 );
    pthread_cond_wait( &cond0, &mutex0 );

    memset( buffer, '\0', sizeof(buffer) );

    k = read( fd[0], buffer, sizeof(buffer) );
    printf( "receive char : %d \n" , k );


    if( k > 0 ){

      // save buffer to rs_rbuffer and alz_buffer k byte ( k:受信したバイト数 )
      memcpy( &rs_rbuffer[k1], buffer, k );
      memcpy( &alz_buffer[k1], buffer, k );

      k1 += k;
      k2 += k;

      pthread_cond_signal( &cond1 ); 

   }
    pthread_mutex_unlock( &mutex0 );

  }

   pthread_exit(0);
}
シグナル

// SIGIO handler
void* SignalHandler(int signum, siginfo_t *info, void* ctxt){
  pthread_cond_signal( &cond0 );
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

Pthreadプログラミング一般論の観点から、条件変数(pthread_cond_*)の利用方法が誤っています。特に待機関数pthread_cond_waitは、必ず 通常変数を用いた条件式とループ構造 としなければなりません。

条件変数とミューテックスを使ったスレッド間通知では、下記のようなコード構造をとる必要があります。(ここでは一方向通知を仮定)

int condition; // 待機したい"条件"をあらわす通常の変数
               // 複数の変数からなる複合条件の場合もある
pthread_mutex_t mtx;
pthread_cond_t cv;
// (初期化処理は省略)

// 通知側スレッド
pthread_mutex_lock(&mtx);
condition = 1;/* 待機条件を満たしたことを設定 */
pthread_cond_signal(&cv);
pthread_mutex_unlock(&mtx);

// 待機側スレッド
pthread_mutex_lock(&mtx);
while (!condition/*条件を満たしていない間 wait処理に入る*/) {
  pthread_cond_wait(&cv, &mtx);
}
// 待機条件が満たされた状態で到達
pthread_mutex_unlock(&mtx);

条件変数 Step-by-Step入門 の説明も参考に下さい。


通知スレッドの部分をシグナルハンドラで実装する場合も同様にして実装すればよいでしょうか?

いいえ。Linux(POSIX)シグナルハンドラ内部で行える処理は、通常コードに比べると大幅に制限されます。基本的に、ほとんど全てのCライブラリ関数は呼び出せないと考えてください。JP-CERTの シグナルハンドラ内では非同期安全な関数のみを呼び出す も参照ください。

厳格な話をすると、シグナルハンドラ本体では「volatile sig_atomic_t型の変数を書き換える」か「ごく一部の限られた関数(シグナルセーフ関数)を呼び出すこと」しか行ってはいけません。ミューテックスのロック操作や条件変数への通知操作はシグナルセーフではないため、何が起きても(期待通り動かなくても)文句を言えません。

シグナルハンドラと別スレッドの通信には、volatile sig_atomic_t型の変数を使うか、POSIXセマフォ(sem_post)しか通信の手段がありません。


非同期シグナルハンドラとPthreadマルチスレッド処理を組合せるのは、非常に高度なトピックですし、難解で間違えやすいプログラミングの代表格です。

非同期シグナルハンドラを使うのではなく、sigwait関数による同期的なシグナル待機に書き換えられるのであれば、後者の方がマルチスレッドプログラミングとの親和性が高くなります。詳細は シグナルハンドラを使わないでシグナルをハンドルする や シグナルについて を参照ください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/25 16:27 編集

    通知スレッドの部分をシグナルハンドラで実装する場合も同様にして実装すればよいでしょうか?
    下記します.


    // SIGIO receive handler  通知側
    void* SignalHandler(int signum, siginfo_t *info, void* ctxt){

    pthread_mutex_lock( &mutex0 );
    condition = 1;
    pthread_cond_signal( &cond0 );
    pthread_mutex_unlock( &mutex0 );

    }


    // 受信スレッド
    void* thread0(){
    struct sigaction action;
    struct sigevent evp;
    k1 = 0;
    k2 = 0;

    pthread_mutex_init( &mutex0, NULL );
    pthread_cond_init( &cond0, NULL );

    memset( &action, 0, sizeof(action) );
    memset( &evp, 0, sizeof(evp) );

    action.sa_sigaction = SignalHandler;
    action.sa_flags = 0;
    action.sa_restorer = NULL;
    sigemptyset( &action.sa_mask );

    evp.sigev_notify = SIGEV_SIGNAL;
    evp.sigev_signo = SIGIO;

    if( sigaction( SIGIO, &action, NULL ) < 0 ){
    perror( "sigaction error" );
    return -1;
    }

    while(1){
    pthread_mutex_lock( &mutex0 );

    while( !condition ){
    pthread_cond_wait( &cond0, &mutex0 );
    }
    condition = 0;

    memset( buffer, '\0', sizeof(buffer) );
    k = 0;

    k = read( fd[0], buffer, sizeof(buffer) );

    if( k > 0 ){
    memcpy( &rs_rbuffer[k1], buffer, k );

    k1 += k;
    k2 += k;

    pthread_cond_signal( &cond1 );

    }

    }

    pthread_mutex_unlock( &mutex0 );

    }
    pthread_exit(0);
    }

    キャンセル

  • 2016/10/25 21:06

    非同期シグナルセーフな関数を確認してみましたが、本当に限定されており、プログラムの大幅な改善が必要なように思いました。

    sigwait関数を利用したプログラムを再度コーディングして、
    わからない場所はまた質問致します。
    ありがとうございました。

    キャンセル

0

受信処理部分のデータがなかった場合に、pthread_cond_signalをコールしていないのは
気になります。

受信するデータが無かった場合は、pthread_cond_wait()解除する信号が送られないので
でデッドロックします。
ですので、下記の修正が必要かと思います。

受信処理の一部

if( k > 0 ){
    ・・・省略・・・
#      pthread_cond_signal( &cond1 ); 
}
pthread_cond_signal( &cond1 );  # ここに移動 
pthread_mutex_unlock( &mutex0 );

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/25 10:11

    メインスレッドの他に, pthread_create によって2つのスレッドを作成しております.
    しかし現在は受信がうまく動作していないため、もう一つの処理部分のスレッドはコメントアウトしています.

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.81%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る