web-dev-qa-db-ja.com

LinuxでCのノンブロッキングコンソールI / Oを行うにはどうすればよいですか?

C/Linux/OS XでノンブロッキングコンソールIOを実行するにはどうすればよいですか?

32

そうではありません。 TTY(コンソール)は非常に限られたデバイスであり、非ブロッキングI/Oはほとんど行いません。 curses/ncursesアプリケーションなどで、非ブロッキングI/Oのように見えるものを見たときに行うことは、raw I/Oと呼ばれます。 raw I/Oでは、文字の解釈、消去処理などはありません。代わりに、他のことをしながらデータをチェックする独自のコードを記述する必要があります。

最新のCプログラムでは、コンソールI/Oをthreadまたは軽量プロセスに入れることで、この別の方法を簡素化できます。その後、I/Oは通常のブロッキング方式で続行できますが、データをキューに挿入して別のスレッドで処理できます。

更新

cursesチュートリアル がこれをさらにカバーしています。

10
Charlie Martin

Pete Kirkham のように cc.byexamples.com が見つかりました。 ncursesのバージョンだけでなく、問題についての適切な説明を参照してください。

私のコードでは、標準入力またはファイルから初期コマンドを取得し、初期コマンドの処理中にキャンセルコマンドを監視する必要がありました。私のコードはC++ですが、scanf()と、C++入力関数getline()を使用する残りを使用できるはずです。

Meatは、利用可能な入力があるかどうかをチェックする関数です。

#include <unistd.h>
#include <stdio.h>
#include <sys/select.h>

// cc.byexamples.com calls this int kbhit(), to mirror the Windows console
//  function of the same name.  Otherwise, the code is the same.
bool inputAvailable()  
{
  struct timeval tv;
  fd_set fds;
  tv.tv_sec = 0;
  tv.tv_usec = 0;
  FD_ZERO(&fds);
  FD_SET(STDIN_FILENO, &fds);
  select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
  return (FD_ISSET(0, &fds));
}

これは、stdin入力関数の前に呼び出す必要があります。この関数を使用する前にstd :: cinを使用した場合、再びtrueを返すことはありませんでした。たとえば、main()には次のようなループがあります。

int main(int argc, char* argv[])
{ 
   std::string initialCommand;
   if (argc > 1) {
      // Code to get the initial command from a file
   } else {
     while (!inputAvailable()) {
       std::cout << "Waiting for input (Ctrl-C to cancel)..." << std::endl;
       sleep(1);
     }
     std::getline(std::cin, initialCommand);
   }

   // Start a thread class instance 'jobThread' to run the command
   // Start a thread class instance 'inputThread' to look for further commands
   return 0;
}

入力スレッドでは、新しいコマンドがキューに追加され、jobThreadによって定期的に処理されました。 inputThreadは次のようになりました。

THREAD_RETURN inputThread()
{
  while( !cancelled() ) {
    if (inputAvailable()) {
      std::string nextCommand;
      getline(std::cin, nextCommand);
      commandQueue.lock();
      commandQueue.add(nextCommand);
      commandQueue.unlock();
    } else {
        sleep(1);
    }
  }
  return 0;
}

この関数はおそらくmain()に含まれていた可能性がありますが、私は既存のコードベースで作業しており、それに対してではありません。

私のシステムでは、改行が送信されるまで利用可能な入力はありませんでした。入力時にすべての文字を読みたい場合は、stdinで「標準モード」をオフにする必要があります。 cc.byexamples.com にはまだ試したことのない提案がいくつかありますが、残りは機能しているので、動作するはずです。

19
jwhitlock

私は例を追加したい:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char const *argv[])

{
    char buf[20];
    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
    sleep(4);
    int numRead = read(0,buf,4);
    if(numRead > 0){
        printf("You said: %s", buf);
    }
}

このプログラムを実行すると、標準入力に入力を提供するのに4秒かかります。入力が見つからない場合、ブロックせずに単純に戻ります。

2つのサンプル実行:

Korays-MacBook-Pro:~ koraytugay$ ./a.out
fda 
You said: fda
Korays-MacBook-Pro:~ koraytugay$ ./a.out
Korays-MacBook-Pro:~ koraytugay$ 
14
Koray Tugay

ncursesなしのループ内の非ブロッキングユーザー入力 」をブックマークしました。今月初めに、非ブロッキング、非バッファーコンソール入力が必要かもしれないと思ったのですが、そうではなかったので、保証できませんそれが機能するかどうか。私が使用するために、ユーザーがEnterキーを押すまで入力が得られないことを気にしなかったので、 aio を使用してstdinを読み取りました。

6
Pete Kirkham
4
Aziz

ncurses を使用します

3
soulmerge

Ncursesまたはスレッドを使用する別の方法は、 GNU Readline を使用することです。具体的には コールバック関数を登録する を許可する部分です。パターンは次のとおりです。

  1. (他の記述子の中で)STDINでselect()を使用します
  2. Select()からSTDINの読み取り準備ができたことが通知されたら、readlineのrl_callback_read_char()を呼び出します。
  3. ユーザーが完全な行を入力した場合、rl_callback_read_charはコールバックを呼び出します。それ以外の場合は、すぐに戻り、他のコードを続行できます。
2
Tyler McHenry

「コンソールIO」が何を意味するのか完全にはわかりません-STDINから読んでいますか、またはこれは他のソースから読み取るコンソールアプリケーションですか?

STDINから読み取る場合は、fread()をスキップし、read()およびwrite()をpoll()またはselect()とともに使用して、呼び出しがブロックされないようにする必要があります。 setbuf()を使用すると、freadがEOFを返す入力バッファリングを無効にできる場合がありますが、私は試したことはありません。

0
Don Werve