Google グループは Usenet の新規の投稿と購読のサポートを終了しました。過去のコンテンツは引き続き閲覧できます。
Dismiss

kbhit_getch 相当の処理方法

閲覧: 96 回
最初の未読メッセージにスキップ

H.Shiozaki

未読、
2002/10/24 3:35:132002/10/24
To:
汐崎と申します。
次の様な処理をしたいのですが,
Linux+gcc 環境で同等処理をする方法のアイデアか,
参考資料を教えていただけませんか?
int kbhit_getch(void) /* 16 bit compiler can do */
{
 int c;
 while(1){
 if(kbhit()==0) continue;
 c = getch();
   /* do something */
if(c=='b')break;
}
}
-----
hshiozaki:_no_space_:@nifty.com

Shinji KONO

未読、
2002/10/24 4:28:572002/10/24
To:
河野 真治@琉球大情報工学です。

In article <3DB7A2B1...@nifty.com>, "H.Shiozaki" <hshi...@nifty.com> writes


>Linux+gcc 環境で同等処理をする方法のアイデアか,
>参考資料を教えていただけませんか?
>int kbhit_getch(void) /* 16 bit compiler can do */

C FAQ に go!

http://www.catnet.ne.jp/kouno/c_faq/c19.html#0

ここは結構重いかな。手元に置いておくと便利ですね。

---
Shinji KONO @ Information Engineering, University of the Ryukyus,
PRESTO, Japan Science and Technology Corporation
河野真治 @ 琉球大学工学部情報工学科,
科学技術振興事業団さきがけ研究21(機能と構成)

Junn Ohta

未読、
2002/10/24 4:47:492002/10/24
To:
fj.os.linuxの記事<3DB7A2B1...@nifty.com>で
hshi...@nifty.comさんは書きました。
> 次の様な処理をしたいのですが,
> Linux+gcc 環境で同等処理をする方法のアイデアか,
> 参考資料を教えていただけませんか?
> ...
>  while(1){
>  if(kbhit()==0) continue;

こんなことをやってはいけません。

キー入力があるか? ないか、そうか...
キー入力があるか? ないか、そうか...
キー入力があるか? ないか、そうか...
キー入力があるか? ないか、そうか...
...

という無駄な処理を入力があるまでくり返してCPU時間
を浪費してしまい、ほかのプログラムに迷惑をかけるだ
けですから。kbhit()を使ってよいのは「キー入力がな
いときに何か処理をしたい」ときだけです。

実現したいことは要するに「エコーバックなしで、改行
を待たずに文字単位でキー入力を受け取りたい」という
ことだけですよね? つまりgetch()があればよい。

簡単なのはcursesライブラリーに含まれるgetch()を使
う方法でしょう。getch()だけのためにcursesを使うの
は無駄が多いので、私は嫌いですけどね。

ちゃんとやろうと思ったらtermiosインターフェースを
使って端末ドライバーを一時的に「エコーバックなしの
文字単位入力」のモードにしてread()でキー入力を読む
ことになります。

というわけで、まあ自分で調べてみてください。それで
わからなかったらまた質問していただければ。
--
太田純(Junn Ohta) (株)リコー/新横浜事業所
oh...@sdg.mdd.ricoh.co.jp

ishida

未読、
2002/10/24 4:36:022002/10/24
To:
これではどうでしょう?


#include <termio.h>
main()
{
struct termio ttyd;
struct termio ttyd_old;
int c;

ioctl(0,TCGETA,&ttyd);
memcpy(&ttyd_old,&ttyd,sizeof(ttyd));
ttyd.c_cc[VMIN]=1;
ttyd.c_cc[VTIME]=0;
ttyd.c_lflag &= ~(ICANON | ISIG | ECHO);
ioctl(0,TCSETA,&ttyd);

while(1) {
c=getchar();

/* do something */

if (c=='b') break;
}

ioctl(0,TCSETA,&ttyd_old);
}

--
(株)タイトー CP第三開発グループ 石田一朋


H.Shiozaki

未読、
2002/10/31 9:58:462002/10/31
To:
汐崎です。
河野 真治さん
http://www.catnet.ne.jp/kouno/c_faq/c19.html#0
FAQ全体をダウンロードし,参考にさせてもらいました。
非常に参考になりました,有難う御座いました。
しかし,直接の解は,有りませんでした。
太田純(Junn Ohta)
石田一朋
皆さん有難う御座います。
石田さんのコードを,一部修正させてもらい,
int getkey(void); と言う関数にまとめました。(後述)
getkey関数の評価(特徴)は,次の通りです。
1)キーを押すと,CRキーを押さ押さなくても返る。
2)キー入力するまで,ウェイトする。
3)ファンクションキーでの押下では,
  3回コールして,3バイト迄得る。
これは,私が求めたいたものです。誠に有難う御座います。
後,出きれば,STDINバッファーをフラッシュして,
別のところに影響しないようにしておきたいと思います。
(検討中です...まだ自信は有りませんが)
-----------------------------------
[2]
実は,別のところでは,kbhit() 相当が必要なのです。
int kbhit(void);
やはりioctl関数かncurses関係だろうと思い,
調べてはいるのですがまだ見えていません。
はっきり言えば,お手上げです。
出きればこれも,参考になるサンプルか,資料を
教えて頂けば助かるのですが?
または,ioctl(0,,)に応答(対応)する関数はこれだと
教えて頂くと,調べる効率が上がるのですが。
宜しくお願いします。
-------------------
[3] getkey関数を以下に示します。
#include <termio.h>
int getkey(void)

{
struct termio ttyd;
struct termio ttyd_old;
int c;

ioctl(0,TCGETA,&ttyd);
memcpy(&ttyd_old,&ttyd,sizeof(ttyd));
ttyd.c_cc[VMIN]=1;
ttyd.c_cc[VTIME]=0;
ttyd.c_lflag &= ~(ICANON | ISIG | ECHO);
ioctl(0,TCSETA,&ttyd);

c=getchar();

ioctl(0,TCSETA,&ttyd_old);
return c;
}
以下は,参考にさせてもらった,石田さんの資料
(私目のまずい質問を配慮して頂いたものです)
----------

Junn Ohta

未読、
2002/10/31 12:50:222002/10/31
To:
fj.os.linuxの記事<3DC14526...@nifty.com>で
hshi...@nifty.comさんは書きました。

> 石田さんのコードを,一部修正させてもらい,
> int getkey(void); と言う関数にまとめました。(後述)
> getkey関数の評価(特徴)は,次の通りです。
> 1)キーを押すと,CRキーを押さ押さなくても返る。
> 2)キー入力するまで,ウェイトする。
> 3)ファンクションキーでの押下では,
>   3回コールして,3バイト迄得る。
> これは,私が求めたいたものです。誠に有難う御座います。
> 後,出きれば,STDINバッファーをフラッシュして,
> 別のところに影響しないようにしておきたいと思います。

kbhit()があれば
char ch;
while (kbhit())
read(0, &ch, (size_t)1);
で実現できそうですね。

