Armadilloフォーラム

usleepにてerrorが返ってくる

marumo_mizuno

2016年6月29日 20時09分

いつもお世話になります。
初めて投稿致します
丸茂電機㈱ 水野と申します。
宜しくお願い致します。

【プログラム構成】
・main( QApplocation )にてForm,QThreadを4つ,TimerThreadを起動しています。
・Formは画面描画用に使用しています。
・CON14_3ピンとCPN14_4ピンを使用してI2CでICからのデータを受け取っています。
・受け取ったデータをTimerThreadでUDPにて送信しています。

【不具合現象】
・ICでの設定書込み待ちで使用しているusleepで時たま戻り値がerror値( -1 )を返します。
 電源を入れ直すかソフトを再起動し直すと
 環境が変わるのか全くでないときと頻繁にでるときがあります。
・他に使用しているtimer関係は
 main起動時にFormを生成し、描画処理用にQTimerをsetInterval( 20msec割り込み )にて使用、
 main起動時にtimer_create( 10msec割り込み )を生成しています。

【お聞きしたい内容】
・usleepはSignalでの割り込みないしtimer_create等といったその他のタイマー関数と同時使用した場合
 動作は未定義とのことを踏まえて、
1.usleepではなくnanosleepを使用した方が良い
2.nanosleepに切替えた後、出来ればnanosleepだけで他timerは使用しない方が良い
3.armadillo-440のkernel側でtimer関係を使用しているアプリケーションがあり、それの影響を受ける可能性はないか
4.フォーラムの過去の投稿で似た内容などがある等ありましたら教えてください。

【環境】
・armadillo-440 LCD拡張付
・atmark-dist-20160126
・linux-2.6.26-at25
・atde4-amd64-qt

以上、何卒宜しくお願い致します。

コメント

access.mihara

2016年6月29日 20時16分

株式会社ACCESS 三原と申します。

> 1.usleepではなくnanosleepを使用した方が良い
> 2.nanosleepに切替えた後、出来ればnanosleepだけで他timerは使用しない方が良い

全ハードウェア共通の Linux man ページにおいてすら「代わりに nanosleep(2) を使うこと」と記されているので、ハードウェアがなんであれ、man ページの指示に従うべきなのでしょう。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/usleep.3.html

access.mihara

2016年6月29日 22時04分

株式会社ACCESS 三原と申します。

失礼なことを書きました。ここで釈明させてください。

nanosleep の Linux man ページ
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/nanosleep.2.html

nanosleep も -1 を返し得ます。そのときは errno を確認して対処していくしかありません。そして、おそらく、nanosleep に変えても -1 が返るのではないかと想像します。きっと相応の理由があります。

ここからは技術は教えられなくて精神論しか言えません。

水野様は組み込み OS を使用した開発で苦労なされたのだと想像します。OS が自分のアプリより信頼性が低く、動作を知っているベンダに問い合わせるしかないことが多々あったと想像します。

しかし Linux を用いた場合は事情が違います。

Armadillo には Linux OS カーネルの他に多数のライブラリ・アプリケーションまでついてきますが、それらは「動くだろう」というものも多く、おそらくアットマークテクノ様もテストしていない項目があると思われます。あるシステムコールの挙動を問い合わせても答えを持っていないことがあります。

ではテストされていないのかというと、Linux OS カーネルや glibc は多方面で使用され頻繁なバグ報告を受けており、供給元において私たちが開発するアプリケーションよりはるかにバグの少ないシステムになっています。だから apt-get コマンドで追加したアプリケーションが何事もなく動くわけです。私たちのアプリケーションより信頼性が低ければ、その上で lighttpd (HTTP サーバ)なんて動いていません。

Linux でマニュアルにエラーが発生すると書かれていれば、長く使用していれば実際に出ます。逆に、マニュアル通りに使っても動かないんじゃないかと疑うと、そんなバグを見つける機会はほとんどありません。

組込み OS の場合と Linux の場合は、誰が信頼できるのかという問題が逆になるんです。組込み OS では OS の方が信頼性が低いものでした。しかし Linux では、OS カーネルは新規開発アプリケーションより信頼性が高いです。アプリケーションがエラーを無視すればバグになり、マニュアル外の動作を疑えば徒労に終わり、どちらも時間を損します。

組込み開発の事情通が Linux 開発の妨害役になるのは皮肉な現実です。

どうすればいいのかというと、マニュアルを信じるしかないんです。Linux は組込み OS に比べて、はるかにマニュアルに嘘の少ないシステムです。マニュアルの指示に最大限従って、それでもダメだったときに初めてバグだと問い合わせるぐらいがいいのです。

綺麗事の力を信じよう。それが Linux 開発の基礎です。

marumo_mizuno

2016年6月30日 13時30分

株式会社ACCESS 三原様

