7

初期化中にfork()する必要があるCライブラリを書いています。したがって、アプリケーションコード(私の制御の外にある)がシングルスレッドコンテキストからライブラリ初期化コードを呼び出すことをassert()したいと思います(よく知られている「スレッドとフォークが混ざらない」問題を回避するため)。ライブラリが初期化されると、スレッドセーフになります(アプリケーションレベルのコードでスレッドが作成される可能性があります)。私はpthreadのサポートにのみ関心があります。

pthreadを使用して、現在のプロセススペースのスレッド数をカウントすることは不可能のようです。実際、googletestでさえMac OSとQNXでのみGetThreadCount()を実装しています。

スレッドを数えることができないとすると、代わりにシングルスレッドコンテキストをアサートできる可能性はありますか?

明確化:可能であれば、「/ proc」(移植性がない)、追加のライブラリ依存関係(libprocなど)およびLD_PRELOADスタイルのpthread_createラッパーの使用を避けたいと思います。

明確化#2:私の場合、ライブラリ内のワーカーは(Webkitを使用して)比較的重いため、クラッシュする可能性があるため、複数のプロセスを使用する必要があります。ただし、元のプロセスがワーカーのクラッシュに耐えられるようにしたいと思います。

4

3 に答える 3

2

ライブラリ初期化関数を、アプリケーションの前に実行するようにマークすることができますmain()。たとえば、GCCを使用すると、

static void my_lib_init(void) __attribute__((constructor));

static void my_lib_init(void)
{
    /* ... */
}

もう1つのオプションはposix_spawn()、ワーカープロセスを個別のスレーブバイナリとしてフォークして実行するために使用することです。

追加するために編集:

プロセスがすでに(実際のカーネルベースの)スレッドを作成しているかどうかを判断したい場合は、OS固有のコードに依存する必要があるように思われます。

Linuxの場合、決定は簡単で、他のOSでも安全に実行できます。現在のプロセスで使用されているスレッドの数を判別できない場合、関数は-1を返します。

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int count_threads_linux(void)
{
    DIR           *dir;
    struct dirent *ent;
    int            count = 0;

    dir = opendir("/proc/self/task/");
    if (!dir)
        return -1;

    while (1) {

        errno = 0;
        ent = readdir(dir);
        if (!ent)
            break;

        if (ent->d_name[0] != '.')
            count++;
    }

    if (errno) {
        const int saved_errno = errno;
        closedir(dir);
        errno = saved_errno;
        return -1;
    }

    if (closedir(dir))
        return -1;

    return count;
}

/proc/Linuxでもそのチェックが失敗する場合があります(chroot withoutなど)。そのため、-1戻り値はエラーではなく常に不明として扱われる必要があります(ただし、失敗の実際の理由が示されます)。errno

FreeBSDのマニュアルページを見ると、対応する情報がまったく入手できないのではないかと思います。

ついに:

問題のあるケースを検出しようとするのではなく、子プロセス(の前)で非同期シグナルセーフ関数(を参照)のみを使用して、フォーク()スレッドの複雑さを回避することfork()を真剣に推奨します。forking()の前に、共有メモリセグメント、ソケットペアなどを作成できます。私が見ることができる唯一の欠点は、スレーブワーカーに別々のバイナリを使用する必要があることです。それらの説明を考えると、これは私にとって欠点のようには聞こえません。exec()posix_spawn()man 7 signalexec()

于 2012-12-21T16:34:21.610 に答える
1

プロセスの制御ttyにSIGINFOシグナルを送信する場合、プロセスはスレッドのステータスを記述する必要があります。説明から、スレッドが作成されたかどうかを推測できるはずです。

出力をライブラリに読み戻すために、popenを介して呼び出される小さなユーティリティを開発する必要がある場合があります。

サンプルコードを追加金12月2114:45

5つのスレッドを作成する簡単なプログラムを実行します。スレッドは基本的にスリープします。プログラムが終了する前に、SIGINFOシグナルを送信して、スレッドのステータスを取得します。

openbsd> cat a.c
#include <unistd.h>
#include <pthread.h> 

#define THREADS 5

void foo(void);

int
main()
{    
    pthread_t thr[THREADS];
    int j;

    for (j = 0; j < THREADS; j++) {
        pthread_create(&thr[j], NULL, (void *)foo, NULL);
    }   

    sleep(200);                             

    return(0);
}             

void
foo()
{   
    sleep(100);
}             
openbsd> gcc a.c -pthread
openbsd> a.out &
[1] 1234
openbsd> kill -SIGINFO 1234
 0x8bb0e000 sleep_wait  15 -c---W---f 0000          
 0x8bb0e800 sleep_wait  15 -c---W---f 0000          
 0x8bb0e400 sleep_wait  15 -c---W---f 0000          
 0x7cd3d800 sleep_wait  15 -c---W---f 0000          
 0x7cd3d400 sleep_wait  15 -c---W---f 0000          
 0x7cd3d000 sleep_wait  15 -c---W---f 0000 main  
于 2012-12-21T02:13:30.197 に答える
-1

useを使用pthread_once()して、他のスレッドが同じことを行っていないことを確認できます。この方法では、複数のスレッドが初期化関数を呼び出していることを気にする必要はなく、実際に実行されるのは1つだけです。

パブリック初期化関数に、を介してプライベート初期化を実行させますpthread_once()

static pthread_once_t my_initialisation_once = PTHREAD_ONCE_INIT;

static void my_initialisation(void)
{
    /* do something */
}

int lib_initialisation(void)
{
    pthread_once(&my_initialisation_conce, my_initialisation);

    return 0;
}

別の例はここにあります。

リンク

于 2012-12-21T17:10:26.597 に答える