> 実は,別のところでは,kbhit() 相当が必要なのです。
> int kbhit(void);
> やはりioctl関数かncurses関係だろうと思い,
> 調べてはいるのですがまだ見えていません。
> はっきり言えば,お手上げです。
> 出きればこれも,参考になるサンプルか,資料を
> 教えて頂けば助かるのですが?

select()あたりを使うと実現できます。

ずっと前のやりとりを再投稿しておきます。

| From: oh...@src.ricoh.co.jp (Junn Ohta)
| Newsgroups: fj.lang.c,fj.sys.sun,fj.sys.luna
| Subject: Re: kbhit() in Unix
| Date: 13 Dec 1994 12:42:33 +0900
| Message-ID: <3cj579...@snoopy.src.ricoh.co.jp>
| References: <SEKE.94D...@leo.sys.es.osaka-u.ac.jp>
| <3c9umu...@snoopy.src.ricoh.co.jp>
|
| 自己フォローです。
|
| In article <3c9umu...@snoopy.src.ricoh.co.jp>
| oh...@src.ricoh.co.jp (Junn Ohta) writes:
| > とりあえず動けばいいや、というのなら話は別ですが、
| > そうでなければもっとちゃんとした方法を学ばれたほう
| > がよろしいかと思います。
|
| あまりいいかげんな方法がまかり通るのを放置しておく
| のも問題なので、kbhit()まわりで私の知っていること
| を書いておきます。1.はUNIX USER 1993 No.2に、2.は
| UNIX USER 1992 No.4に執筆したもの(一部追加あり)な
| ので、手もとにバックナンバーがあったらご覧ください。
| サンプルコードも付属しています。
|
| kbhit()を実現するには、大まかにいって次のような方
| 法があります。括弧の中はその方法が最初に用意された
| システムを指しています。方法はいろいろありますが、
| 実際には古めのSystem V系UNIX(SVR2とかSVR3)を除けば
| ioctl()のFIONREADリクエストを使う方法かselect()を
| 使う方法が利用できるようです。
|
| 1. キー入力の有無を調べる方法
| (a) データを読まずに調べる方法
| データを読まずに未読データの有無を直接調べる。
| 本来のkbhit()の意図に最も近い。
| (1) 未読データのバイト数を調べる
| ・ioctl()のFIONREADリクエストを使う(4.0BSD)
| (2) 未読データの有無を調べる
| ・select()を使う(4.2BSD)
| ・poll()を使う(SVR3)
| HP-UXではpoll()よりselect()の方がよい。
| ・ioctl()のI_NREADリクエストを使う(SVR3)
| (3) System V cursesを頼る(SVR3)
| ・文書化されていない関数_chkinput()を使う
| 内部的にはノンブロッキングI/Oで実現され
| ている可能性があるので、cursesの文字入力
| 関数との組み合わせで使う必要がある。
| (b) データを読んでみて調べる方法
| read()で読む際に、データがなかったらブロックせ
| ずにただちに返るようにしておき、データが読めた
| かどうかでデータの有無を調べる。kbhit()の目的
| に使うためには、読めてしまったデータはどこかに
| しまっておき、実際の読み込みのときにはそちらを
| 先に使うようにする必要がある。
| (1) リードタイマー機能を利用する
| ・termioのリードタイマーを使う(System V)
| ・termiosのリードタイマーを使う(POSIX)
| (2) ノンブロッキングI/Oを利用する
| ・fcntl()のFNDELAYリクエストを使う(4.2BSD)
| ・fcntl()のO_NDELAYリクエストを使う(System V)
| ・fcntl()のO_NONBLOCKリクエストを使う(POSIX)
| ・ioctl()のFIONBIOリクエストを使う(4.2BSD)
| (3) System V cursesを頼る(SVR3)
| ・nodelay(win,TRUE)の状態でwgetch(win)を使う
| これだけのためにcursesを使うのはやめた方
| がいい。
| (c) データの到着を非同期的に知る方法
| データを読むのではなく、データが用意されたとき
| にシステムに通知してもらう。
| (1) シグナルを利用する
| ・シグナルSIGIOを使う(4.2BSD)
| ・シグナルSIGPOLLを使う(SVR3)
|
| また、これを実現する前提として、データが1文字入力
| されるごとに読み取れ、かつエコーバックも発生しない
| ようなキー入力を実現する必要がありますが、UNIXでは
| 直接そのような関数が存在するわけではなく、端末ドラ
| イバーの状態を適当に設定してから普通のread()を使う
| という方法を取ります。端末ドライバーを設定するには
| 次のような方法を使います。
|
| 2. キー入力をエコーバックなしに1文字ずつ読む方法
| (a) シグナルは受け取りたい場合
| (1) sgttyインターフェース(V7、4.0BSD)
| ioctl()のTIOCGETP/TIOCSETPリクエストでフラ
| グECHOを落とし、CBREAKを立てる。特殊文字の
| 効果を個々に設定したければTIOCGETC/TIOCSETC、
| TIOCGLTC/TIOCSLTCリクエストも併用する。
| (2) termioインターフェース(System V)
| ioctl()のTCGETA/TCSETAリクエストでフラグ
| ICANONとECHOを落とす。同時に特殊文字の効果
| についても設定できる。
| (3) termiosインターフェース(POSIX)
| tcgetattr()/tcsetattr()でフラグICANONとECHO
| を落とす。同時に特殊文字の効果についても設
| 定できる。
| (4) HP-UX(HP-UX)
| HP-UXでは(1)か(3)のいずれかを利用するが、
| どちらを使ってもdsusp特殊文字にアクセスで
| きない。この特殊文字の効果を設定するには、
| <bsdtty.h>をインクルードしてから(1)と同じ
| ioctl()のTIOCGLTC/TIOCSLTCリクエストを使う。
| (b) シグナルも無視したい場合
| (1) sgttyインターフェース(V7、4.0BSD)
| ioctl()のTIOCGETP/TIOCSETPリクエストでフラ
| グECHOを落とし、RAWを立てる。あるいは(a)の
| (1)と同じ方法で割り込み文字を無効にしても
| よい。
| (2) termioインターフェース(System V)
| ioctl()のTCGETA/TCSETAリクエストでフラグ
| ICANONとISIGとECHOを落とす。同時に特殊文字
| の効果についても設定する。
| (3) termiosインターフェース(POSIX)
| tcgetattr()/tcsetattr()でフラグICANONとISIG
| とECHOを落とす。同時に特殊文字の効果につい
| ても設定する。
| (4) HP-UX(HP-UX)
| (a)の(4)と同じ。
|
| もちろん、プログラムを終了する前には端末ドライバー
| を起動時と同じ状態に戻しておく必要があります。
| --
| 太田純(Junn Ohta) (株)リコー ソフトウェア事業部
| oh...@src.ricoh.co.jp/JCF00656@NIFTY-Serve
|
|
| From: oh...@src.ricoh.co.jp (Junn Ohta)
| Newsgroups: fj.lang.c,fj.sys.sun,fj.sys.luna
| Subject: Re: kbhit() in Unix
| Date: 14 Dec 1994 01:11:37 +0900
| Message-ID: <3ckh3p...@snoopy.src.ricoh.co.jp>
| References: <GOUGI.94D...@ix-38.caelum.co.jp>
| <3chpip...@snoopy.src.ricoh.co.jp>
| <GOUGI.94D...@ix-38.caelum.co.jp>
|
| In article <GOUGI.94D...@ix-38.caelum.co.jp>
| go...@ix-02.caelum.co.jp (x11) writes:
| > そうでした。fj.sys.sunなのでSun4.1.XとSolarisでコンパイルできる
| > ものを考えました。すべてのBSDとSVでコンパイル出来るものというのは、
|
| お気付きでないようですが、fj.lang.cとfj.sys.lunaに
| もクロスポストされているのでした。:-)
|
| # lunaというのが曲者で、「lunaで動くもの」と言われ
| # たら、もしかすると「古ーい古ーいSVR2でも動いてほ
| # しい」のかもしれません。警戒が必要です。(^_^;)
|
| > 太田純さんが以前フォローされた
| > >In article <SEKE.94D...@aries.sys.es.osaka-u.ac.jp>
| > ...
| > >comp.sources.miscに最近次のようなものが流れました。
| > か、cursesを利用するのが良いですね。
|
| プログラムが全体としてcursesを利用するならともかく、
| この部分だけが必要なためにcursesを使うことはあまり
| おすすめしません。場合によってはプログラム独自の画
| 面管理と衝突しますし、余計なオーバーヘッドもありま
| すので。
|
| > 魏@大阪大学さんの質問への回答は、この2つを利用すると、自分で作成するのは、
| > なかなか大変ということですね。しかし、利用するだけでなく自分で作成するのは、
| > 楽しいことなので是非魏@大阪大学さんにもkbhit()を作成してほしいですね。
|
| そのUNIXにどういう機能が用意されていて、kbhit()を
| どのように実現できるか考えるのは楽しいのかもしれま
| せんが、不十分なものを作ってハマるのも困りますよね。
| 部品化しておいてみんなで共有できるのがベストです。
|
| もちろん、部品化されたプログラムは自分で理解・咀嚼
| して、自分でもそらで同じものが書けるようになればさ
| らによいですね。
|
| > そこでついで、なんですが
| > ...
| > Sun OS 4.1.X版kbhit() ということで、selectをいれた実装を作成しましたので
| > timeoutについての意見や実装についての指摘とkbhit()の仕様は、
| > わたしは知らないので修正をお願いしたいのですが、もちろんSVとBSDでコンパイル
| > 出来るものに修正することも歓迎です。
|
| 普通kbhit()といえば、「その時点で読み込めるデータ
| があれば非0、なければ0を返す関数」を指します。
|
| > ====== 以下selectをいれたkbhit() ================
|
| 拝見しました。このkbhit()は必ず1文字を読んで返す仕
| 様になっているようですが、それなら単なるread()と変
| わりがありませんよね。timeout刻みでCPUをわずらわせ
| ている分だけ逆に意味がないかもしれません。何のため
| にselect()を使っているのでしょうか。
|
| そこでついでなんですが:-)、kbhit()をさまざまな方法
| で実装したサンプルプログラムを付けておきます。これ
| はUNIX USER 1993 No.2の付録ディスクに入っていたの
| と同じものです。「==>」を目印に手でチョキチョキし
| てください。
|
| # 結局私はあぶり出されてしまったのだろうか。(^_^;)
|
| ==> makefile <==========================================
| ##### シグナル・ハンドラの型の選択 (いずれかの行頭の#を取り除くこと)
| #SIGTYPE = -Dsig_t=int # シグナル・ハンドラがintを返す場合
| SIGTYPE = -Dsig_t=void # シグナル・ハンドラがvoidを返す場合
|
| ##### シグナル・パッケージの選択 (いずれかの行頭の#を取り除くこと)
| SIGPACK = -DBSDSIG # BSD UNIXのreliable signalを持つ場合
| #SIGPACK = # System III/Vの場合
|
| ##### 端末制御システム・コールの選択 (いずれかの行頭の#を取り除くこと)
| TIOCALL = -DSGTTY # V7、BSD UNIXの場合
| #TIOCALL = -DTERMIO # System III/Vの場合
| #TIOCALL = -DTERMIOS # SVR4などPOSIX準拠のUNIXの場合
|
| ##### kbhit()の実現方法の選択 (いずれかの行頭の#を取り除くこと)
| KBHIT = -DUSE_FIONREAD # FIONREADを使う (4.xBSD)
| #KBHIT = -DUSE_SELECT # select()を使う (4.2BSD)
| #KBHIT = -DUSE_POLL # poll()を使う (SVR3)
| #KBHIT = -DUSE_I_NREAD # I_NREADを使う (SVR3)
| #KBHIT = -DUSE_TERMIO # termioのリード・タイマーを使う (SysIII/V)
| #KBHIT = -DUSE_TERMIOS # termiosのリード・タイマーを使う (POSIX)
| #KBHIT = -DUSE_NB -DBSDNBIO # BSDのFNDELAYを使う (4.2BSD)
| #KBHIT = -DUSE_NB -DSYSVNBIO # SysVのO_NDELAYを使う (System V)
| #KBHIT = -DUSE_NB -DPOSIXNBIO # POSIXのO_NONBLOCKを使う (POSIX)
| #KBHIT = -DUSE_FIONBIO # FIONBIOを使う (4.2BSD)
|
| CC = cc
| CFLAGS = -O $(TIOCALL) $(SIGTYPE) $(SIGPACK) $(KBHIT)
|
| OBJS = main.o kbhit.o term.o
| PROG = kbhit
|
| .c.o:
| $(CC) -c $(CFLAGS) $*.c
|
| $(PROG): $(OBJS)
| $(CC) -o $(PROG) $(OBJS)
|
| clean:
| -rm -f $(PROG) $(OBJS)
|
| ==> main.c <==========================================
| /*
| * UNIX USER 1993年2月号「実践 UNIX C」サンプル・プログラム
| * Written by Junn Ohta
| */
|
| #include <stdio.h>
|
| extern int kbhit();
| extern int getch();
| extern void putch();
| extern int tinit();
| extern void tterm();
| extern int exit();
|
| /* メイン・ルーチン */
| void
| main()
| {
| int c;
|
| /* 端末ドライバの初期設定 */
| if (tinit() == -1) {
| fprintf(stderr, "Can't init terminal\n");
| exit(1);
| }
| for (;;) {
| sleep(1);
| if (!kbhit()) {
| putch('.');
| continue;
| }
| while (kbhit()) {
| c = getch();
| putch(c);
| if (c == 'q')
| goto out;
| }
| }
| out:
| /* 端末ドライバの終了処理 */
| tterm();
| putch('\n');
| exit(0);
| }
|
| ==> kbhit.c <==========================================
| #include <stdio.h>
| #include <errno.h>
| extern int errno;
|
| void putch();
| int getch();
| int kbhit();
|
| static void pushback();
|
| unsigned char pbbuf[10];
| int nchars = 0;
|
| /* 出力を書き出す */
| void
| putch(c)
| unsigned char c;
| {
| write(1, &c, 1);
| }
|
| /* 入力を読み戻す */
| static void
| pushback(c)
| int c;
| {
| pbbuf[nchars++] = c;
| }
|
| /* キー入力を読む */
| int
| getch()
| {
| unsigned char c;
| int n;
|
| if (nchars > 0)
| return pbbuf[--nchars];
| while ((n = read(0, &c, 1)) < 0 && errno == EINTR)
| ;
| if (n == 0)
| return -1;
| return (int)c;
| }
|
| /* 以降はさまざまな方式によるkbhit()の実現 */
|
| #ifdef USE_FIONREAD
| #include <sgtty.h>
|
| int
| kbhit()
| {
| int ret, n;
|
| ret = ioctl(0, FIONREAD, &n);
| if (ret != -1) {
| /* 未読データのバイト数はn */
| return n;
| }
| return 0;
| }
| #endif
|
| #ifdef USE_SELECT
| #include <sys/types.h>
| #include <sys/time.h>
|
| int
| kbhit()
| {
| int rfd, ret;
| struct timeval timeout;
|
| timeout.tv_sec = 0; /* 0秒 */
| timeout.tv_usec = 0; /* 0マイクロ秒 */
| rfd = (1 << (0));
| ret = select(1, &rfd, NULL, NULL, &timeout);
| if (ret == 1) {
| /* 標準入力に未読データがある */
| return 1;
| }
| return 0;
| }
| #endif
|
| #ifdef USE_POLL
| #include <poll.h>
|
| int
| kbhit()
| {
| struct pollfd fds[1];
|
| fds[0].fd = 0;
| fds[0].events = POLLIN;
| poll(fds, 1L, 0); /* 0ミリ秒 */
| if (fds[0].revents == POLLIN) {
| /* 標準入力に未読データがある */
| return 1;
| }
| return 0;
| }
| #endif
|
| #ifdef USE_I_NREAD
| #include <stropts.h>
|
| int
| kbhit()
| {
| int ret, n;
|
| n = 0;
| ret = ioctl(0, I_NREAD, &n);
| if (ret > 0 && n > 0) {
| /* 標準入力に未読データがある */
| return n;
| }
| return 0;
| }
| #endif
|
| #ifdef USE_TERMIO
| #include <termio.h>
|
| int
| kbhit()
| {
| unsigned char c;
| int n;
| struct termio old, new;
|
| if (nchars > 0) {
| /* 読み戻された入力がある */
| return 1;
| }
| /* リード・タイマーを設定する */
| ioctl(0, TCGETA, &old);
| new = old;
| new.c_lflag &= ~ICANON;
| new.c_cc[VMIN] = 0;
| new.c_cc[VTIME] = 0; /* 0秒 */
| ioctl(0, TCSETAW, &new);
| /* 入力を読む */
| while ((n = read(0, &c, 1)) < 0 && errno == EINTR)
| ;
| /* リード・タイマーを復元する */
| ioctl(0, TCSETAW, &old);
| if (n == 1) {
| /* 入力を読み戻す */
| pushback(c);
| return 1;
| }
| return 0;
| }
| #endif
|
| #ifdef USE_TERMIOS
| #include <termios.h>
| #include <unistd.h>
|
| int
| kbhit()
| {
| unsigned char c;
| int n;
| struct termios old, new;
|
| if (nchars > 0) {
| /* 読み戻された入力がある */
| return 1;
| }
| /* リード・タイマーを設定する */
| tcgetattr(0, &old);
| new = old;
| new.c_lflag &= ~ICANON;
| new.c_cc[VMIN] = 0;
| new.c_cc[VTIME] = 0; /* 0秒 */
| tcsetattr(0, TCSADRAIN, &new);
| /* 入力を読む */
| while ((n = read(0, &c, 1)) < 0 && errno == EINTR)
| ;
| /* リード・タイマーを復元する */
| tcsetattr(0, TCSADRAIN, &old);
| if (n == 1) {
| /* 入力を読み戻す */
| pushback(c);
| return 1;
| }
| return 0;
| }
| #endif
|
| #ifdef USE_NB
| #ifdef SYSVNBIO
| #include <fcntl.h>
| #define NONBLOCK O_NDELAY
| #endif
| #ifdef BSDNBIO
| #include <sys/file.h>
| #define NONBLOCK FNDELAY
| #endif
| #ifdef POSIXNBIO
| #include <sys/types.h>
| #include <fcntl.h>
| #include <unistd.h>
| #define NONBLOCK O_NONBLOCK
| #endif
|
| int
| kbhit()
| {
| unsigned char c;
| int fdflags, n;
|
| if (nchars > 0) {
| /* 読み戻された入力がある */
| return 1;
| }
| /* ノンブロッキングI/Oを設定する */
| fdflags = fcntl(0, F_GETFL, 0);
| fcntl(0, F_SETFL, (fdflags | NONBLOCK));
| /* 入力を読む */
| while ((n = read(0, &c, 1)) < 0 && errno == EINTR)
| ;
| /* ノンブロッキングI/Oを解除する */
| fcntl(0, F_SETFL, fdflags);
| if (n == 1) {
| /* 入力を読み戻す */
| pushback(c);
| return 1;
| }
| return 0;
| }
| #endif
|
| #ifdef USE_FIONBIO
| #include <sys/ioctl.h>
|
| int
| kbhit()
| {
| unsigned char c;
| int fdflags, n, i;
|
| if (nchars > 0) {
| /* 読み戻された入力がある */
| return 1;
| }
| /* ノンブロッキングI/Oを設定する */
| i = 1;
| ioctl(0, FIONBIO, &i);
| /* 入力を読む */
| while ((n = read(0, &c, 1)) < 0 && errno == EINTR)
| ;
| /* ノンブロッキングI/Oを解除する */
| i = 0;
| ioctl(0, FIONBIO, &i);
| if (n == 1) {
| /* 入力を読み戻す */
| pushback(c);
| return 1;
| }
| return 0;
| }
| #endif
|
| ==> term.c <==========================================
| #include <stdio.h>
| #include <signal.h>
| #include <errno.h>
| extern int errno;
|
| #ifdef SGTTY
| #include <sgtty.h>
| #endif
| #ifdef TERMIO
| #include <termio.h>
| #endif
| #ifdef TERMIOS
| #include <termios.h>
| #include <unistd.h>
| #ifdef VDISCRD /* IBM AIX */
| #define VDISCARD VDISCRD
| #endif
| #ifdef VFLUSHO /* IBM AIX */
| #define VDISCARD VFLUSHO
| #endif
| #ifdef V_DSUSP /* MIPS RISC/os */
| #define VDSUSP V_DSUSP
| #endif
| #ifdef V_FLUSH /* MIPS RISC/os */
| #define VDISCARD V_FLUSH
| #endif
| #ifdef V_LNEXT /* MIPS RISC/os */
| #define VLNEXT V_LNEXT
| #endif
| #ifndef _POSIX_VDISABLE /* for SunOS4.x */
| #define _POSIX_VDISABLE 0xff
| #endif
| #endif
|
| /* 端末ドライバ制御パケット */
| #ifdef SGTTY
| struct sgttyb otty, ntty;
| struct tchars otch, ntch;
| #ifdef TIOCSLTC
| struct ltchars oltc, nltc;
| #endif
| #endif
| #ifdef TERMIO
| struct termio otty, ntty;
| #endif
| #ifdef TERMIOS
| struct termios otty, ntty;
| #endif
|
| int tinit();
| void tterm();
|
| static void setntty(), setotty();
| static sig_t onsignal();
|
| /* 端末ドライバの初期設定 */
| int
| tinit()
| {
| /* 端末ドライバを割り込みあり、エコーバックなし、*/
| /* 文字単位入力、特殊文字無効のモードにする */
| #ifdef SGTTY
| if (ioctl(1, TIOCGETP, &otty) < 0)
| return -1; /* 標準出力が端末でない */
| ntty = otty;
| ntty.sg_flags &= ~(ECHO|CRMOD|XTABS);
| ntty.sg_flags |= CBREAK;
| ioctl(1, TIOCGETC, &otch);
| ntch = otch;
| ntch.t_startc = 0xff;
| ntch.t_stopc = 0xff;
| #ifdef TIOCSLTC
| ioctl(1, TIOCGLTC, &oltc);
| nltc = oltc;
| nltc.t_dsuspc = 0xff;
| nltc.t_flushc = 0xff;
| nltc.t_lnextc = 0xff;
| #endif
| #endif
| #ifdef TERMIO
| if (ioctl(1, TCGETA, &otty) < 0)
| return -1; /* 標準出力が端末でない */
| ntty = otty;
| ntty.c_iflag &= ~(INLCR|ICRNL|IXON|IXOFF|ISTRIP);
| ntty.c_oflag &= ~OPOST;
| ntty.c_lflag &= ~(ICANON|ECHO);
| ntty.c_cc[VMIN] = 1;
| ntty.c_cc[VTIME] = 0;
| #endif
| #ifdef TERMIOS
| if (tcgetattr(1, &otty) < 0)
| return -1; /* 標準出力が端末でない */
| ntty = otty;
| ntty.c_iflag &= ~(INLCR|ICRNL|IXON|IXOFF|ISTRIP);
| ntty.c_oflag &= ~OPOST;
| ntty.c_lflag &= ~(ICANON|ECHO);
| ntty.c_cc[VMIN] = 1;
| ntty.c_cc[VTIME] = 0;
| #ifdef VDSUSP /* V_DSUSPのこともある */
| ntty.c_cc[VDSUSP] = _POSIX_VDISABLE;
| #endif
| #ifdef VDISCARD /* VDISCRD、VFLUSHO、V_FLUSHのこともある */
| ntty.c_cc[VDISCARD] = _POSIX_VDISABLE;
| #endif
| #ifdef VLNEXT /* V_LNEXTのこともある */
| ntty.c_cc[VLNEXT] = _POSIX_VDISABLE;
| #endif
| #endif
| setntty();
|
| /* シグナルの設定 */
| signal(SIGINT, onsignal);
| signal(SIGQUIT, onsignal);
| signal(SIGTERM, onsignal);
| signal(SIGHUP, onsignal);
| #ifdef SIGTSTP
| signal(SIGTSTP, onsignal);
| #endif
| }
|
| /* 端末ドライバの終了処理 */
| void
| tterm()
| {
| /* 端末モードを復元する */
| setotty();
| }
|
| /* 端末モードの設定 */
| static void
| setntty()
| {
| #ifdef SGTTY
| ioctl(1, TIOCSETN, &ntty);
| ioctl(1, TIOCSETC, &ntch);
| #ifdef TIOCSLTC
| ioctl(1, TIOCSLTC, &nltc);
| #endif
| #endif
| #ifdef TERMIO
| ioctl(1, TCSETAW, &ntty);
| #endif
| #ifdef TERMIOS
| tcsetattr(1, TCSADRAIN, &ntty);
| #endif
| }
|
| /* 端末モードの復元 */
| static void
| setotty()
| {
| #ifdef SGTTY
| ioctl(1, TIOCSETN, &otty);
| ioctl(1, TIOCSETC, &otch);
| #ifdef TIOCSLTC
| ioctl(1, TIOCSLTC, &oltc);
| #endif
| #endif
| #ifdef TERMIO
| ioctl(1, TCSETAW, &otty);
| #endif
| #ifdef TERMIOS
| tcsetattr(1, TCSADRAIN, &otty);
| #endif
| }
|
| /* シグナル・ハンドラ */
| static sig_t
| onsignal(sig)
| int sig;
| {
| /* シグナルを無効にする */
| signal(sig, SIG_IGN);
|
| /* 端末モードを復元する */
| setotty();
|
| /* シグナルごとの処理 */
| switch (sig) {
|
| case SIGINT:
| case SIGQUIT:
| case SIGTERM:
| case SIGHUP:
|
| /* 終了する */
| exit(1);
| break;
|
| #ifdef SIGTSTP
| case SIGTSTP:
|
| #ifdef BSDSIG
| /* すべてのシグナルのブロックを解除 */
| sigsetmask(0);
| #endif
|
| /* シグナル・ハンドラを既定値に戻し、*/
| /* 自プロセスにSIGTSTPシグナルを送る */
| signal(SIGTSTP, SIG_DFL);
| kill(getpid(), SIGTSTP);
|
| /*
| * ここでプロセスが中断される ...
| *
| * Zzz...
| *
| * ... SIGCONTでプロセスが再開された
| */
|
| /* シグナルを再び有効にする */
| signal(sig, onsignal);
|
| /* 画面モードの利用を再開する */
| setntty();
|
| break;
| #endif
| }
| }
| --
| 太田純(Junn Ohta) (株)リコー ソフトウェア事業部
| oh...@src.ricoh.co.jp/JCF00656@NIFTY-Serve
| From: shi...@pon.nintendo.co.jp (Takashi SHIRAI)
| Newsgroups: fj.comp.lang.c,fj.questions.unix
| Subject: Re: getch()とkbhit()とrand()で困難
| Date: 17 Jun 1998 03:58:45 GMT
| Message-ID: <6m7etl$qb6$1...@yellow.nintendo.co.jp>
| References: <6m5ji2$vsm$1...@news.iwafune.ne.jp> <6m60h7$l...@ns.src.ricoh.co.jp>
|
|  Neco@任天堂です。
|
| In article <6m60h7$l...@ns.src.ricoh.co.jp>,
| Junn Ohta <oh...@src.ricoh.co.jp> wrote:
| >> C 言語でプログラムを作ってみたのですが、一般的なMS-DOS上
| >> のC の処理系に用意されているgetch() が、UNIXのccという処
| >> 理系では使えませんでした。バッファリングなしの一文字入力
| >> をUNIXでやるにはどうすればいいのでしょうか。
|
| >だいぶむかしの記事の使い回しですが、また投稿してお
| >きます。getch()とkbhit()の実装例です。もはや古くな
| >ってしまって、一部は考古学的価値しか残っていないか
| >もしれませんが...。
| >
| >こういう記事って、年に一度くらいの需要はあるみたい
| >ですね。
|
|  既に定例行事ですね。とゆー訳で、太田さんが出てきたので私も
| お約束の follow を入れます。
|
|  太田さんの記事では否定されていますが、慣れない人が取り敢え
| ず getch() を実現させるという目的ならば、curses ライブラリと
| いう選択も間違ってはいないと思います。
|  いきなり ioctl() は敷居が高過ぎますよね。何事も段階を踏ん
| でからなので、慣れてきたらこういう上級テクニック(?)にも挑戦
| するということで、最初は curses でも構わないでしょう。
|
|  curses を使うコツは、
| 1.#include <curses.h> をソースに追加。
| 2.プログラムの頭で initscr() を実行。
| 3.プログラムの最後で endwin() を実行。
| 4.コンパイル時に -lcurses オプションをつける。
| だけですね。取り敢えずやってみる分には手っ取り早い。
|  4.は、OS 環境によっては -lncurses だったり、加えて -ltermlib
| も要ったりしますので、色々試してみて下さい。
|
| % cat getch.c
| #include <curses.h>
| main()
| {
| int c;
|
| initscr();
| while ((c = getch()) != '\033') printf("%02x\n", c);
| endwin();
| }
|
| % cc -O -o getch getch.c -lcurses -ltermlib
| % ./getch
| a61
| b62
| c63
| ^[%
|
| # 本当は setlocate(LC_CTYPE, "") もやっといた方がいいかな。
|
| --
| %  ΛΛ -^^^^ヽ  任天堂(株) 開発第二部 開発課    %
| % /  \ (  )     白井 隆   (as Neco)   %
| % \ヽ<\ _ > つ    shi...@nintendo.co.jp     %

Shinji KONO

未読、
2002/10/31 13:06:542002/10/31
To:
河野 真治@琉球大情報工学です。

In article <3DC14526...@nifty.com>, "H.Shiozaki" <hshi...@nifty.com> writes


>[2]
>実は,別のところでは,kbhit() 相当が必要なのです。

いや、そんなこと(必要な場合っての)はないってのがC-FAQにかか
れていることだと思います。そもそも何がしたいのかをちゃんと考
えると kbhit() は、いらないのが普通です。と、そういうことが
C-FAQに書かれているんです。

何にもでも例外はあるものだけど、この手のbusy wait が有効なのは、

高速レスポンスが要求される共有バスマルチプロセッサの同期

だけだと思います。それもバスのクロックとプロセッサのクロックのギャップ
が100倍近い現状では正しいかどうか怪しい..

Linux上のゲームマシンでも作っていて、しかも、X-Window ベースでない
とかいうなら、割と面白い問題かも知れない。それでも、僕だったら、
khbit()に相当する別なルーチンを組むだろうな。

>または,ioctl(0,,)に応答(対応)する関数はこれだと
>教えて頂くと,調べる効率が上がるのですが。

直接的には、select でしょ? でも、効率とか言っているあたりが、
既に勉強する気がないって言っているようなものではあるが... い
そがばまわれなんだけど。

>[3] getkey関数を以下に示します。
> ttyd.c_cc[VMIN]=1;

これでは待ちが入ってしまうのでだめです。待たないのが欲しいんですよね。
man temios だと、

Case B: MIN > 0, TIME = 0
A pending read is not satisfied until MIN bytes are received

Busy wait したいなら、
Case D: MIN = 0, TIME = 0
The minimum of either the number of bytes requested or the number of
bytes currently available is returned without waiting for more bytes to
be input. If no characters are available, read returns a value of zero,
having read no data.

でしょう。

でも、僕だったら select する。それがUnix流です。(cf. man selext)

ishida

未読、
2002/11/04 22:48:302002/11/04
To:
石田です。

> int getkey(void); と言う関数にまとめました。(後述)

1文字入力する度にioctl()を呼んでいたらレスポンス悪くならないで
すか? これらは、最初と最後だけやれば良いものです。

問題ないのでしたら構いませんが。


> 実は,別のところでは,kbhit() 相当が必要なのです。

先の例で、
ttyd.c_cc[VMIN]=0;
とすれば、getchar()は入力がなくても直ちに返ってきます。
入力がないと返り値はEOF (-1)になるので、それで判断されては如何
でしょうか。

H.Shiozaki

未読、
2002/12/29 11:38:542002/12/29
To:
質問者の汐崎です。
皆様のおかげで,一応,kbhit(), getch() 相当のものが出来,
mainで,それらをテストする形でまとめています。kbhit6.c は後述です。
誠に有難う御座いました。
また,応答が大変送れて申し訳無く思っています。
特に,太田様には,貴重な資料を有難う御座いました。
---
太田様のコードは,試してみました。
makeの定義の前3項目は,
SIGTYPE = -Dsig_t=void
SIGPACK = -DBSDSIG
TIOCALL = -DTERMIOS
に固定して
KBHITを上から順に#01~#10としてやリました。
簡単な結果レポートは,後述の result.txt です。
まず感じたことは,Linux は,SVR3,4.2BSD等では,どれなだろう
ということで,かなり,迷いましたが,どうも全体としては
POSIX志向らしいことは分かりました。
が,個々のAPは,その作者に拠り,一概には言えない,
と言うことでしょうか。
また,ioctl(), select()等は,私目の様な素人には,
難しいことを痛感しました。
---
この検討の中で,新たな疑問が発生しています。
1) ioctl() を,実際に実効するコードは,どのファイルでしょうか。
  find-grep でかなり調べましたが,途中に Wrapper が有るような感じで,
  対象ファイルがまだ,絞りきれていないのです。
