3

しばらく前に pthread を使用してダイニング哲学者問題の C プログラムを作成しましたが、現在、代わりに fork() を使用するように変更しようとしています。これは、私がすでに合格した講義の練習問題です。しかし、友人に助けを求められたのですが、自分では理解できないようで、気が狂いそうです!

「ps」を実行すると、プロセスがそこにあります。しかし、stdout への出力がないので、パイプに何か問題があると思います。

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

#define N 5     
#define LEFT (i+4)%N
#define RIGHT (i+1)%N
#define THINKING 0
#define HUNGRY 1
#define EATING 2

sem_t spoon;
sem_t phil[N];
int state[N];
int phil_num[N]={0,1,2,3,4};
int fd[N][2]; // file descriptors for pipes
pid_t pid, pids[N]; // process ids
int i; 
int num;

void philosopher(int i);
void test(int i);
void take_spoon(int i);
void put_spoon(int i);

char buffer[100];

int main(void) 
{
  for(i=0;i<N;++i)
  {
    pipe(fd[i]);        
    pids[i] = fork();

    printf("i=%d\n",i);
    printf("pids[i]=%d\n",pids[i]);

    if(pids[i]==0)
    {
      // child
      dup2(fd[i][1],1);
      close(fd[i][0]);      
      close(fd[i][1]);
      philosopher(i);
      _exit(0);
    }

    else if(pids[i]>0)
    {
      // parent
      dup2(fd[i][0],0);
      close(fd[i][0]);      
      close(fd[i][1]);
    }
  }

  // wait for child processes to end
  for(i=0;i<N;++i) waitpid(pids[i],NULL,0);

  return 0;
}



void philosopher(int i)
{
  while(1)
  {
    sleep(1);
    take_spoon(i);
    sleep(2);
    put_spoon(i);
    sleep(1);
  }
}

void take_spoon(int i)
{
  sem_wait(&spoon);
  state[i] = HUNGRY;
  printf("philosopher %d is hungry\n",i+1);
  test(i);
  sem_post(&spoon);
  sem_wait(&phil[i]);
}

void put_spoon(int i)
{
  sem_wait(&spoon);
  state[i] = THINKING;
  printf("philosopher %d puts down spoon %d and %d hin\n",i+1,LEFT+1,i+1);
  printf("philosopher %d thinks\n",i+1);
  test(LEFT);
  test(RIGHT);
  sem_post(&spoon);
}

void test(int i)
{
  if( state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING)
  {
    state[i] = EATING;
    printf("philosopher %d takes spoon %d and %d\n",i+1,LEFT+1,i+1);
    printf("philosopher  %d eats\n",i+1);
    sem_post(&phil[i]);
  }
}

よろしくお願いします。

4

1 に答える 1

5

いくつかの問題。まず、 の後fork()、子プロセスと親プロセスはメモリを共有しません。これは、スレッドとプロセスの主な違いの 1 つです。各プロセスには、独自の仮想アドレス空間があります。哲学者に共有してもらいたいものは何でも、共有メモリを作成して明示的に行う必要があります。グローバル変数をすべてのプロセスで共有するつもりだったようです。(開いているファイル記述子など、共有されるものがいくつかあることに注意してください。子は親から変数のコピーを取得し、呼び出し時に割り当てられた値に初期化されfork()ます。)

第 2 に、紛らわしい不必要な変数がいくつかあります。特に、パイプは実際の目的を果たしません。for each プロセスはstdout、親プロセスにパイプで戻そうとする必要なく、すでにコンソール画面に移動します。これは、子プロセスがすでに親の開いているファイル記述子を継承しているためです。そのため、子はすでにstdout親と同じものを使用しています。さらにphil_num、 およびnum変数は使用されず、i. 変数は不必要にグローバル化されているようですpidpids

3 番目に、セマフォの初期化に失敗しました。グローバル変数としてのデフォルトの初期化は、おそらくセマフォを「使用可能」のままにしますが、初期値が 0 であるため、sem_wait()ブロックされるだけです。あなたの場合、共有メモリにこれらのセマフォが必要なので、sem_init()とにかくへの呼び出しが必須であり(複数のプロセス間で共有されることを示すため)、呼び出しにより、セマフォを値1soで適切に初期化する機会が得られます最初のsem_wait()呼び出しが返される可能性があること。

グローバルを本当に共有する必要があるものに調整した後、それらをまとめて構造にまとめることができます。次に、共有データのグローバル ポインターを作成できます。

struct shared_data {
  sem_t spoon;
  sem_t phil[N];
  int state[N];
};

struct shared_data *shared;

void initialize_shared(); /* at program start */
void finalize_shared();   /* at program end */

共有メモリを作成する 1 つの方法は、 を使用することmmap()です。メモリを作成したら、データを適切に初期化する必要があります。sem_init()これには、セマフォに対する呼び出しが含まれます。sem_destroy()はセマフォをクリーンアップするために使用され、マップされたメモリは で解放できますmunmap()。これらはプロセスの終了時に自動的に行われますが、完全を期すために提供されています。(実行するすべてのオペレーティング システム呼び出しの戻り値を常に確認する必要がありますが、簡潔にするために省略しています。)

void initialize_shared()
{
  int i;
  int prot=(PROT_READ|PROT_WRITE);
  int flags=(MAP_SHARED|MAP_ANONYMOUS);
  shared=mmap(0,sizeof(*shared),prot,flags,-1,0);
  memset(shared,'\0',sizeof(*shared));
  sem_init(&shared->spoon,1,1);
  for(i=0;i<N;++i) sem_init(&shared->phil[i],1,1);
}

void finalize_shared()
{
  int i;
  for(i=0;i<N;++i) sem_destroy(&shared->phil[i]);
  munmap(shared, sizeof(*shared));
}

不必要にグローバルだったものにローカル変数を追加し、必要に応じて andを呼び出す必要があることを除いて、実装は実際には変わりませmain()ん。また、関連するすべてのコードを削除します。initialize_shared()finalize_shared()pipe()

int main(void)
{
  int i;
  pid_t pid, pids[N]; // process ids
  initialize_shared();
  for(i=0;i<N;++i)
  {
    pid = fork();
    if(pid==0)
    {
      // child
      philosopher(i);
      _exit(0);
    }
    else if(pid>0)
    {
      // parent
      pids[i] = pid;
      printf("pids[%d]=%d\n",i,pids[i]);
    }
    else
    {
      perror("fork");
      _exit(0);
    }
  }
  // wait for child processes to end
  for(i=0;i<N;++i) waitpid(pids[i],NULL,0);

  finalize_shared();
  return 0;
}

philosopher()は無限ループとして実装されているため、プログラムが単独で終了することは決してないことに注意してください。

于 2013-06-13T19:25:29.083 に答える