返信遅くなり申し訳ありません。
コメントありがとうございます。

丸茂電機㈱ 水野です。

おっしゃる通り組込OSでの知識を元に記載してしまいました。
Linuxについて勉強不足だった事を改めて痛感しました。
もう一度勉強し直します。

usleepの件については、usleepでerrorが出た際のerrornoを監視し何が原因かの調査を進めています。
また、何かわかり次第こちらにてコメント致します。

> 水野様は組み込み OS を使用した開発で苦労なされたのだと想像します。OS が自分のアプリより信頼性が低く、動作を知っているベンダに問い合わせるしかないことが多々あったと想像します。
>
> しかし Linux を用いた場合は事情が違います。
>
> Armadillo には Linux OS カーネルの他に多数のライブラリ・アプリケーションまでついてきますが、それらは「動くだろう」というものも多く、おそらくアットマークテクノ様もテストしていない項目があると思われます。あるシステムコールの挙動を問い合わせても答えを持っていないことがあります。
>
> ではテストされていないのかというと、Linux OS カーネルや glibc は多方面で使用され頻繁なバグ報告を受けており、供給元において私たちが開発するアプリケーションよりはるかにバグの少ないシステムになっています。だから apt-get コマンドで追加したアプリケーションが何事もなく動くわけです。私たちのアプリケーションより信頼性が低ければ、その上で lighttpd (HTTP サーバ)なんて動いていません。
>
> Linux でマニュアルにエラーが発生すると書かれていれば、長く使用していれば実際に出ます。逆に、マニュアル通りに使っても動かないんじゃないかと疑うと、そんなバグを見つける機会はほとんどありません。
>
> 組込み OS の場合と Linux の場合は、誰が信頼できるのかという問題が逆になるんです。組込み OS では OS の方が信頼性が低いものでした。しかし Linux では、OS カーネルは新規開発アプリケーションより信頼性が高いです。アプリケーションがエラーを無視すればバグになり、マニュアル外の動作を疑えば徒労に終わり、どちらも時間を損します。
>
> 組込み開発の事情通が Linux 開発の妨害役になるのは皮肉な現実です。
>
> どうすればいいのかというと、マニュアルを信じるしかないんです。Linux は組込み OS に比べて、はるかにマニュアルに嘘の少ないシステムです。マニュアルの指示に最大限従って、それでもダメだったときに初めてバグだと問い合わせるぐらいがいいのです。
>
> 綺麗事の力を信じよう。それが Linux 開発の基礎です。

shkoga

2016年6月30日 7時09分

こんにちは。サムシングプレシャスの古賀です。

水野さん:
>【プログラム構成】
>・main( QApplocation )にてForm,QThreadを4つ,TimerThreadを起動しています。
>・Formは画面描画用に使用しています。
>・CON14_3ピンとCPN14_4ピンを使用してI2CでICからのデータを受け取っています。
>・受け取ったデータをTimerThreadでUDPにて送信しています。
>
>【不具合現象】
>・ICでの設定書込み待ちで使用しているusleepで時たま戻り値がerror値( -1 )を返します。
> 電源を入れ直すかソフトを再起動し直すと
> 環境が変わるのか全くでないときと頻繁にでるときがあります。
> ・他に使用しているtimer関係は
> main起動時にFormを生成し、描画処理用にQTimerをsetInterval( 20msec割り込み )にて使用、
> main起動時にtimer_create( 10msec割り込み )を生成しています。
>
>【お聞きしたい内容】
>・usleepはSignalでの割り込みないしtimer_create等といったその他のタイマー関数と同時使用した場合
> 動作は未定義とのことを踏まえて、
>1.usleepではなくnanosleepを使用した方が良い
>2.nanosleepに切替えた後、出来ればnanosleepだけで他timerは使用しない方が良い
>3.armadillo-440のkernel側でtimer関係を使用しているアプリケーションがあり、それの影響を受ける可能性はないか
>4.フォーラムの過去の投稿で似た内容などがある等ありましたら教えてください。
>
>【環境】
>・armadillo-440 LCD拡張付
>・atmark-dist-20160126
>・linux-2.6.26-at25
>・atde4-amd64-qt

ご質問のうち4については、以前、フォーラムではなくメーリングリストだった時に、usleep と nanosleep の違いについての話題が出たことがありました:
 http://lists.atmark-techno.com/pipermail/armadillo/2012-December/008492…

usleep も nanosleep も、glibc の中で同じシステムコール(__nanosleep())を呼び出しますので、違いはないです。で、usleep() や nanosleep() が -1 を返すことがあるのは、不具合ではなく仕様だと思います。-1 が戻った時、errno の値を見てみて、EINTR であれば、割り込み完了通知によるシグナルが、そのスレッドに届いているために指定された時間スリープせず、途中で戻った、という状況かも知れません。
 https://linuxjm.osdn.jp/html/LDP_man-pages/man2/nanosleep.2.html

