用C语言用C语言哲学家()

时间:2013-06-13 16:20:00

标签: c fork semaphore dup2 dining-philosopher

我前段时间使用pthread编写了一个针对Dining Philosophers问题的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]);
  }
}

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:4)

几个问题。首先是在fork()之后,子进程和父进程不共享内存。这是线程和进程之间的主要区别之一。每个进程都有自己的虚拟地址空间。无论你想让哲学家分享什么,你都必须通过创造共享记忆来明确地做到这一点。您似乎希望在所有进程之间共享全局变量。 (请注意,有些内容是共享的,例如打开文件描述符,并且子项确实从父项获取变量的副本,初始化为fork()调用时分配给它们的值。 )

其次,你有一些令人困惑的不必要的变量。特别是,管道不能用于任何真正的目的。每个进程的stdout已经进入控制台屏幕,无需尝试将它们传回父级。这是因为子进程已经继承了父进程的打开文件描述符,因此子进程已经使用了与父进程相同的stdout。此外,phil_numnum变量未使用,ipidpids变量似乎是不必要的全球化。

第三,您无法初始化信号量。作为全局变量的默认初始化可能使信号量“可用”但具有0初始值,意味着sem_wait()将仅阻塞。在您的情况下,您需要共享内存中的那些信号量,因此无论如何都必须调用sem_init()(以表明它将在多个进程之间共享),并且该调用使您有机会正确初始化信号量值为1,以便初始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 */

创建共享内存的一种方法是使用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));
}

您的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()实现为无限循环。