2)そもそも私は,stdin/stdout をどの様にして実現しているかが
  興味があるものの単に利用していただけだったと痛感しました。
  また,この実現方法についての解説資料はあまり無いような気がしています。
  参考になる資料を教えてくださればと思っています。
3)kbhit()の実現は,将来的には,ncursesライブラリを利用する
  のが(私の場合には)本命の様に思われます。
  (ncursesは,インストールの途中です。
  その中で,SIGTYPE = -Dsig_t=void のチェックがあり,
  太田さんのMakeを絞りこめたのでやって見る気になりました: 感想です)
4)添付ファイル kbhit6.c の場合,ほとんど同じコードの
  kbhit() と until_kbhit() ですが,後者は,前者に比べて
  その部分のスピードが,以下の様に,3倍ほど速いのですが。
  kbhit() は 5000[Clock] 程度です。
until_kbhit()は 1500[Clock]程度です。
  私にとっては好都合ですが,なぜでしょうか?
5)kbhit.c の動作テストは,Linux-2.0.35, Linux-2.2.17, Linux-2.4.19 で確認
しています。
  FreeBSD-4.4 ではコンパイルエラーです。
  また,より古いLinuxバージョンで作成したものは,
  新しいLinuxバージョンで動作しますが,
  Linux-2.2.17 で作成したものは,linux-2.0.35環境では,
  Segmentation fault となり動作しません。
  しかし,Linux-2.4.19 で作成したものは Linux-2.2.17環境で動作しました。
  それで,今時では,Linux-2.0xはもう古く,使っていない方が多いならば,
  そのバージョンは無視してしまいたいと思っています。
  この辺の判断する情報は,どこかに有りますでしょうか?
  例えば,使っているLinuxバージョンの統計情報とかですが。
