6

コンテキスト:Ubuntu11.10およびlibfuse2.8.4-1.4ubuntu1 Linux 3.0.0-14-generic#23-Ubuntu SMP Mon Nov 21 20:28:43 UTC 2011 x86_64 x86_64 x86_64 GNU / Linux

libfuseを使おうとしています。(シグナルハンドラーまたは別のスレッドから)fuse_session_loopを終了させたいのですが、fuse_session_exitを呼び出すと、セッションが新しい要求を受信するまで何も起こりません。

Fuse_session_exitは、fuse_session_exitedによって読み取られるフラグを設定します。Fuse_session_loopにデバッグすると、fuse_chan_recvでブロックされているように見えるため、ループの先頭まで、fuse_session_exitedを再度チェックしません...

int fuse_session_loop(struct fuse_session *se)
{
    int res = 0;
    struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
    size_t bufsize = fuse_chan_bufsize(ch);
    char *buf = (char *) malloc(bufsize);
    if (!buf) {
        fprintf(stderr, "fuse: failed to allocate read buffer\n");
        return -1;
    }

    while (!fuse_session_exited(se)) {
        struct fuse_chan *tmpch = ch;
        res = fuse_chan_recv(&tmpch, buf, bufsize); <--- BLOCKING
        if (res == -EINTR)
            continue;
        if (res <= 0)
            break;
        fuse_session_process(se, buf, res, tmpch);
    }

    free(buf);
    fuse_session_reset(se);
    return res < 0 ? -1 : 0;
}

Fuse_chan_recvは、「/ dev /fuse」デバイスの「読み取り」システムコールをブロックするfuse_kern_chan_receiveを呼び出すため、fuse_session_exitedフラグが設定されていても、まだ何も起こりません。

static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
                  size_t size)
{
    struct fuse_chan *ch = *chp;
    int err;
    ssize_t res;
    struct fuse_session *se = fuse_chan_session(ch);
    assert(se != NULL);

restart:
    res = read(fuse_chan_fd(ch), buf, size); <--- BLOCKING
    err = errno;

    if (fuse_session_exited(se))
        return 0;
    if (res == -1) {
        /* ENOENT means the operation was interrupted, it's safe
           to restart */
        if (err == ENOENT)
            goto restart;

        if (err == ENODEV) {
            fuse_session_exit(se);
            return 0;
        }
        /* Errors occuring during normal operation: EINTR (read
           interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
           umounted) */
        if (err != EINTR && err != EAGAIN)
            perror("fuse: reading device");
        return -err;
    }
    if ((size_t) res < sizeof(struct fuse_in_header)) {
        fprintf(stderr, "short read on fuse device\n");
        return -EIO;
    }
    return res;
}

この問題は、libfuseで提供されているhello_ll.cの例と私のプログラムに影響を与えているようです。おそらく、機能していないはずのメカニズムがあるのではないかと思います。おそらく、fuse_session_exitは、読み取り呼び出しを中断する何かを実行しているはずです。これは、何らかの理由で私のシステムで機能していません。

何か案は?

4

4 に答える 4

2

これはバグレポートの価値があるかもしれません。また、「期待どおりに機能している」として閉鎖される場合もあります。

とは言うものの、関数で実行中の呼び出しを中断するシグナル送信すると、スタック全体にエラーを伝播する準備ができているように見えます。これにより、フラグに気付く上位レベルの呼び出しがトリガーされ、うまくいけば終了します。ループを可能な限りきれいにします。read()fuse_kern_chan_receive()continueexited

pthread_kill(3)問題の特定のスレッドを強制終了するには、を追加してみてください。fuse_signals.cのハンドラーをインストールしSIGHUPSIGINTそれSIGTERMを呼び出しますfuse_session_exit()

于 2012-01-18T00:30:00.943 に答える
1