以下、ご質問の回答を順に記します:

1. はい。上述したように、usleep() と nanosleep() のどちらも、同じシステムコールを呼び出しますので違いはありませんが、推奨されている nanosleep() を使う方が、よいでしょう。

2. 「他 timer」が何を指しているのか明確でないのですが、nanosleep() だけでは実装上の都合が合わない場合は、そのままでも特に問題ないと思われます(Qt の API の Timer も、Linux 用の実装では nanosleep() を使っているでしょうし)。

3. それは、ないと思います。また、上述したように、usleep() や nanosleep() が -1 を返す場合があるのは、不具合ではなく仕様です(スレッドにシグナルが届いている場合は、指定された時間いっぱいスリープさせず、シグナルを処理する前に途中で待ち動作を終了すべき、ということです)。ですから、それを考慮して待ち動作処理を実装する必要があります。

4. 上で紹介したものの他にあったかも知れませんが、まずは、上記をご覧になって頂ければと思います。

以上、ご参考になりましたら幸いです。
--
古賀信哉 (株)サムシングプレシャス

at_hanada

2016年6月30日 13時18分

本筋から外れますが…

> (Qt の API の Timer も、Linux 用の実装では nanosleep() を使っているでしょうし)。

ここ、ちょっと気になったので見てみました。

http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qtimer.cpp
void QTimer::start(int msec)
void QTimer::start()
から
http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qobject.cpp
int QObject::startTimer(int interval, Qt::TimerType timerType)
が呼ばれeventDispatcherにregisterTimerされ。

最終的にwaitしているのは、
http://code.qt.io/cgit/qt/qt.git/plain/src/corelib/kernel/qeventdispatc…

bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
から呼ばれる
int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags, timeval *timeout)
みたいですね。
名前の通り、selectで実装されていました。

marumo_mizuno

2016年6月30日 13時55分

at_hanada様

コメントありがとうございます。

丸茂電機㈱ 水野です。

QTimerについて調べて頂きありがとうございます。
勉強になります。

> 本筋から外れますが…
>
> > (Qt の API の Timer も、Linux 用の実装では nanosleep() を使っているでしょうし)。
>
> ここ、ちょっと気になったので見てみました。
>
> http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qtimer.cpp
> void QTimer::start(int msec)
> void QTimer::start()
> から
> http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qobject.cpp
> int QObject::startTimer(int interval, Qt::TimerType timerType)
> が呼ばれeventDispatcherにregisterTimerされ。
>
> 最終的にwaitしているのは、
> http://code.qt.io/cgit/qt/qt.git/plain/src/corelib/kernel/qeventdispatc…
> の
> bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
> から呼ばれる
> int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags, timeval *timeout)
> みたいですね。
> 名前の通り、selectで実装されていました。
>

marumo_mizuno

2017年4月25日 13時18分

ご連絡遅くなり申し訳ありません。
タイマーについて無事解決しました。
割り込みタイマーとして/* 1 */の様に記載していましたが、
SIGALRMが何かのタイミングで生成されタイマー割り込みが
想定した通りではなくなっておりました。
何も起きないときもありますが、電源の入れ直しでタイマー割込みが想定外の動きをするときがありました。

現在は、/* 2 */のように割り込みタイマーを生成しています。
また、nanosleepにてerrorが生じた際は残値分にてnanosleepを再セットするようにしています。

ご助力頂きました皆様に感謝致します。
以上、遅くなりましたが解決した結果を報告致します。

/* 1 */
void TimerHandler( int signum )
{
if( signum == SIGALRM )
{
CalcTimerInt( );
}
}

void *CalcTimer( void *arg )
{
struct itimerspec itval;
struct sigaction sigact;

itval.it_interval.tv_sec = INT_TIME_SEC;
itval.it_interval.tv_nsec = INT_TIME_MIN_SEC;
itval.it_value.tv_sec = INT_TIME_SEC;
itval.it_value.tv_nsec = INT_TIME_MIN_SEC;

sigact.sa_handler = TimerHandler;
sigact.sa_flags = 0;
sigemptyset( &sigact.sa_mask );

/* ハンドラ設定 */
if( sigaction( SIGALRM, &sigact, NULL ) == -1 )
{
printf( "CALC Timer sigaction Error\n" );
}
else
{
/* タイマー作成 */
if( timer_create( CLOCK_REALTIME, NULL, &sTimerId ) == -1 )
{
sTimerId = (timer_t)-1;
}
else
{
/* タイマー設定 */
if( timer_settime( sTimerId, 0, &itval, NULL ) == -1 )
{
printf( "CALC Timer timer_settime Error\n" );
return 0;
}
else
{
printf( "CALC Timer Start id = %x ( %d usec )\n", (int)sTimerId, (int)INT_TIME_MIN_SEC );
}
}
}
}