---
最後になりますが,kbhit()と使い道は,
メイン処理部分に,インターラクテブ処理部分と非インターラクテブ処理部分
からなる場合に,非インターラクテブ処理部分からインターラクテブ処理に
切替えるのに使いたいと思っています。
それで出来るだけkbhit()は処理時間が短いことが,望まれるのです。
以上です。
----以下は,kbhit.c のファイル(下手な英文ベース)です。

/* kbhit6.c : simulate kbhit, getch for interactive operations
in Linux environment, using <ioterms> method.
gcc -v -Wall -o kbhit kbhit6.c
note1: This program works on Pentuim CPU or compatibles.
tested: Linux-2.4.19, -2.2.17, -2.0.35 ...work OK
tested: FreeBSD-4.4 (compile error, FIONREAD not defined)
Bug report to: hshi...@nifty.com

note2: this program is made with much helped in
news://fj.os.linux and http://www.ncad.co.jp/~komata/c-for-unux/
*/

#include <stdio.h>
#include <unistd.h> // read, write, sleep, etc.
//#include <termio.h> // include termios.h, sys/ioctl.h
#include <termios.h> // No <termio.h> in my FreeBSD-4.4
#include <sys/ioctl.h>
//#include <sys/ioctl.h>


#include <errno.h>
extern int errno;

