2017-02-14 13 views
0

私はシェルをcで書こうとしています。その一部は、SIGTSTP信号を捕捉し、プログラムをフォアグラウンド専用モードに出し入れするように設定するハンドラです。シェルはc:キーボードのEnterキーを押した後にSIGTSTPにしか応答しません。

: ^Z 
Entering foreground-only mode (& is now ignored) 
: //":"should show up own automatically on the next line after I press ctrl-z, then enter 
: ^Z 
Exiting foreground-only mode 
: 
:私は出力がこのisteadのように見えることができます願ってい

: ^ZExiting foreground-only mode    //pressed ctrl+z 
      //nothing here, had to press enter again 
:   //pressing enter just gives me another :, which is what I want 
: ^ZEntering foreground-only mode (& is now ignored) 
^ZExiting foreground-only mode 

//global variables 
int global; 

//header 
void catch_tstp(int); 

//main function 
int main(int argc, char** argv){ 
    ... 

    // initiate sigaction struct for CTRL-Z action 
    struct sigaction ctrlz_act; 
    ctrlz_act.sa_handler = catch_tstp; 
    ctrlz_act.sa_flags = SA_SIGINFO|SA_RESTART; 
    sigfillset(&(ctrlz_act.sa_mask)); 
    sigaction(SIGTSTP, &ctrlz_act, NULL); 
    global = 0; 

    ... 
} 

//handler 
void catch_tstp(int sig){ 
    if(sig == SIGTSTP){ 
     if(global){ 
      global=0; 
      printf("Entering foreground-only mode (& is now ignored)\n"); 
     } 
     else{ 
      global=1; 
      printf("Exiting foreground-only mode\n"); 
     } 
    } 
} 

が今の私の出力は次のようになります。ここでは

はコードの関連するスニペットです

誰かが私が間違っていることを指摘できますか?どんな助けでも大歓迎です。ありがとうございました!

+0

シグナルハンドラから 'global'のような非定数ファイルスコープ変数に安全にアクセスするには、' volatile'で 'sig_atomic_t'型でなければなりません。私はあなたの失敗があなたの問題に直接的な責任はないと思っています。 –

+0

私はそれに応じて 'global'を改訂しましたが、問題は解決されませんでした。 –

+0

また、 'sa_sigaction'メンバーではなく' struct sigaction'の 'sa_handler'メンバーを使用しているので、' SA_SIGINFO'フラグを 'sigaction()'に指定しないでください。これは重大な問題ですが、フラグを削除しても観測された動作は変わらないと思います。 –

答えて

1

シグナルハンドラにはいくつかの問題があり、どのように設定していますか?

私はすでにコメントで述べたように、シグナルハンドラが安全にその変数がvolatileで、sig_atomic_tを入力した場合にのみ、このようなglobalとしてファイル・スコープ変数にアクセスすることができます。他の変数にアクセスしようとすると、その変数に設定されている最新の値が表示されないというリスクがあります。ハンドラのスコープ外のコードは、ハンドラがその変数に書き込む値シグナルハンドラの非アトミックな書き込みの結果、変数の値が壊れることに注意してください。

もすでにコメントで指摘し、より深刻な問題は、あなたがそうであるように、sigaction()SA_SIGINFOフラグを指定すると、あなたが実際にそれを指定しているのに対し、あなたは、あなたのstruct sigactionsa_sigaction部材を介してハンドラを指定していること、それを伝えることですsa_handlerメンバーを通じて。 ハンドラをsa_handlerで指定する場合は、フラグからSA_SIGINFOを省略し、その逆も忘れてはなりません。 SA_SIGINFOフラグを不適切に指定すると、シグナルの受信は、誤った数の引数でハンドラを呼び出すか、ガーベジ関数ポインタを介して関数を呼び出すことによって、未定義の動作を引き起こします。

さらに、シグナルハンドラによって呼び出さすべての機能が非同期シグナルセーフでなければなりません。マニュアルの第7章の「信号」の項目には、安全に呼び出せる機能のリストが含まれており、printf()はその中にはありません。

しかし

がいることをあなたの欲求に干渉する主な問題「:」私はCtrl-Zを

を押した後、次の行に自身を自動的に表示すべきは、そのあなたのように見えますsigactionフラグの中にはSA_RESTARTが含まれています。そのフラグは、EINTRエラーまたは部分的な結果を呼び出し元に返す代わりに、信号ハンドラが終了した後に信号の受信によって自動的に再開されるように中断された(一部の)システム機能を引き起こします。特殊なケースでは、コマンドを読み取っているときにシェルがSIGSTPを受け取った場合、読み取りを再開すると、シェルはシェルに戻り、新しいプロンプトを表示しません。 これは、^ Zの前に入力された文字が、コマンドの読み方の詳細によってはに依存するということもあります。


あなたは^ Zと端末インタフェースと標準のシェルによってSIGSTPの意味と使用についての誤解を有することができるようにまた、あなたのプログラムのメッセージから、それはそうです。これらは、シェルによって実行されるジョブのジョブ制御機能を提供します。主にシェル自体のモードに関するものではありません。シェルがフラッグを保持する必要はなく、(シグナルハンドラ自体のスコープの外側で)^ Zを受け取った後に別の振る舞いをする必要はありません。必要な対話的な振る舞いは、シェルが標準入力フォアグラウンドにいるときだけです。

シェルがSIGSTPを受け、

  • それがフォアグラウンドである場合、それは何もしない(と未処理の入力を失うことを避ける)必要があります。
  • バックグラウンドの場合は、フォアグラウンドプロセスグループにSIGSTOP(違いを書き留めてください)を送信し、必要に応じてフォアグラウンドに戻してください。この場合、メッセージおよび/またはプロンプトが出されることがあります。
+0

詳細な説明ありがとうございます。私は最後の部分について少し混乱しています。私のシェルでは、 '^ Z'ハンドラを追加する前に、'^Z'を入力するたびにシェルは '[1] + Stopped'を出力します。私がしたいことは、元の投稿に記載されているように、「前景のみモードの開始/終了」を表示している間は、前景専用モードになります。 SA_RESTARTフラグを修正した後もまだそれが行われていないので、問題がprintf()の部分にあると仮定しています。どのように私はそれを修正することができます上の任意の提案? –

+0

@AishaAshwal、あなたは間違っています。あなたのシェルが 'SIGSTP'のためのハンドラを全く持っていなければ、その親シェル*はあなたが^ Zとタイプするときに記述する出力を生成します。いずれにせよ、私はあなたがハンドラを実装すべきではないことを示唆していませんでした。私の答えの最後の部分は、ハンドラが何をすべきかについてです*。 –

+0

@AishaAshwal、私が提起したポイントに対処した後のあなたの継続的な難しさについては、あなたが提示していないコードについて推測するのは無意味です。 –

関連する問題