通常、システムコール(read(2)など)がブロックされているときにシグナルハンドラーが実行されると、システムコールはEINTRを使用して(シグナルハンドラーの実行が完了した後)すぐに戻ります。これは明らかに、fuse_session_loopfuse_session_exitが設計された動作です。

ただし、 SA_RESTARTフラグが設定された状態でシグナルハンドラーがインストールされている場合( sigaction(2)を参照) 、シグナルハンドラーの実行後、システムコールはEINTRで返されません。代わりに、システムコールはブロックを再開します。

私のシステム(Ubuntu 11.10 x86_64)での何らかの理由により、signal(2)のデフォルトの動作は、 SA_RESTARTフラグを使用してシグナルハンドラーをインストールすることです。

つまり、次のプログラムのstrace..。

#include <stdlib.h>
#include <signal.h>

void f(int signum) {}

int main()
{
    signal(SIGINT,f);
    return EXIT_SUCCESS;
}

...以下のとおりであります...

rt_sigaction(SIGINT, {0x400524, [INT], SA_RESTORER|SA_RESTART, 0x7f4997e1f420}, {SIG_DFL, [], 0}, 8) = 0

このため、信号(fuseと私自身のプログラムで提供されている例)は、作成者も予想していたように、 fuse_kern_chan_receiveで読み取られたブロッキングを中断しませんでした。

修正は、sigaction(2)を使用して(そしてSA_RESTARTビットをゼロのままにして)ハンドラーをインストールすることでした(signal(2)の代わりに

まだ残っている未解決の質問は、signal(2)の呼び出しでSA_RESTARTフラグがデフォルトでオンになっているのはなぜですか?中断(再起動ではない)が予想されるデフォルトの動作であると思います。

于 2012-01-18T22:41:06.310 に答える
1

ヒューズ2.9.2のSA_RESTARTフラグをゼロにすることで問題を解決できませんでした。

代わりに、終了するときに偽の読み取りを使用しました。

  1. 呼び出す前にファイルを開くfuse_session_exit
  2. 電話fuse_session_exit
  3. ファイルから1バイトを読み取る
于 2018-04-08T14:50:47.457 に答える
0

シグナルのマンページを読む:http://man7.org/linux/man-pages/man2/signal.2.html

signal()の動作は、UNIXのバージョンによって異なり、Linuxのバージョンによっても歴史的に異なります。その使用は避けてください。代わりにsigaction(2)を使用してください。以下の移植性を参照してください。

signal()の唯一の移植可能な使用法は、シグナルの処理をSIG_DFLまたはSIG_IGNに設定することです。signal()を使用してシグナルハンドラーを確立するときのセマンティクスは、システムによって異なります(POSIX.1ではこのバリエーションが明示的に許可されています)。この目的には使用しないでください。

Linuxの状況は次のとおりです。

  • カーネルのsignal()システムコールは、SystemVセマンティクスを提供します。

  • デフォルトでは、glibc 2以降では、signal()ラッパー関数はカーネルシステムコールを呼び出しません。代わりに、BSDセマンティクスを提供するフラグを使用してsigaction(2)を呼び出します。このデフォルトの動作は、_BSD_SOURCE機能テストマクロが定義されている限り提供されます。デフォルトでは、_BSD_SOURCEが定義されています。_GNU_SOURCEを定義する場合も暗黙的に定義され、もちろん明示的に定義することもできます。

  • glibc 2以降では、_BSD_SOURCE機能テストマクロが定義されていない場合、signal()はSystemVセマンティクスを提供します。(標準モード(-std = xxxまたは-ansi)のいずれかでgcc(1)を呼び出す場合、または_POSIX_SOURCE、_XOPEN_SOURCE、_SVID_SOURCEなどの他のさまざまな機能テストマクロを定義する場合、_BSD_SOURCEのデフォルトの暗黙的な定義は提供されません。feature_test_macrosを参照してください。 (7)。)

そのため、GLibcは信号を後で再起動しないようにしていましたが、BSDとの互換性を高めるために変更しました。

于 2014-11-17T22:34:59.960 に答える