2

プログラムを正常に実行すると、セグメンテーション違反が発生します。ただし、gdb run を使用すると問題なく動作します。また、フィロ関数でスリープ時間を増やすと、セグメンテーション違反の割合が増加します。ubuntu12.04を使用しています。助けや指摘をいただければ幸いです。これが私のコードです

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>

#include <signal.h>
#include <sys/wait.h>
#include <time.h>
#include <semaphore.h>
#include <errno.h>

#define STACKSIZE 10000
#define NUMPROCS 5
#define ROUNDS 10

int ph[NUMPROCS];
//cs[i] is the chopstick between philosopher i and i+1
sem_t cs[NUMPROCS], dead;

int philo() {
    int i = 0;
    int cpid = getpid();
    int phno;

    for (i=0; i<NUMPROCS; i++)
        if(ph[i] == cpid) phno = i;

    for (i=0; i < ROUNDS ; i++){
        // Add your entry protocol here
        if (sem_wait(&dead) != 0) {
            perror(NULL);
            return 1;
        }
        if (sem_wait(&cs[phno]) != 0) {
            perror(NULL);
            return 1;
        }
        if (sem_wait(&cs[(phno-1+NUMPROCS) % NUMPROCS]) != 0){
            perror(NULL);
            return 1;
        }

        // Start of critical section -- simulation of slow n++
        int sleeptime = 20000 + rand()%50000;
        printf("philosopher %d is eating by chopsticks %d and %d\n",phno,phno,(phno-1+NUMPROCS)%NUMPROCS);
        usleep(sleeptime) ;
        // End of critical section

        // Add your exit protocol here
        if (sem_post(&dead) != 0) {
            perror(NULL);
            return 1;
        }
        if (sem_post(&cs[phno]) != 0) {
            perror(NULL);
            return 1;
        }
        if (sem_post(&cs[(phno-1+NUMPROCS) % NUMPROCS]) != 0){
            perror(NULL);
            return 1;
        }
    }
    return 0;
}

int main( int argc, char ** argv){
    int i;
    void* stack[NUMPROCS];
    srand(time(NULL));

    //initialize semaphores
    for (i=0; i<NUMPROCS; i++) {
        if (sem_init(&cs[i],1,1) != 0){
            perror(NULL);
            return 1;
        }
    }
    if (sem_init(&dead,1,4) != 0){
        perror(NULL);
        return 1;
    }

    for (i = 0; i < NUMPROCS; i++){
        stack[i] = malloc(STACKSIZE) ;
        if ( stack[i] == NULL ) {
            printf("Error allocating memory\n") ;
            exit(1) ;
        }

        // create a child that shares the data segment
        ph[i] = clone(philo, stack[i]+STACKSIZE-1, CLONE_VM|SIGCHLD, NULL) ;
        if (ph[i] < 0) {
            perror(NULL) ;
            return 1;
        }
    }

    for (i=0; i < NUMPROCS; i++) wait(NULL);
    for (i=0; i < NUMPROCS; i++) free(stack[i]);

    return 0 ;
}
4

2 に答える 2

2

典型的なハイゼンバグ: 見れば消える。私の経験では、gdb の外部でのみ segv を取得すること、またはその逆は、初期化されていないメモリを使用しているか、実際のポインター アドレスに依存していることを示しています。通常の実行valgrindは、それらを検出する際に容赦なく正確です。残念ながら(私は)文脈の外でvalgrindあなたを扱うことができません。clonepthread

目視検査では、メモリの問題ではないことが示唆されています。スタックのみがヒープに割り当てられ、それらの使用は問題ないようです。それらをポインターで処理してからそれに何かを追加することを除いvoid *て、これは標準 C (GNU 拡張) では許可されていません。を使用するのが適切char *ですが、GNU拡張機能はあなたが望むことを行います。

スタックのトップ アドレスから 1 を引くことはおそらく必要なく、単純な実装ではアラインメント エラーが発生する可能性がありますが、スタック トップcloneが再度アラインされる可能性が高いため、これは問題ではないと思いますclone。そして確かに、のマニュアルページはclone、アドレスの正確な場所についてあまり明確ではありません:「メモリ空間の最上位アドレス」。