unsigned long long int rdtsc(void);
int kbhit(void);
int until_kbhit(void);
int set_term(int mode);
int test_until_kbhit(void);

int set_term(int mode) /* mode=0: init, 1:=restore */
{
static struct termios ttyd_save;
struct termios ttyd;
if(mode==0){
if(tcgetattr(0, &ttyd)==-1){
perror("tcgetattr");
return(-1);
}
//memcpy(&ttyd_save, &ttyd, sizeof(ttyd));
ttyd_save = ttyd;
/* set input mode: no_signal at break, no strip */
ttyd.c_iflag &=~(BRKINT | ISTRIP |IXON);
/* set local mode: disable Erase & Kill, no Special controls */
ttyd.c_lflag &=~(ICANON|IEXTEN|ECHO|ECHOE|ECHOK|ECHONL); //ISIG
ttyd.c_cc[VMIN]=1; /* min. charcter */
ttyd.c_cc[VTIME]=0; /* timout, 0.1 sec unit */
if(tcsetattr(0, TCSANOW, &ttyd)==-1){
perror("tcsetattr");
return(-1);
}
return(0);
}
else{ /* mode=1: restore termio */
if(tcsetattr(0, TCSANOW, &ttyd_save)==-1){
perror("tcsetattr");
return(-1);
}
return(0);
}
}

