pipeを使用してプログラム本文で信号をキャプチャして処理する


前回の論文では,信号処理関数ではなくプログラムの本文で信号をキャプチャして処理する方法を検討した.当時使われていた案はsigprocmask()でした.しかし、その方法は理論的にいくつかの信号を漏らす可能性がある.
本当に安全な方法は、プロセス/スレッド間通信手段を用いて、信号処理関数で信号を外部に送信し、プログラム本文でこれらのデータを傍受することである(epollselectなど).
これはグローバル変数を使用する必要があります.私はまだグローバル変数を使用しないスキームはありません.
本住所:https://segmentfault.com/a/1190000009280819
Reference & Related
「UNIX環境高度プログラミング」libeventソース深さプロファイリングsigprocmaskとsigpendingを使用してプログラム本文で信号をキャプチャし、処理する
きほんげんり
キャプチャ信号が設定される前に(signal())、まず通信チャネルが作成される.割り込み処理関数では、取得した信号数をこのチャネルに書き込む.一方,プログラム本文では,このチャネルを読み出し,プログラム本文で信号をキャプチャすることができる.
これは、実は「libeventソースコード深さプロファイル」でいうlibeventがevsignalを実現する案を参考にしたものです.信号処理関数では,printfなどのstdioライブラリの関数を呼び出さないのが一般的であることはよく知られている.理由は「UNIX環境高度プログラミング」の「信号」章の「再入力可能関数」の節を参照してください.
この機能を実現する上で最も重要なread / write関数は、信号処理関数で呼び出すことができます!これが本案の原理的基礎である.
メリット
  • 通信チャネルは基本的にFIFOであるため、信号が複数回生成されると、プログラム本文もキューから送られてきたデータを受け取ることができ、複数の信号を逃すことを避けることができる.
  • 信号処理関数は非常に短く、write()を呼び出し、極めて少ないデータを書き込むだけでよい.
  • 大量のデータ処理はプログラム本文に置かれ、信号を取得する方法は簡単で、read()にすぎない.また、取得毎のデータ長は一定である:signumのタイプはintであり、sizeof(int)バイトのデータを取得すればよい.
  • pipeは、read / write APIを直接使用して操作することができ、非同期I/Oライブラリを容易にドッキングすることができる.

  • pipeを選択した理由
    「libeventソース深さプロファイリング」においてlibeventはUNIX socket(AF_UNIX)を使用していると記載されている.ここで私はこの案を使わずにpipeを使いました.理由は以下の通りです.
  • pipe単純
  • の初期化と作成
  • pipeの作成は非ネーミングであり、生存サイクルはプロセス内部のみであり、複数のプロセスが同じアーキテクチャを使用することもなく、ネーミング競合の問題が発生する
  • AF_UNIXは名前付きであり、プロトコルスタックが複雑であり、システムオーバーヘッドがやや大きい
  • 一般的にpipeは親子プロセス間の通信に用いられ、「UNIX環境高度プログラミング」の原文では「単一プロセスのパイプはほとんど役に立たない」とも述べている.私はハハハと笑った--本稿の応用シーンでは、実際にはpipeが数少ない単一プロセス内で使用されている.
    コード実装
    私は自分でepollベースの非同期I/Oライブラリ(GitHubリンク)を設計しています.現在、libeventのような一般的なeventとevsignalが実現されています.この実装に興味があれば、私のプロジェクトに直接コードを見ることができます.本文の内容は主にepEventSignal.cファイルの中の実現である.
    主なのは実際にはepEventSignal_AddToBase()関数です.この関数の操作フローは次のとおりです.
  • 呼び出しpipe()パイプ
  • を作成する.
  • nonblock、closeonexecオプション
  • を設定
  • は、pipe[0]をグローバル変数
  • に割り当てる.
  • は、sigaction()関数を使用して信号をキャプチャする.信号処理関数では、信号値をpipe[0]
  • に書き込む.
  • は、pipe[1]epollに登録する、読み出しイベント
  • をキャプチャする.
    私は自分の簡単なテストプログラムtest_server.cでは、SIGQUIT(無視、出力のみ)とSIGINT(event loopの安全な終了をトリガー)の2つの信号がキャプチャされている.読者はチェックアウトしてみてください.
    何か問題があったら教えてください~~~~