子の状態の変化を待って、それが死亡したと仮定するのは少しずさんで、そのスタックを取り除くとセグメンテーション違反が発生する可能性がありますが、これも問題ではないと思います。あなたの哲学者。

あなたのアプリケーションを実行すると、哲学者は gdb の内外で邪魔されずにダイナーを終えることができるので、以下は推測です。哲学者のクローンを作成する親プロセスを「テーブル」と呼びましょう。哲学者が複製されると、テーブルは返された pid を に格納しますph。たとえば、その番号を椅子に割り当てます。哲学者が最初にすることは、自分の椅子を探すことです。椅子が見つからない場合はphno、セマフォにアクセスするために使用される初期化されていないものがあります。現在、これはセグメンテーション違反につながる可能性があります。

実装は、哲学者が開始する前に制御がテーブルに返されることを前提としています。マニュアルページでそのような保証を見つけることができず、実際にはこれが真実ではないことを期待しています. また、クローン インターフェイスには、プロセス ID を子と親の間で共有されるメモリに配置する可能性があり、これは認識されている問題であることを示唆しています (パラメータpidと を参照ctid)。それらが使用される場合、テーブルまたはクローンされたばかりの哲学者が制御を取得する前に、pid が書き込まれます。

は監視下で生成されたプロセスを十分に認識しており、オペレーティング システムとは異なる方法で処理する可能性があるgdbため、このエラーが inside と outside の違いを説明している可能性が高いです。gdb

または、テーブルにセマフォを割り当てることもできます。したがって、テーブルがそう言うまで、明らかにすべての椅子を割り当てた後、誰もテーブルに座らない. これにより、セマフォをより有効に使用できますdead

ところで。もちろん、ソリューションの設定により、すべての哲学者がそれぞれ 1 つのフォーク (お箸) を持ち、もう一方のフォークを待って餓死するという状況が可能になることを十分に認識しています。幸いなことに、それが起こる可能性は非常に低いです。

于 2013-03-12T18:47:05.797 に答える
1
ph[i] = clone(philo, stack[i]+STACKSIZE-1, CLONE_VM|SIGCHLD, NULL) ;

これにより、glibc が何も知らない実行スレッドが作成されます。そのため、glibc は動的シンボル解決などに必要なスレッド固有の内部構造を作成しません。

このようなセットアップでは、関数から任意の glibc 関数をphilo呼び出すと、未定義の動作が呼び出され、クラッシュすることがあります (ダイナミック ローダーがメイン スレッドのプライベート データを使用してシンボル解決を実行し、ローダーが各スレッドに独自のプライベート エリアがあると想定するため)。しかし、 clone「glibc の背後にある」単一のプライベート領域を共有する s を作成することで、この仮定に違反しています)。

コア ダンプを見ると、実際のクラッシュが で発生する可能性が高くld.so、これは私の推測を裏付けるものです。

直接使用しないでくださいclone(自分が何をしているのかわかっていない限り)。pthread_create代わりに使用してください。

これは、私が得たばかりのコアに表示されるものです (これはまさに私が説明した問題です):

Program terminated with signal 4, Illegal instruction.
#0  _dl_x86_64_restore_sse () at ../sysdeps/x86_64/dl-trampoline.S:239
239             vmovdqa %fs:RTLD_SAVESPACE_SSE+0*YMM_SIZE, %ymm0
(gdb) bt
#0  _dl_x86_64_restore_sse () at ../sysdeps/x86_64/dl-trampoline.S:239
#1  0x00007fb694e1dc45 in _dl_fixup (l=<optimized out>, reloc_arg=<optimized out>) at ../elf/dl-runtime.c:127
#2  0x00007fb694e0dee5 in _dl_runtime_resolve () at ../sysdeps/x86_64/dl-trampoline.S:42
#3  0x00000000004009ec in philo ()
#4  0x00007fb69486669d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
于 2013-03-12T20:52:58.803 に答える