/* kbhit: return number of char(s) in stdin at this moment.
application may do n times getchar to clean stdin buffer
*/
int kbhit(void)
{
int n, rv;
rv = ioctl(0, FIONREAD, &n);
if(rv == -1 || n == 0) return(0);
else return(n);
}

/* until_kbhit is like !kbhit().
but, this is useful for while(until_kbhit) application
cuurently, until_kbhit processing time is much fast than kbhit.
untill_kbhit's process_time is hoped as short as possible.
*/
int until_kbhit(void)
{
int n, rv;
rv = ioctl(0, FIONREAD, &n);
if(rv == -1 || n == 0) return(1);
else return(0);
}
/*** cut cut cut cut cut cut cut cut cut ***/
int main(void) /* test for kbhit and related stuffs. */
{
int i, j, c, n, rv;
unsigned long long int rdtsc0;
int tsc;
printf("kbhit: test start...\n");
rv = set_term(0); // init_term
if(rv==0) printf("kbhit: set INTERRACTIVE termio mode, initialized
OK.\n");
printf("------------------------------------------\n");
rdtsc0 = rdtsc();
n = kbhit();
tsc = rdtsc() - rdtsc0;
printf("kbhit: returned immediately with kbhit_n=%d...OK.\n", n);
printf("kbhit: this process time is about=%d[CPU_CLK].\n", tsc);
printf("------------------------------------------\n");
printf("test_until_kbhit:\n");
if(!(test_until_kbhit()))
printf("test_until_kbhit: end OK.\n");
printf("------------------------------------------\n");
printf("kbhit: NOW Starting kbhit function detailed...\n");
printf("kbhit: type < Normal key > to check kbhit operation.\n");
while(1) if(n = kbhit()) break;
c = getchar();
printf("kbhit: detected kbhit_n=%d, hex=%02Xh, c=<%c>\n", n, c, c);
printf("------------------------------------------\n");
printf("kbhit: type < F1 key > to check kbhit operation.\n");
while(1) if(n = kbhit()) break;
printf("kbhit: detected kbhit_n=%d, and read these chars as below:\n",
n);
for(i=1; i<=n; i++){
c = getchar();
printf("[%d]=%02Xh<%c> ", i, c, c);
}
printf("\n");
printf("------------------------------------------\n");
printf("kbhit: test getchar: at INTERRACTIVE termio mode, No Enter key
need.\n");
printf("kbhit: type <any key> to test kbhit, type < SPace key > to
end.\n"); i=1;
while(1){
n = kbhit();
if(n==0) continue;
printf("[%2d]: ", i++);
for(j=1; j<=n; j++) {
c = getchar();
printf("[%d]: h=%02Xh<%c> ", j, c, c);
}
printf("\n");
//printf("[%3d(kbhit=%d)]: hex=%02Xh, c='%c', d=%d \n", i++, j, c, c,
c);
if(n==1 && c==' ') break;
}
printf("kbhit: kbhit section end...\n");
set_term(1); // restore term
printf("------------------------------------------\n");
printf("kbhit: returned _NORMAL_ termio mode, we test getcar().\n");
printf("Type < Any key(s) > and < Enter key > to program end.\n");
c = getchar();
if(c==' ') printf("getchar() function behabior becomes as usual?\n");
return 0;
}
/* main end */
int test_until_kbhit(void)
{
int c, i, n1, n2, n3, kb;
unsigned long long int tsc0;
int tsc;
tsc0 = rdtsc();
kb = until_kbhit();
tsc = rdtsc() - tsc0;
printf("until_kbhit: returned immediately with until_kbhit=%d\n", kb);
printf("until_kbhit: this process time is about %d[CPU_CLK]\n", tsc);
printf("Type <Any key> to end this section.\n");
while(until_kbhit()){
sleep(1);
printf(".nokb");
fflush(stdout);
}
printf("\n");
n1 = kbhit();
fflush(stdin); n3 = kbhit();
if(n3) printf("note: fflush(stdout) is effective, but fflush(stdin) is
no effective.\n");
for(i=0; i<n1; i++) c = getchar();
n2 = kbhit();
if(n2) {
printf("cannot cleaned stdin bufffer\n");
return(-1);
}
printf("stdin is cleaned using n time(s) getchar().\n");
return(0);
}

unsigned long long int rdtsc(void)

{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return (x);
}
----以下は,result.txt です。
## makefile for kbhit


##### シグナル・ハンドラの型の選択 (いずれかの行頭の#を取り除くこと)
#SIGTYPE = -Dsig_t=int # シグナル・ハンドラがintを返す場合
SIGTYPE = -Dsig_t=void # シグナル・ハンドラがvoidを返す場合

##### シグナル・パッケージの選択 (いずれかの行頭の#を取り除くこと)
SIGPACK = -DBSDSIG # BSD UNIXのreliable signalを持つ場合
#SIGPACK = # System III/Vの場合

##### 端末制御システム・コールの選択 (いずれかの行頭の#を取り除くこと)
#TIOCALL = -DSGTTY # V7、BSD UNIXの場合


#TIOCALL = -DTERMIO # System III/Vの場合

TIOCALL = -DTERMIOS # SVR4などPOSIX準拠のUNIXの場合

##### kbhit()の実現方法の選択 (いずれかの行頭の#を取り除くこと)
#KBHIT = -DUSE_FIONREAD # FIONREADを使う (4.xBSD) #01
#KBHIT = -DUSE_SELECT # select()を使う (4.2BSD) #02
#KBHIT = -DUSE_POLL # poll()を使う (SVR3) #03
#KBHIT = -DUSE_I_NREAD # I_NREADを使う (SVR3) #04
#KBHIT = -DUSE_TERMIO # termioのリード・タイマーを使う (SysIII/V)#05
#KBHIT = -DUSE_TERMIOS # termiosのリード・タイマーを使う (POSIX) #06
#KBHIT = -DUSE_NB -DBSDNBIO # BSDのFNDELAYを使う (4.2BSD) #07
#KBHIT = -DUSE_NB -DSYSVNBIO # SysVのO_NDELAYを使う (System V) #08
#KBHIT = -DUSE_NB -DPOSIXNBIO # POSIXのO_NONBLOCKを使う (POSIX) #09
KBHIT = -DUSE_FIONBIO # FIONBIOを使う (4.2BSD) #10
--------------------------------------------------------------------
Test Results are as follow: Environment: Linux-2.2.17
#00: compile-er:
#01: compile-ok: operation-ng (normal-key-ok, F6-key-ng)
#02: compile-ok: operation-ng (normal-key-ok, F6-key-ng)
#03: compile-ok: operation-ng (normal-key-ok, F6-key-ng)
#04: compile-ok: operation-ng (kb-no-detec)
#05: compile-ok: operation-ng (2characters out per one kb_type)
#06: compile-ok: operation-ng (kb-no-detec)
#07: compile-er:
#08: compile-er:
#09: compile-er:
#10: compile-ok: operation-ng (normal-key-ok, F6-key-ng)
---以上です
: hshiozaki:_no_space_:@nifty.com

Junn Ohta

未読、
2002/12/30 5:54:042002/12/30
To:
fj.os.linuxの記事<aun8rm$4bs$1...@news511.nifty.com>で
hshi...@nifty.comさんは書きました。

> 1) ioctl() を,実際に実効するコードは,どのファイルでしょうか。

「実効する」の意味がわからないのですが、ioctl()は
Linuxではシステムコールなので、カーネルの中で実行
されています。

> 4)添付ファイル kbhit6.c の場合,ほとんど同じコードの
>   kbhit() と until_kbhit() ですが,後者は,前者に比べて
>   その部分のスピードが,以下の様に,3倍ほど速いのですが。
>   kbhit() は 5000[Clock] 程度です。
> until_kbhit()は 1500[Clock]程度です。
>   私にとっては好都合ですが,なぜでしょうか?

コードの違いは真偽の判定が反転しているだけのような
ので、実行速度に違いがあるはずがありません。測定に
使っているクロックの粒度の問題か、kbhit()のあとに
until_kbhit()を呼んでいるのでioctl()を処理している
コードがCPUキャッシュに入ったために高速に実行でき
ているかでしょう。キー入力がない状態でkbhit()なり
until_kbhit()なりの数千~数万回呼んでみて、合計の
消費時間を呼び出し回数で割ってみてください。

> 最後になりますが,kbhit()と使い道は,
> メイン処理部分に,インターラクテブ処理部分と非インターラクテブ処理部分
> からなる場合に,非インターラクテブ処理部分からインターラクテブ処理に
> 切替えるのに使いたいと思っています。
> それで出来るだけkbhit()は処理時間が短いことが,望まれるのです。

非インタラクティブ処理の比重が大きくて、しかも高速
性が求められるなら、kbhit()でポーリングするよりも、
インタラクティブ処理と非インタラクティブ処理でスレ
ッドを分けたらどうですかね。で、インタラクティブ処
理はread(0, ...)でキー入力を待っていて、入力があっ
たら非インタラクティブ処理のスレッドにシグナルを送
ってちょっと待っていてもらう、と。そうすれば非イン
タラクティブ処理のほうはkbhit()のコストもいらず自
分勝手にばんばん処理を実行しているだけですむことに
なりますし。

コードのほうも眺めてみました。

> while(1) if(n = kbhit()) break;

前にも書きましたが、こういうことはやってはだめです。
キー入力があるまでCPU時間を無駄遣いして、ほかのプ
ロセスに迷惑をかけるのですから。

このコード断片で何かがテストできているかといえば何
もテストできていないわけで、こういうコードを書いて
しまうセンスは根本から叩き直さないとだめです。

> while(1){
> n = kbhit();
> if(n==0) continue;

これも同じですね。

> fflush(stdin); n3 = kbhit();
> if(n3) printf("note: fflush(stdout) is effective, but fflush(stdin) is
> no effective.\n");

移植性というか、どこででも通用するプログラムを書こ
うという気があるなら、入力に対してfflush()を使って
はだめです。特定の環境でのみ動けばよいプログラムを
書くにしても、「stdinに対してfflush()が使える」と
マニュアルに明記されている環境でないかぎり、やはり
使ってはだめです。

それ以前の問題として、kbhit()のような関数を使うつ
もりなら、キー入力はread(0, ...)で読むべきであって、
getchar()を使うべきではありません。標準入出力パッ
ケージは入出力をバッファリングすることによって効率
向上を図るしくみなので、kbhit()のような機能とは目
的が正反対だからです。

たとえば標準入力のバッファーが空の状態でgetchar()
を実行したとき、たまたまOS側のキー入力バッファーに
2文字の入力があったとすると、getchar()はその2文字
を読んでしまい、後の1文字を標準入力のバッファーに
残して先の1文字を値として返すことになります。この
標準入力のバッファーにある1文字はkbhit()にとっては
存在しないものなので、getchar()で読める文字がある
にもかかわらずkbhit()を実行すると0が返るという状況
が発生することになります。

新着メール 0 件