/* 2 */
void SetTimer( void )
{
struct itimerspec ts;
int ret;

ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = INT_TIME_MIN_SEC;
ts.it_value = ts.it_interval;

timer_fd = timerfd_create( CLOCK_MONOTONIC, 0 );
if ( timer_fd == -1 )
{
perror( "timerfd_create" );
exit( 1 );
}

system( "touch /root/timer" );
fd = open( "/root/timer", O_RDONLY, O_NONBLOCK );

ret = timerfd_settime( timer_fd, TFD_TIMER_ABSTIME, &ts, 0 );
if( ret != 0 )
{
perror( "timerfd_settime" );
exit( 1 );
}
}

void *CalcTimer( void *arg )
{
(void)arg;
fd_set rfds, rfds_org;
int nfds;
int ret;

SetTimer( ); // タイマーを生成

FD_ZERO( &rfds_org );
FD_SET( timer_fd, &rfds_org );
nfds = timer_fd + 1;

while( 1 )
{
// システムテスト中はカウントしない用に
if( SystemTest > 1 )
{
nanosleep_set( 200 );
continue;
}

rfds = rfds_org;
ret = select(nfds, &rfds, NULL, NULL, NULL);
if( ret < 0 )
{
if (errno == EINTR) continue;
qDebug( "select" );
exit( 1 );
}

if( FD_ISSET( timer_fd, &rfds ) )
{
uint64_t expired;
ret = read( timer_fd, &expired, sizeof(expired) );
if( ret != sizeof(expired) )
{
qDebug("read");
exit( 1 );
}
// インターバルタイマーが既定値なら割り込み処理を実行する
if( ( intervaltimer = ( intervaltimer + 1 ) % 5 ) == 0 )
{
CalcTimerInt( );
}
}
}
}

int nanosleep_set( int wait_time )
{
struct timespec *setval = &itval;
struct timespec waitval;
int reval;
int i;

setval->tv_sec = INT_TIME_SEC;
setval->tv_nsec = SET_TIME_MIN_SEC * wait_time;

waitval.tv_sec = 0;
waitval.tv_nsec = 0;

// nanosleepでシグナルエラーが起きた際の対策
for( i = 0; i < TIME_SET_MAX; i++ )
{
// スリープ処理でエラーが無ければbreak
if( ( reval = nanosleep( setval, &waitval ) ) == 0 ) break;

setval->tv_sec = waitval.tv_sec;
setval->tv_nsec = waitval.tv_nsec;

waitval.tv_sec = 0;
waitval.tv_nsec = 0;
}

return reval;
}

marumo_mizuno

2016年6月30日 13時50分

サムシングプレシャス 古賀様

返信遅くなり申し訳ありません。
コメントありがとうございます。
今回のソフト修正に対し参考にさせて頂きます。

丸茂電機㈱ 水野です。

1.と2.についてはusleepを使わずnanosleepにて実装するように変更して検証をする予定です。
 その際、nanosleepで-1が返ってきた時の残時間で再度timerをセットするように変更予定です。

3.についてはerrorについては見ていたのですがerrornoについては漏れていました。
 そのため、現在errorが出た際のerrornoを監視し原因を調査しています。
 何かわかり次第、こちらにコメント致します。

4.については確認致しました。ありがとうございます。
 sleepとusleepはライブラリ関数でnanosleepを呼び出している。
 nanosleepはシステムコールであることを再度確認しました。
 振舞いについてはどれも一緒ということも確認しました。

> 以下、ご質問の回答を順に記します:
>
> 1. はい。上述したように、usleep() と nanosleep() のどちらも、同じシステムコールを呼び出しますので違いはありませんが、推奨されている nanosleep() を使う方が、よいでしょう。
>
> 2. 「他 timer」が何を指しているのか明確でないのですが、nanosleep() だけでは実装上の都合が合わない場合は、そのままでも特に問題ないと思われます(Qt の API の Timer も、Linux 用の実装では nanosleep() を使っているでしょうし)。
>
> 3. それは、ないと思います。また、上述したように、usleep() や nanosleep() が -1 を返す場合があるのは、不具合ではなく仕様です(スレッドにシグナルが届いている場合は、指定された時間いっぱいスリープさせず、シグナルを処理する前に途中で待ち動作を終了すべき、ということです)。ですから、それを考慮して待ち動作処理を実装する必要があります。
>
> 4. 上で紹介したものの他にあったかも知れませんが、まずは、上記をご覧になって頂ければと思います。
>
>
> 以上、ご参考になりましたら幸いです。
> --
> 古賀信哉 (株)サムシングプレシャス